diff --git a/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift b/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift index f050cd6..25073ea 100644 --- a/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift +++ b/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift @@ -233,12 +233,13 @@ extension AcmeSwift { var descs: [ChallengeDescription] = [] for auth in authorizations where auth.status == .pending { for challenge in auth.challenges where (challenge.type == preferring || auth.wildcard == true) && (challenge.status == .pending || challenge.status == .invalid) { - let digest = "\(challenge.token).\(accountThumbprint.base64EncodedString().base64ToBase64Url())" + let digest = "\(challenge.token).\(accountThumbprint.base64URLString)" + if challenge.type == .dns { let challengeDesc = ChallengeDescription( type: challenge.type, endpoint: "_acme-challenge.\(auth.identifier.value)", - value: sha256Digest(data: digest.data(using: .utf8)!).base64EncodedString().base64ToBase64Url(), + value: Crypto.SHA256.hash(data: Array(digest.utf8)).base64URLString, url: challenge.url ) descs.append(challengeDesc) @@ -323,7 +324,7 @@ extension AcmeSwift { /// Return the SHA256 digest of the ACMEv2 account public key's JWK JSON. /// /// This value has to be present in an HTTP challenge value. - private func getAccountThumbprint() throws -> Data { + private func getAccountThumbprint() throws -> SHA256Digest { guard let login = self.client.login else { throw AcmeError.mustBeAuthenticated("\(AcmeSwift.self).init() must be called with an \(AccountCredentials.self)") } @@ -339,18 +340,13 @@ extension AcmeSwift { ) let encoder = JSONEncoder() encoder.outputFormatting = .sortedKeys - let jwkData = try! encoder.encode(jwk) - return sha256Digest(data: jwkData) + return Crypto.SHA256.hash(data: try encoder.encode(jwk)) } - - private func sha256Digest(data: Data) -> Data { - let digest: SHA256Digest = Crypto.SHA256.hash(data: data) - let array = digest.compactMap{UInt8($0)} - let hashData = Data(array) - return hashData - /*return String(data: data, encoding: .utf8)! - return digest.map { String(format: "%02x", $0) }.joined()*/ - } - + } +} + +extension SHA256Digest { + var base64URLString: String { + Data(self).base64EncodedString().base64ToBase64Url() } } diff --git a/Sources/AcmeSwift/APIs/AcmeSwift.swift b/Sources/AcmeSwift/APIs/AcmeSwift.swift index 58ee5de..8944175 100644 --- a/Sources/AcmeSwift/APIs/AcmeSwift.swift +++ b/Sources/AcmeSwift/APIs/AcmeSwift.swift @@ -90,7 +90,7 @@ public class AcmeSwift { let wrappedBody = try AcmeRequestBody(accountURL: accountURL, privateKey: privateKey, nonce: nonce, payload: endpoint) let body = try JSONEncoder().encode(wrappedBody) - let bodyDebug = String(data: body, encoding: .utf8)! + let bodyDebug = String(decoding: body, as: UTF8.self) logger.debug("\(Self.self) Endpoint: \(endpoint.method) \(endpoint.url) request body: \(bodyDebug)") request.body = .bytes(ByteBuffer(data: body)) diff --git a/Sources/AcmeSwift/Helpers/AcmeRequestBody.swift b/Sources/AcmeSwift/Helpers/AcmeRequestBody.swift index 8ed4361..2489231 100644 --- a/Sources/AcmeSwift/Helpers/AcmeRequestBody.swift +++ b/Sources/AcmeSwift/Helpers/AcmeRequestBody.swift @@ -88,33 +88,19 @@ struct AcmeRequestBody: Encodable { var container = encoder.container(keyedBy: CodingKeys.self) let protectedData = try jsonEncoder.encode(self.protected) - guard let protectedJson = String(data: protectedData, encoding: .utf8) else { - throw AcmeError.jwsEncodeError("Unable to encode AcmeRequestBody.protected as JSON string") - } - let protectedBase64 = protectedJson.toBase64Url() + let protectedJSON = String(decoding: protectedData, as: UTF8.self) + let protectedBase64 = protectedJSON.toBase64Url() try container.encode(protectedBase64, forKey: .protected) let payloadData = try jsonEncoder.encode(self.payload) - guard let payloadJson = String(data: payloadData, encoding: .utf8) else { - throw AcmeError.jwsEncodeError("Unable to encode AcmeRequestBody.payload as JSON string") - } + let payloadJSON = String(decoding: payloadData, as: UTF8.self) - let payloadBase64: String! // Empty payload is required most of the time for so-called POST-AS-GET ACMEv2 requests. - if payloadJson == "\"\"" { - payloadBase64 = "" - } - else { - payloadBase64 = payloadJson.toBase64Url() - } + let payloadBase64 = payloadJSON == "\"\"" ? "" : payloadJSON.toBase64Url() try container.encode(payloadBase64, forKey: .payload) - let signedString = "\(protectedBase64).\(payloadBase64!)" - guard let signedData = signedString.data(using: .utf8) else { - throw AcmeError.jwsEncodeError("Unable to encode data to sign String as Data") - } - - let signature = try self.privateKey.signature(for: signedData) + let signedString = "\(protectedBase64).\(payloadBase64)" + let signature = try self.privateKey.signature(for: Data(signedString.utf8)) let signatureData = signature.rawRepresentation let signatureBase64 = signatureData.toBase64UrlString() diff --git a/Sources/AcmeSwift/Helpers/HttpClient+Decode.swift b/Sources/AcmeSwift/Helpers/HttpClient+Decode.swift index bc4c594..406f831 100644 --- a/Sources/AcmeSwift/Helpers/HttpClient+Decode.swift +++ b/Sources/AcmeSwift/Helpers/HttpClient+Decode.swift @@ -28,7 +28,7 @@ extension HTTPClientResponse { throw AcmeError.dataCorrupted("Unable to read Data from response body buffer") } if T.self == String.self { - return String(data: data, encoding: .utf8) as! T + return String(decoding: data, as: UTF8.self) as! T } return try decoder.decode(type, from: Data(data)) @@ -42,7 +42,7 @@ extension HTTPClientResponse { if let error = try? JSONDecoder().decode(AcmeResponseError.self, from: data) { throw error } - throw AcmeError.errorCode(self.status.code, String(data: data, encoding: .utf8)) + throw AcmeError.errorCode(self.status.code, String(decoding: data, as: UTF8.self)) } throw AcmeError.errorCode(self.status.code, self.status.reasonPhrase) } diff --git a/Sources/AcmeSwift/Helpers/String+base64Url.swift b/Sources/AcmeSwift/Helpers/String+base64Url.swift index d35b017..2ae553f 100644 --- a/Sources/AcmeSwift/Helpers/String+base64Url.swift +++ b/Sources/AcmeSwift/Helpers/String+base64Url.swift @@ -10,10 +10,10 @@ extension String { while base64.count % 4 != 0 { base64 = base64.appending("=") } - guard let data = Data(base64Encoded: base64) else { - return nil - } - return String(data: data, encoding: .utf8) + guard let data = Data(base64Encoded: base64) + else { return nil } + + return String(decoding: data, as: UTF8.self) } /// Encodes the string as a Base64 string suitable for use as URL parameters. diff --git a/Sources/AcmeSwift/Models/AcmeError.swift b/Sources/AcmeSwift/Models/AcmeError.swift index 6c8b499..53c6de7 100644 --- a/Sources/AcmeSwift/Models/AcmeError.swift +++ b/Sources/AcmeSwift/Models/AcmeError.swift @@ -20,7 +20,7 @@ public enum AcmeError: Error, Sendable { case jwsEncodeError(String) case dataCorrupted(String) - case errorCode(UInt, String?) + case errorCode(UInt, String) /// A resource should have a URL, returned in a response "Location" header, but couldn't find or parse the header. case noResourceUrl diff --git a/Tests/AcmeSwiftTests/OrderTests.swift b/Tests/AcmeSwiftTests/OrderTests.swift index a4c08ae..803a272 100644 --- a/Tests/AcmeSwiftTests/OrderTests.swift +++ b/Tests/AcmeSwiftTests/OrderTests.swift @@ -119,8 +119,7 @@ final class OrderTests: XCTestCase { let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let data = try! encoder.encode(value) - return String(data: data, encoding: .utf8)! - + return String(decoding: data, as: UTF8.self) } }