diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c8c150 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +## Build directory +.build/ + +## Xcode ser settings +xcuserdata/ +.swiftpm/xcode + +## macOS files +.DS_Store + +## Resolved package dependencies +Package.resolved diff --git a/Package.swift b/Package.swift index 81c7bf8..187b0cd 100644 --- a/Package.swift +++ b/Package.swift @@ -16,8 +16,8 @@ let package = Package( .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.10.0"), .package(url: "https://github.com/apple/swift-crypto.git", from: "2.1.0"), .package(url: "https://github.com/vapor/jwt-kit.git", branch: "main"), - // x509 - .package(url: "https://github.com/outfoxx/PotentCodables.git", from: "2.2.0"), + .package(url: "https://github.com/apple/swift-certificates.git", from: "1.0.0-beta.1"), + .package(url: "https://github.com/apple/swift-asn1.git", from: "1.0.0-beta.1") ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -29,7 +29,8 @@ let package = Package( .product(name: "Crypto", package: "swift-crypto"), .product(name: "_CryptoExtras", package: "swift-crypto"), .product(name: "JWTKit", package: "jwt-kit"), - "PotentCodables" + .product(name: "X509", package: "swift-certificates"), + .product(name: "SwiftASN1", package: "swift-asn1") ]), .testTarget( name: "AcmeSwiftTests", diff --git a/README.md b/README.md index ebf60ac..ad2a93b 100644 --- a/README.md +++ b/README.md @@ -168,13 +168,12 @@ let finalizedOrder = try await acme.orders.finalize(order: order, withPemCsr: ". If you want AcmeSwift to generate one for you: ```swift // ECDSA key and certificate -let csr = try AcmeX509Csr.ecdsa(domains: ["mydomain.com", "www.mydomain.com"]) +let (privateKey, csr, finalizedOrder) = try await acme.orders.finalizeWithEcdsa(order: order, domains: ["mydomain.com", "www.mydomain.com"]) // .. or, good old RSA -let csr = try AcmeX509Csr.rsa(domains: ["mydomain.com", "www.mydomain.com"]) +let (privateKey, csr, finalizedOrder) = try await acme.orders.finalizeWithRsa(order: order, domains: ["mydomain.com", "www.mydomain.com"]) -let finalizedOrder = try await acme.orders.finalize(order: order, withCsr: csr) // You can access the private key used to generate the CSR (and to use once you get the certificate) -print("\n• Private key: \(csr.privateKeyPem)") +print("\n• Private key: \(try privateKey.serializeAsPEM().pemString)") ```
@@ -255,10 +254,8 @@ guard failed.count == 0 else { } // Let's create a private key and CSR using the rudimentary feature provided by AcmeSwift -let csr = try AcmeX509Csr.ecdsa(domains: domains) - // If the validation didn't throw any error, we can now send our Certificate Signing Request... -let finalized = try await acme.orders.finalize(order: order, withCsr: csr) +let (privateKey, csr, finalized) = try await acme.orders.finalizeWithRsa(order: order, domains: domains) // ... and the certificate is ready to download! let certs = try await acme.certificates.download(for: finalized) @@ -268,7 +265,7 @@ try certs.joined(separator: "\n").write(to: URL(fileURLWithPath: "cert.pem"), at // Now we also need to export the private key, encoded as PEM // If your server doesn't accept it, append a line return to it. -try csr.privateKeyPem.write(to: URL(fileURLWithPath: "key.pem"), atomically: true, encoding: .utf8) +try privateKey.serializeAsPEM().pemString.write(to: URL(fileURLWithPath: "key.pem"), atomically: true, encoding: .utf8) ``` diff --git a/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift b/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift index 44fd844..d157795 100644 --- a/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift +++ b/Sources/AcmeSwift/APIs/AcmeSwift+Orders.swift @@ -1,6 +1,9 @@ import Foundation import Crypto +import _CryptoExtras import JWTKit +import SwiftASN1 +import X509 extension AcmeSwift { @@ -33,6 +36,8 @@ extension AcmeSwift { /// - Parameters: /// - url: The URL of the Order. public func get(url: URL) async throws -> AcmeOrderInfo { + try await self.client.ensureLoggedIn() + let ep = GetOrderEndpoint(url: url) var (info, headers) = try await self.client.run(ep, privateKey: self.client.login!.key, accountURL: client.accountURL!) info.url = URL(string: headers["Location"].first ?? "") @@ -96,17 +101,96 @@ extension AcmeSwift { let (info, _) = try await self.client.run(ep, privateKey: self.client.login!.key, accountURL: client.accountURL!) return info } + + /// Finalizes an Order and send the ECDSA CSR. + /// - Parameters: + /// - order: The `AcmeOrderInfo` returned by the call to `.create()` + /// - subject: Subject of certificate + /// - domains: Domains for certificate + /// - Throws: Errors that can occur when executing the request. + /// - Returns: Returns `Certificate.PrivateKey`, `CertificateSigningRequest` and `Account`. + public func finalizeWithEcdsa(order: AcmeOrderInfo, subject: String? = nil, domains: [String]) async throws -> (Certificate.PrivateKey, CertificateSigningRequest, AcmeOrderInfo) { + guard domains.count > 0 else { + throw AcmeError.noDomains("At least 1 DNS name is required") + } + + let p256 = P256.Signing.PrivateKey() + let privateKey = Certificate.PrivateKey(p256) + let commonName = subject ?? domains[0] + let name = try DistinguishedName { + CommonName(commonName) + } + let extensions = try Certificate.Extensions { + SubjectAlternativeNames(domains.map({ GeneralName.dnsName($0) })) + } + let extensionRequest = ExtensionRequest(extensions: extensions) + let attributes = try CertificateSigningRequest.Attributes( + [.init(extensionRequest)] + ) + let csr = try CertificateSigningRequest( + version: .v1, + subject: name, + privateKey: privateKey, + attributes: attributes, + signatureAlgorithm: .ecdsaWithSHA256 + ) + + let account = try await finalize(order: order, withCsr: csr) + + return (privateKey, csr, account) + } + + /// Finalizes an Order and send the RSA CSR. + /// - Parameters: + /// - order: The `AcmeOrderInfo` returned by the call to `.create()` + /// - subject: Subject of certificate + /// - domains: Domains for certificate + /// - Throws: Errors that can occur when executing the request. + /// - Returns: Returns `Certificate.PrivateKey`, `CertificateSigningRequest` and `Account`. + public func finalizeWithRsa(order: AcmeOrderInfo, subject: String? = nil, domains: [String]) async throws -> (Certificate.PrivateKey, CertificateSigningRequest, AcmeOrderInfo) { + guard domains.count > 0 else { + throw AcmeError.noDomains("At least 1 DNS name is required") + } + + let p256 = try _CryptoExtras._RSA.Signing.PrivateKey(keySize: .bits2048) + let privateKey = Certificate.PrivateKey(p256) + let commonName = subject ?? domains[0] + let name = try DistinguishedName { + CommonName(commonName) + } + let extensions = try Certificate.Extensions { + SubjectAlternativeNames(domains.map({ GeneralName.dnsName($0) })) + } + let extensionRequest = ExtensionRequest(extensions: extensions) + let attributes = try CertificateSigningRequest.Attributes( + [.init(extensionRequest)] + ) + let csr = try CertificateSigningRequest( + version: .v1, + subject: name, + privateKey: privateKey, + attributes: attributes, + signatureAlgorithm: .sha256WithRSAEncryption + ) + + let account = try await finalize(order: order, withCsr: csr) + + return (privateKey, csr, account) + } /// Finalizes an Order and send the CSR. /// - Parameters: /// - order: The `AcmeOrderInfo` returned by the call to `.create()` - /// - withCsr: An instance of an `AcmeX509Csr`. + /// - withCsr: An instance of an `Certificate`. /// - Throws: Errors that can occur when executing the request. /// - Returns: Returns the `Account`. - public func finalize(order: AcmeOrderInfo, withCsr: AcmeX509Csr) async throws -> AcmeOrderInfo { + public func finalize(order: AcmeOrderInfo, withCsr csr: CertificateSigningRequest) async throws -> AcmeOrderInfo { try await self.client.ensureLoggedIn() - - let csrBytes = try withCsr.derEncoded() + + var serializer = DER.Serializer() + try serializer.serialize(csr) + + let csrBytes = Data(serializer.serializedBytes) let pemStr = csrBytes.toBase64UrlString() let ep = FinalizeOrderEndpoint(orderURL: order.finalize, spec: .init(csr: pemStr)) diff --git a/Sources/AcmeSwift/Models/AcmeError.swift b/Sources/AcmeSwift/Models/AcmeError.swift index 72e083b..202e6e7 100644 --- a/Sources/AcmeSwift/Models/AcmeError.swift +++ b/Sources/AcmeSwift/Models/AcmeError.swift @@ -26,6 +26,8 @@ public enum AcmeError: Error { /// A resource should have a URL, returned in a response "Location" header, but couldn't find or parse the header. case noResourceUrl + + case noDomains(String) } public struct AcmeResponseError: Codable, Error { diff --git a/Sources/AcmeSwift/x509/AcmeX509Csr.swift b/Sources/AcmeSwift/x509/AcmeX509Csr.swift deleted file mode 100644 index 7424582..0000000 --- a/Sources/AcmeSwift/x509/AcmeX509Csr.swift +++ /dev/null @@ -1,176 +0,0 @@ -import Foundation -import Crypto -import _CryptoExtras -import PotentASN1 -import CryptoKit - -public struct AcmeX509Csr { - - /// The private key used to generate the CSR, in DER format - private(set) public var privateKey: Data - - /// The private key used to generate the CSR, in PEM format with headers - private(set) public var privateKeyPem: String - - private var asn1Csr: Asn1CertificateSigningRequest - - public static func rsa(key: _CryptoExtras._RSA.Signing.PrivateKey = try! .init(keySize: .bits2048), subject: X509Subject? = nil, domains: [String], keyUsage: X509KeyUsage? = nil, extendedKeyUsage: [X509ExtendedKeyUsageOID]? = nil) throws -> AcmeX509Csr { - guard domains.count > 0 else { - throw X509Error.noDomains("At least 1 DNS name is required") - } - let rsa = try RsaCSR.init(key: key, subject: subject, domains: domains, keyUsage: keyUsage, extendedKeyUsage: extendedKeyUsage) - let csr = self.init(privateKey: rsa.key.derRepresentation, privateKeyPem: rsa.key.pemEncoded(), asn1Csr: rsa.asn1Csr) - - return csr - } - - public static func rsa(key: _CryptoExtras._RSA.Signing.PrivateKey = try! .init(keySize: .bits2048), subject: X509Subject? = nil, order: AcmeOrderInfo, keyUsage: X509KeyUsage? = nil, extendedKeyUsage: [X509ExtendedKeyUsageOID]? = nil) throws -> AcmeX509Csr { - guard order.identifiers.count > 0 else { - throw X509Error.noDomains("At least 1 DNS name is required") - } - let domains = order.identifiers.map{$0.value} - let rsa = try RsaCSR.init(key: key, subject: subject, domains: domains, keyUsage: keyUsage, extendedKeyUsage: extendedKeyUsage) - let csr = self.init(privateKey: rsa.key.derRepresentation, privateKeyPem: rsa.key.pemEncoded(), asn1Csr: rsa.asn1Csr) - - return csr - } - - public static func rsa(keyPem: String, subject: X509Subject? = nil, domains: [String]) throws -> AcmeX509Csr { - let key = try _CryptoExtras._RSA.Signing.PrivateKey.init(pemRepresentation: keyPem) - return try rsa(key: key, subject: subject, domains: domains) - } - - public static func rsa(keyPem: String, subject: X509Subject? = nil, order: AcmeOrderInfo) throws -> AcmeX509Csr { - let key = try _CryptoExtras._RSA.Signing.PrivateKey.init(pemRepresentation: keyPem) - let domains = order.identifiers.map{$0.value} - return try rsa(key: key, subject: subject, domains: domains) - } - - /// A CSR using a P256 private key - public static func ecdsa(key: Crypto.P256.Signing.PrivateKey = .init(), subject: X509Subject? = nil, domains: [String], keyUsage: X509KeyUsage? = nil, extendedKeyUsage: [X509ExtendedKeyUsageOID]? = nil) throws -> AcmeX509Csr { - guard domains.count > 0 else { - throw X509Error.noDomains("At least 1 DNS name is required") - } - let ecdsa = try EcdsaCSR.init(key: key, subject: subject, domains: domains, keyUsage: keyUsage, extendedKeyUsage: extendedKeyUsage) - return self.init(privateKey: ecdsa.key.derRepresentation, privateKeyPem: ecdsa.key.pemEncoded(), asn1Csr: ecdsa.asn1Csr) - } - - /// A CSR using a P256 private key - public static func ecdsa(key: Crypto.P256.Signing.PrivateKey = .init(), subject: X509Subject? = nil, order: AcmeOrderInfo, keyUsage: X509KeyUsage? = nil, extendedKeyUsage: [X509ExtendedKeyUsageOID]? = nil) throws -> AcmeX509Csr { - guard order.identifiers.count > 0 else { - throw X509Error.noDomains("At least 1 DNS name is required") - } - let domains = order.identifiers.map{$0.value} - let ecdsa = try EcdsaCSR.init(key: key, subject: subject, domains: domains, keyUsage: keyUsage, extendedKeyUsage: extendedKeyUsage) - return self.init(privateKey: ecdsa.key.derRepresentation, privateKeyPem: ecdsa.key.pemEncoded(), asn1Csr: ecdsa.asn1Csr) - } - - /// A CSR using a P256 private key - public static func ecdsa(keyPem: String, subject: X509Subject? = nil, domains: [String]) throws -> AcmeX509Csr { - let key = try Crypto.P256.Signing.PrivateKey.init(pemRepresentation: keyPem) - return try ecdsa(key: key, subject: subject, domains: domains) - } - - /// A CSR using a P256 private key - public static func ecdsa(keyPem: String, subject: X509Subject? = nil, order: AcmeOrderInfo) throws -> AcmeX509Csr { - let key = try Crypto.P256.Signing.PrivateKey.init(pemRepresentation: keyPem) - let domains = order.identifiers.map{$0.value} - return try ecdsa(key: key, subject: subject, domains: domains) - } - - /// Returns the CSR as DER Data - public func derEncoded() throws -> Data { - let encoder = ASN1Encoder(schema: Asn1CertificateSigningRequest.schema) - return try encoder.encode(self.asn1Csr) - } - - /// Returns the CSR as a PEM encoded string with headers. - public func pemEncoded() throws -> String { - let data = try self.derEncoded().base64EncodedString(options: .lineLength64Characters) - return """ - -----BEGIN CERTIFICATE REQUEST----- - \(data) - -----END CERTIFICATE REQUEST----- - """ - } -} - - -/// A CSR using an ECDSA key -struct EcdsaCSR { - var key: Crypto.P256.Signing.PrivateKey - var subject: X509Subject - var domains: [String] - var asn1Csr: Asn1CertificateSigningRequest - - init(key: Crypto.P256.Signing.PrivateKey = .init(), subject: X509Subject? = nil, domains: [String], keyUsage: X509KeyUsage?, extendedKeyUsage: [X509ExtendedKeyUsageOID]?) throws { - - self.domains = domains - self.subject = subject ?? .init(commonName: domains.first!) - self.key = key - - let crInfo = Asn1CertificateRequestInfo( - subject: .init(subject: subject ?? .init(commonName: domains.first!)), - subjectPKInfo: .init( - algorithm: Asn1AlgorithmIdentifier( - algorithm: X509PublicKeyAlgorithmOID.idEcPublicKey.value, - parameters: ECCurve.prime256v1.value - ), - publicKey: self.key.publicKey.x963Representation - ), - extensions: .init( - san: .init(dnsNames: domains), - keyUsage: keyUsage != nil ? .init(keyUsage!) : nil, - extendedKeyUsage: extendedKeyUsage != nil ? .init(usages: extendedKeyUsage!) : nil - ) - ) - let crInfoEncoder = ASN1Encoder(schema: Asn1CertificateRequestInfo.schema) - let crInfoEncoded = try crInfoEncoder.encode(crInfo) - - let digest = Crypto.SHA256.hash(data: crInfoEncoded) - let signature = try self.key.signature(for: digest) - - self.asn1Csr = .init( - certificationRequestInfo: crInfo, - signatureAlgorithm: .init(algorithm: OID(X509SignatureAlgorithmOID.ecdsaWithSHA256.value)), - signature: signature.derRepresentation - ) - } -} - -/// A CSR using an RSA key -struct RsaCSR { - var key: _CryptoExtras._RSA.Signing.PrivateKey - public var subject: X509Subject - var domains: [String] - var asn1Csr: Asn1CertificateSigningRequest - - init(key: _CryptoExtras._RSA.Signing.PrivateKey = try! .init(keySize: .bits2048), subject: X509Subject? = nil, domains: [String], keyUsage: X509KeyUsage?, extendedKeyUsage: [X509ExtendedKeyUsageOID]?) throws { - self.domains = domains - self.subject = subject ?? .init(commonName: domains.first!) - self.key = key - - let crInfo = Asn1CertificateRequestInfo( - subject: .init(subject: subject ?? .init(commonName: domains.first!)), - subjectPKInfo: .init( - algorithm: .rsaEncryption, - publicKey: self.key.publicKey.derRepresentation - ), - extensions: .init( - san: .init(dnsNames: domains), - keyUsage: keyUsage != nil ? .init(keyUsage!) : nil, - extendedKeyUsage: extendedKeyUsage != nil ? .init(usages: extendedKeyUsage!) : nil - ) - ) - let crInfoEncoder = ASN1Encoder(schema: Asn1CertificateRequestInfo.schema) - let crInfoEncoded = try crInfoEncoder.encode(crInfo) - let digest = Crypto.SHA256.hash(data: crInfoEncoded) - let signature = try self.key.signature(for: digest, padding: .insecurePKCS1v1_5) - - self.asn1Csr = .init( - certificationRequestInfo: crInfo, - signatureAlgorithm: .init(algorithm: OID(X509SignatureAlgorithmOID.sha256WithRSAEncryption.value)), - signature: signature.rawRepresentation - ) - } -} diff --git a/Sources/AcmeSwift/x509/Asn1CertificateRequestInfo.swift b/Sources/AcmeSwift/x509/Asn1CertificateRequestInfo.swift deleted file mode 100644 index e9ab86c..0000000 --- a/Sources/AcmeSwift/x509/Asn1CertificateRequestInfo.swift +++ /dev/null @@ -1,77 +0,0 @@ -import Foundation -import PotentASN1 - -/*struct X509CertificateRequestInfo { - public var version: CSRVersion = .v1 - public var subject: X509Subject - public var subjectPKInfo: Asn1SubjectPublicKeyInfo - public var attributes: [Asn1Extension] - - enum CSRVersion: Int, Codable { - case v1 = 0 - } -}*/ - - struct Asn1CertificateRequestInfo: HasSchemaProtocol { - init(version: Int = 0, subject: Asn1Subject, subjectPKInfo: Asn1SubjectPublicKeyInfo, extensions: Asn1CertificateRequestInfo.Extensions) { - self.version = version - self.subject = subject - self.subjectPKInfo = subjectPKInfo - self.extensions = [extensions] - } - - var version: Int = 0 - var subject: Asn1Subject - var subjectPKInfo: Asn1SubjectPublicKeyInfo - var extensions: [Extensions] - - static var schema: Schema { - .sequence([ - "version": .version(.integer(allowed: 0 ..< 1)), - "subject": Asn1Subject.schema, - "subjectPKInfo": Asn1SubjectPublicKeyInfo.schema, - "extensions": .implicit( - 0, - .setOf(Extensions.schema) - ) - ]) - } - - struct Extensions: HasSchemaProtocol { - private(set) var oid: OID = CsrExtension.extensionRequest.value - var value: [ExtensionValue] - - init(san: Asn1SubjectAltName, keyUsage: Asn1KeyUsage? = nil, extendedKeyUsage: Asn1ExtendedKeyUsage? = nil) { - self.value = [ - .init(san: san, keyUsage: keyUsage, extendedKeyUsage: extendedKeyUsage, basicConstraints: nil) - ] - } - - static var schema: Schema { - .sequence([ - "oid": .objectIdentifier(), - "value": .setOf( - ExtensionValue.schema, - size: .is(1) - ) - ]) - } - - struct ExtensionValue: HasSchemaProtocol { - var san: Asn1SubjectAltName - var keyUsage: Asn1KeyUsage? = nil - var extendedKeyUsage: Asn1ExtendedKeyUsage? = nil - var basicConstraints: Asn1BasicConstraints? = nil - - static var schema: Schema { - .sequence([ - "san": Asn1SubjectAltName.schema, - "keyUsage": .optional(Asn1KeyUsage.schema), - "extendedKeyUsage": .optional(Asn1ExtendedKeyUsage.schema), - "basicConstraints": .optional(Asn1BasicConstraints.schema) - ]) - } - } - - } -} diff --git a/Sources/AcmeSwift/x509/Asn1CertificateSigningRequest.swift b/Sources/AcmeSwift/x509/Asn1CertificateSigningRequest.swift deleted file mode 100644 index fe78597..0000000 --- a/Sources/AcmeSwift/x509/Asn1CertificateSigningRequest.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1CertificateSigningRequest: HasSchemaProtocol { - var certificationRequestInfo: Asn1CertificateRequestInfo - var signatureAlgorithm: Asn1AlgorithmIdentifier - var signature: Data - - static var schema: Schema { - .sequence([ - "certificationRequestInfo": Asn1CertificateRequestInfo.schema, - "signatureAlgorithm": Asn1AlgorithmIdentifier.schema, - "signature": .bitString(), - ]) - } -} - - diff --git a/Sources/AcmeSwift/x509/Asn1Subject.swift b/Sources/AcmeSwift/x509/Asn1Subject.swift deleted file mode 100644 index 4c34abf..0000000 --- a/Sources/AcmeSwift/x509/Asn1Subject.swift +++ /dev/null @@ -1,43 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1Subject: HasSchemaProtocol { - var values: [X509Item] = [] - - init(subject: X509Subject) { - if let c = subject.countryName { - self.values.append(.init(oid: [2,5,4,6], value: c)) - } - if let s = subject.stateOrProvinceName { - self.values.append(.init(oid: [2,5,4,8], value: s)) - } - if let l = subject.localityName { - self.values.append(.init(oid: [2,5,4,7], value: l)) - } - if let o = subject.organizationName { - self.values.append(.init(oid: [2,5,4,10], value: o)) - } - if let ou = subject.organizationalUnitName { - self.values.append(.init(oid: [2,5,4,11], value: ou)) - } - if let cn = subject.commonName { - self.values.append(.init(oid: [2,5,4,3], value: cn)) - } - } - - static var schema: Schema { - .sequence( - ["values": .setOf(X509Kind.schema())] - ) - // TODO: set proper kind and range for each field, or at least check spec - /*.sequence([ - "countryName": X509Kind.schema(), - "stateOrProvinceName": X509Kind.schema(), - "localityName": X509Kind.schema(), - "organizationName": X509Kind.schema(), - "organizationalUnitName": X509Kind.schema(), - "commonName": X509Kind.schema(), - "emailAddress": X509Kind.schema() - ])*/ - } -} diff --git a/Sources/AcmeSwift/x509/Asn1SubjectPublicKeyInfo.swift b/Sources/AcmeSwift/x509/Asn1SubjectPublicKeyInfo.swift deleted file mode 100644 index 3daaadf..0000000 --- a/Sources/AcmeSwift/x509/Asn1SubjectPublicKeyInfo.swift +++ /dev/null @@ -1,41 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1SubjectPublicKeyInfo: HasSchemaProtocol { - - var algorithm: Asn1AlgorithmIdentifier - - /// The Subject public key - var publicKey: Data - - init(algorithm: X509PublicKeyAlgorithmOID, publicKey: Data) { - self.algorithm = Asn1AlgorithmIdentifier(algorithm: algorithm.value) - self.publicKey = publicKey - } - - init(algorithm: Asn1AlgorithmIdentifier, publicKey: Data) { - self.algorithm = algorithm - self.publicKey = publicKey - } - - static var schema: Schema { - .sequence([ - //"algorithm": .sequenceOf(.objectIdentifier(), .none), - "algorithm": Asn1AlgorithmIdentifier.schema, - "publicKey": .bitString(), - ]) - } -} - -struct Asn1AlgorithmIdentifier: HasSchemaProtocol { - var algorithm: OID - var parameters: OID? = nil - - static var schema: Schema { - .sequence([ - "algorithm": .objectIdentifier(), - "parameters": .objectIdentifier() - ]) - } - -} diff --git a/Sources/AcmeSwift/x509/Extensions/Asn1BasicConstraints.swift b/Sources/AcmeSwift/x509/Extensions/Asn1BasicConstraints.swift deleted file mode 100644 index 3c655a2..0000000 --- a/Sources/AcmeSwift/x509/Extensions/Asn1BasicConstraints.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1BasicConstraints: X509ExtensionProtocol { - private(set) var oid: OID = CsrExtensionsOID.basicConstraints.value - var critical: Bool = true - var data: Data - - init(isCa: Bool = false, pathLen: UInt? = nil) { - let constraints = Constraints(isCa: isCa, pathLen: pathLen) - let asn1Encoder = ASN1Encoder(schema: Constraints.schema) - self.data = try! asn1Encoder.encode(constraints) - } - - struct Constraints: HasSchemaProtocol { - var isCa: Bool = false - var pathLen: UInt? = nil - - static var schema: Schema { - .sequence([ - "isCa": .boolean(), - "pathLen": .optional(.integer()) - ]) - } - } -} diff --git a/Sources/AcmeSwift/x509/Extensions/Asn1ExtendedKeyUsage.swift b/Sources/AcmeSwift/x509/Extensions/Asn1ExtendedKeyUsage.swift deleted file mode 100644 index 570147e..0000000 --- a/Sources/AcmeSwift/x509/Extensions/Asn1ExtendedKeyUsage.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1ExtendedKeyUsage: X509ExtensionProtocol { - private(set) var oid: OID = CsrExtensionsOID.extendedKeyUsage.value - var critical: Bool = true - var data: Data = Data() - - init(usages: [X509ExtendedKeyUsageOID]) { - var extKeyUsages: [OID] = [] - for usage in usages { - extKeyUsages.append(usage.value) - } - let asn1Encoder = ASN1Encoder(schema: .sequenceOf(.objectIdentifier(), size: .min(1))) - self.data = try! asn1Encoder.encode(extKeyUsages) - } -} diff --git a/Sources/AcmeSwift/x509/Extensions/Asn1KeyUsage.swift b/Sources/AcmeSwift/x509/Extensions/Asn1KeyUsage.swift deleted file mode 100644 index 2e16307..0000000 --- a/Sources/AcmeSwift/x509/Extensions/Asn1KeyUsage.swift +++ /dev/null @@ -1,13 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1KeyUsage: X509ExtensionProtocol { - private(set) var oid: OID = CsrExtensionsOID.keyUsage.value - var critical: Bool = true - var data: Data - - init(_ value: X509KeyUsage) { - let asn1Encoder = ASN1Encoder(schema: .bitString()) - self.data = try! asn1Encoder.encode(value.rawValue) - } -} diff --git a/Sources/AcmeSwift/x509/Extensions/Asn1SubjectAltName.swift b/Sources/AcmeSwift/x509/Extensions/Asn1SubjectAltName.swift deleted file mode 100644 index 4757ad6..0000000 --- a/Sources/AcmeSwift/x509/Extensions/Asn1SubjectAltName.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -import PotentASN1 - -struct Asn1SubjectAltName: X509ExtensionProtocol { - private(set) var oid: OID = CsrExtensionsOID.subjectAltName.value - var critical: Bool = true - var data: Data - - init(dnsNames: [String]) { - var generalNames: GeneralNames = [] - for name in dnsNames { - generalNames.append(X509GeneralName.dnsName(name)) - } - // The actual value of an extension must be passed as an ASN.1 Octet String - let asn1Encoder = ASN1Encoder(schema: GeneralNames.schema) - self.data = try! asn1Encoder.encode(generalNames) - } -} diff --git a/Sources/AcmeSwift/x509/Extensions/X509ExtensionProtocol.swift b/Sources/AcmeSwift/x509/Extensions/X509ExtensionProtocol.swift deleted file mode 100644 index a625bc7..0000000 --- a/Sources/AcmeSwift/x509/Extensions/X509ExtensionProtocol.swift +++ /dev/null @@ -1,18 +0,0 @@ -import Foundation -import PotentASN1 - -protocol X509ExtensionProtocol: HasSchemaProtocol { - var oid: OID {get} - var critical: Bool {get set} - var data: Data {get set} -} - -extension X509ExtensionProtocol { - static var schema: Schema { - .sequence([ - "oid": .objectIdentifier(), - "critical": .boolean(), - "data": .octetString() - ]) - } -} diff --git a/Sources/AcmeSwift/x509/GeneralName.swift b/Sources/AcmeSwift/x509/GeneralName.swift deleted file mode 100644 index 108da0d..0000000 --- a/Sources/AcmeSwift/x509/GeneralName.swift +++ /dev/null @@ -1,229 +0,0 @@ -// From the excellent https://github.com/outfoxx/Shield - -// GeneralName.swift -// Shield -// -// Copyright © 2019 Outfox, inc. -// -// -// Distributed under the MIT License, See LICENSE for details. -// - -import Foundation -import PotentASN1 - - -typealias GeneralNames = [X509GeneralName] -extension GeneralNames { - static var schema: Schema { - .sequenceOf(X509GeneralName.schema, size: .min(1)) - } -} - -public enum X509GeneralName: HasSchemaProtocol, TaggedValue { - //case otherName(OtherName) - case rfc822Name(String) - case dnsName(String) - case x400Address(ASN1) - //case directoryName(Name) - //case ediPartyName(EDIPartyName) - case uniformResourceIdentifier(String) - case ipAddress(Data) - case registeredID(ObjectIdentifier) - - public init?(tag: ASN1.AnyTag, value: Any?) { - switch ASN1.Tag.value(from: tag, in: .contextSpecific) { - /*case 0: - guard - let values = value as? [ASN1], - let name = try? ASN1Decoder(schema: Schemas.OtherName).decodeTree(OtherName.self, from: .sequence(values)) - else { - return nil - } - self = .otherName(name)*/ - case 1: - guard let value = value as? AnyString else { return nil } - self = .rfc822Name(value.storage) - case 2: - guard let value = value as? AnyString else { return nil } - self = .dnsName(value.storage) - case 3: - guard let value = value as? ASN1 else { return nil } - self = .x400Address(value) - /*case 4: - guard - let values = value as? [ASN1], - let name = try? ASN1Decoder(schema: Schemas.Name).decodeTree(Name.self, from: .sequence(values)) - else { - return nil - } - self = .directoryName(name) - case 5: - guard - let values = value as? [ASN1], - let name = try? ASN1Decoder(schema: Schemas.EDIPartyName).decodeTree(EDIPartyName.self, from: .sequence(values)) - else { - return nil - } - self = .ediPartyName(name)*/ - case 6: - guard let value = value as? AnyString else { return nil } - self = .uniformResourceIdentifier(value.storage) - case 7: - guard let value = value as? Data else { return nil } - self = .ipAddress(value) - case 8: - guard let value = value as? ObjectIdentifier else { return nil } - self = .registeredID(value) - default: - return nil - } - } - - public var tag: ASN1.AnyTag { - switch self { - //case .otherName: return 0 - case .rfc822Name: return 1 - case .dnsName: return 2 - case .x400Address: return 3 - //case .directoryName: return 4 - //case .ediPartyName: return 5 - case .uniformResourceIdentifier: return 6 - case .ipAddress: return 7 - case .registeredID: return 8 - } - } - - public var value: Any? { - switch self { - //case .otherName(let value): return value - case .rfc822Name(let value): return value - case .dnsName(let value): return value - case .x400Address(let value): return value - //case .directoryName(let value): return value - //case .ediPartyName(let value): return value - case .uniformResourceIdentifier(let value): return value - case .ipAddress(let value): return value - case .registeredID(let value): return value - } - } - - public func encode(schema: Schema) throws -> ASN1 { - let encoder = ASN1Encoder(schema: schema) - switch self { - //case .otherName(let value): return try encoder.encodeTree(value) - case .rfc822Name(let value): return try encoder.encodeTree(value) - case .dnsName(let value): return try encoder.encodeTree(value) - case .x400Address(let value): return value - //case .directoryName(let value): return try encoder.encodeTree(value) - //case .ediPartyName(let value): return try encoder.encodeTree(value) - case .uniformResourceIdentifier(let value): return try encoder.encodeTree(value) - case .ipAddress(let value): return try encoder.encodeTree(value) - case .registeredID(let value): return try encoder.encodeTree(value) - } - } - - static let schema: Schema = - .choiceOf([ - //.implicit(0, OtherName), - .implicit(1, .string(kind: .ia5)), - .implicit(2, .string(kind: .ia5)), - .implicit(3, .any), - //.explicit(4, Name), - //.implicit(5, EDIPartyName), - .implicit(6, .string(kind: .ia5)), - .implicit(7, .octetString()), - .implicit(8, .objectIdentifier()), - ]) -} - - -extension X509GeneralName: Codable { - - public init(from decoder: Swift.Decoder) throws { - var container = try decoder.unkeyedContainer() - let tag = try container.decode(UInt8.self) - switch ASN1.Tag.value(from: tag, in: .contextSpecific) { - //case 0: - // self = .otherName(try container.decode(OtherName.self)) - - case 1: - self = .rfc822Name(try container.decode(String.self)) - - case 2: - self = .dnsName(try container.decode(String.self)) - - case 3: - self = .x400Address(try container.decode(ASN1.self)) - - /*case 4: - self = .directoryName(try container.decode(Name.self)) - - case 5: - self = .ediPartyName(try container.decode(EDIPartyName.self)) - */ - case 6: - self = .uniformResourceIdentifier(try container.decode(String.self)) - - case 7: - self = .ipAddress(try container.decode(Data.self)) - - case 8: - self = .registeredID(try container.decode(ObjectIdentifier.self)) - - default: - throw DecodingError.dataCorruptedError(in: container, debugDescription: "No matching tag") - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.unkeyedContainer() - switch self { - /*case .otherName(let value): - try container.encode(ASN1.Tag.tag(from: 0, in: .contextSpecific, constructed: true)) - try container.encode(value) - */ - case .rfc822Name(let value): - try container.encode(ASN1.Tag.tag(from: 1, in: .contextSpecific, constructed: false)) - try container.encode(value) - - case .dnsName(let value): - try container.encode(ASN1.Tag.tag(from: 2, in: .contextSpecific, constructed: false)) - try container.encode(value) - - case .x400Address(let value): - try container.encode(ASN1.Tag.tag(from: 3, in: .contextSpecific, constructed: true)) - try container.encode(value) - - /*case .directoryName(let value): - try container.encode(ASN1.Tag.tag(from: 4, in: .contextSpecific, constructed: true)) - try container.encode(value) - - case .ediPartyName(let value): - try container.encode(ASN1.Tag.tag(from: 5, in: .contextSpecific, constructed: true)) - try container.encode(value) - */ - case .uniformResourceIdentifier(let value): - try container.encode(ASN1.Tag.tag(from: 6, in: .contextSpecific, constructed: false)) - try container.encode(value) - - case .ipAddress(let value): - try container.encode(ASN1.Tag.tag(from: 7, in: .contextSpecific, constructed: false)) - try container.encode(value) - - case .registeredID(let value): - try container.encode(ASN1.Tag.tag(from: 8, in: .contextSpecific, constructed: false)) - try container.encode(value) - } - } - -} - -private func encodeToSequence(_ value: T, using schema: Schema) throws -> [ASN1]? { - return try ASN1Encoder(schema: schema).encodeTree(value).sequenceValue -} - -private func decodeFromSequence(_ type: T.Type, from value: Any?, using schema: Schema) -> T? { - guard let values = value as? [ASN1] else { return nil } - return try? ASN1Decoder(schema: schema).decodeTree(type, from: .sequence(values)) -} diff --git a/Sources/AcmeSwift/x509/HasSchemaProtocol.swift b/Sources/AcmeSwift/x509/HasSchemaProtocol.swift deleted file mode 100644 index d5c4192..0000000 --- a/Sources/AcmeSwift/x509/HasSchemaProtocol.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation -import PotentASN1 - -protocol HasSchemaProtocol: Codable { - static var schema: Schema {get} -} diff --git a/Sources/AcmeSwift/x509/OIDs.swift b/Sources/AcmeSwift/x509/OIDs.swift deleted file mode 100644 index 7f78ef3..0000000 --- a/Sources/AcmeSwift/x509/OIDs.swift +++ /dev/null @@ -1,135 +0,0 @@ -import Foundation -import PotentASN1 - -public enum X509SignatureAlgorithmOID { - case sha256WithRSAEncryption - case ecdsaWithSHA256 - case ecdsaWithSHA512 - - var value: [UInt64] { - switch self { - case .sha256WithRSAEncryption: return [1,2,840,113549,1,1,11] - case .ecdsaWithSHA256: return [1,2,840,10045,4,3,2] - case .ecdsaWithSHA512: return [1,2,840,10045,4,3,4] - } - } -} - -enum X509PublicKeyAlgorithmOID { - case rsaEncryption - case sha256WithRSAEncryption - case sha512WithRSAEncryption - case idEcDH - case idEcPublicKey - - var value: OID { - switch self { - case .rsaEncryption: return [1,2,840,113549,1,1,1] - case .sha256WithRSAEncryption: return [1,2,840,113549,1,1,11] - case .sha512WithRSAEncryption: return [1,2,840,113549,1,1,13] - case .idEcDH: return [1,3,132,1,12] - case .idEcPublicKey: return [1,2,840,10045,2,1] - } - } -} - -enum ECCurve { - case prime256v1 - - var value: OID { - switch self { - case .prime256v1: return [1,2,840,10045,3,1,7] - } - } -} - -enum CsrExtension { - case extensionRequest - - var value: OID { - switch self { - case .extensionRequest: return [1,2,840,113549,1,9,14] - } - } -} - -enum CsrExtensionsOID { - case basicConstraints - case keyUsage - case extendedKeyUsage - case subjectAltName - - var value: OID { - switch self { - case .basicConstraints: return [2,5,29,19] - case .keyUsage: return [2,5,29,15] - case .extendedKeyUsage: return [2,5,29,37] - case .subjectAltName: return [2,5,29,17] - } - } -} - -enum KeyUsageOID { - case digitalSignature - case nonRepudiation - case keyEncipherment - case dataEncipherment - case keyAgreement - case keyCertSign - case cRLSign - case encipherOnly - case decipherOnly - - var value: OID { - switch self { - case .digitalSignature: return [2,5,29,15,0] - case .nonRepudiation: return [2,5,29,15,1] - case .keyEncipherment: return [2,5,29,15,2] - case .dataEncipherment: return [2,5,29,15,3] - case .keyAgreement: return [2,5,29,15,4] - case .keyCertSign: return [2,5,29,15,5] - case .cRLSign: return [2,5,29,15,6] - case .encipherOnly: return [2,5,29,15,7] - case .decipherOnly: return [2,5,29,15,8] - } - } -} - -public enum X509ExtendedKeyUsageOID: Codable { - /// The most common type. A certificate for a server (web...) - case serverAuth - /// A certificate for client authentication (mutual TLS) - case clientAuth - - case codesigning - - case ocspSigning - - var value: OID { - switch self { - case .serverAuth: return [1,3,6,1,5,5,7,3,1] - case .clientAuth: return [1,3,6,1,5,5,7,3,2] - case .codesigning: return [1,3,6,1,5,5,7,3,3] - case .ocspSigning: return [1,3,6,1,5,5,7,3,9] - } - } -} - -enum GeneralNameOID { - /// Email address - case rfc822Name - - /// DNS name - case dNSName - - // URI - case uniformResourceIdentifier - - var value: OID { - switch self { - case .rfc822Name: return [2,5,29,17,1] - case .dNSName: return [2,5,29,17,2] - case .uniformResourceIdentifier: return [2,5,29,17,6] - } - } -} diff --git a/Sources/AcmeSwift/x509/PrivateKey+pemEncoded.swift b/Sources/AcmeSwift/x509/PrivateKey+pemEncoded.swift deleted file mode 100644 index 0997fca..0000000 --- a/Sources/AcmeSwift/x509/PrivateKey+pemEncoded.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation -import Crypto -import _CryptoExtras - -public extension Crypto.P256.Signing.PrivateKey { - /// A PEM encoded representation of the private key. - /// If `withHeaders` is set to `true`, the returned value can be used as-is by most software using PEM keys. - func pemEncoded(withHeaders: Bool = true) -> String { - let privateKeyData = self.derRepresentation.base64EncodedString(options: .lineLength64Characters) - if !withHeaders { - return privateKeyData - } - else { - return """ - -----BEGIN EC PRIVATE KEY----- - \(privateKeyData) - -----END EC PRIVATE KEY---- - """ - } - } -} - -public extension _CryptoExtras._RSA.Signing.PrivateKey { - /// A PEM encoded representation of the private key. - /// If `withHeaders` is set to `true`, the returned value can be used as-is by most software using PEM keys. - func pemEncoded(withHeaders: Bool = true) -> String { - let privateKeyData = self.derRepresentation.base64EncodedString(options: .lineLength64Characters) - if !withHeaders { - return privateKeyData - } - else { - return """ - -----BEGIN PRIVATE KEY----- - \(privateKeyData) - -----END PRIVATE KEY---- - """ - } - } -} diff --git a/Sources/AcmeSwift/x509/X509Error.swift b/Sources/AcmeSwift/x509/X509Error.swift deleted file mode 100644 index 7417ce1..0000000 --- a/Sources/AcmeSwift/x509/X509Error.swift +++ /dev/null @@ -1,5 +0,0 @@ -import Foundation - -public enum X509Error: Error { - case noDomains(String) -} diff --git a/Sources/AcmeSwift/x509/X509KeyUsage.swift b/Sources/AcmeSwift/x509/X509KeyUsage.swift deleted file mode 100644 index ef914e5..0000000 --- a/Sources/AcmeSwift/x509/X509KeyUsage.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation - -public struct X509KeyUsage: OptionSet { - public let rawValue: UInt16 - - public init(rawValue: UInt16) { - self.rawValue = rawValue - } - - public static let digitalSignature = X509KeyUsage(rawValue: 1 << 0) - public static let nonRepudiation = X509KeyUsage(rawValue: 1 << 1) - public static let keyEncipherment = X509KeyUsage(rawValue: 1 << 2) - public static let dataEncipherment = X509KeyUsage(rawValue: 1 << 3) - public static let keyAgreement = X509KeyUsage(rawValue: 1 << 4) - public static let keyCertSign = X509KeyUsage(rawValue: 1 << 5) - public static let cRLSign = X509KeyUsage(rawValue: 1 << 6) - public static let encipherOnly = X509KeyUsage(rawValue: 1 << 7) - public static let decipherOnly = X509KeyUsage(rawValue: 1 << 8) - public static let contentCommitment = nonRepudiation -} diff --git a/Sources/AcmeSwift/x509/X509Kind.swift b/Sources/AcmeSwift/x509/X509Kind.swift deleted file mode 100644 index 040957e..0000000 --- a/Sources/AcmeSwift/x509/X509Kind.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation -import PotentASN1 - -struct X509Kind { - static func schema(_ valueKind: Schema = .string(kind: .ia5, size: .range(1, 64))) -> Schema { - .sequence([ - "oid": .objectIdentifier(), - "value": valueKind - ]) - } -} - -struct X509Item: Codable { - var oid: OID - var value: T? -} diff --git a/Sources/AcmeSwift/x509/X509SignatureAlgorithm.swift b/Sources/AcmeSwift/x509/X509SignatureAlgorithm.swift deleted file mode 100644 index 3fb4ef2..0000000 --- a/Sources/AcmeSwift/x509/X509SignatureAlgorithm.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation -import PotentASN1 - -public struct X509SignatureAlgorithm: HasSchemaProtocol { - var oid: OID - - public init(_ kind: X509SignatureAlgorithmOID) { - self.oid = .init(kind.value) - } - - static var schema: Schema { - .sequence(["oid": .objectIdentifier()]) - } - -} diff --git a/Sources/AcmeSwift/x509/X509Subject.swift b/Sources/AcmeSwift/x509/X509Subject.swift deleted file mode 100644 index 440b3a4..0000000 --- a/Sources/AcmeSwift/x509/X509Subject.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation -import PotentASN1 - -public struct X509Subject { - /// C - public var countryName: String? - - /// S - public var stateOrProvinceName: String? - - /// L - public var localityName: String? - - /// O - public var organizationName: String? - - /// OU - public var organizationalUnitName: String? - - /// CN - public var commonName: String? - - /// E (deprecated) - //public var emailAddress: String? -} - diff --git a/Tests/AcmeSwiftTests/MiscTemp.swift b/Tests/AcmeSwiftTests/MiscTemp.swift index 26da067..d6505ab 100644 --- a/Tests/AcmeSwiftTests/MiscTemp.swift +++ b/Tests/AcmeSwiftTests/MiscTemp.swift @@ -2,29 +2,53 @@ import XCTest import AsyncHTTPClient import NIO import Logging -import PotentASN1 import Crypto import _CryptoExtras +import SwiftASN1 +import X509 @testable import AcmeSwift final class MiscTempTests: XCTestCase { func testHahi() throws { - let privateKey = Crypto.P256.Signing.PrivateKey.init() - let rsaPrivate = try _RSA.Signing.PrivateKey.init(keySize: .bits2048) - //privateKey.publicKey. - //try rsaPrivate.signature(for: Data()).rawRepresentation - - let csr = try AcmeX509Csr.ecdsa( - domains: ["www.nuw.run", "nuw.run"], - keyUsage: [.dataEncipherment, .digitalSignature], - extendedKeyUsage: [.clientAuth, .serverAuth] + let domains = ["www.nuw.run", "nuw.run"] + + let p256 = P256.Signing.PrivateKey() + let privateKey = Certificate.PrivateKey(p256) + let commonName = domains[0] + let name = try DistinguishedName { + CommonName(commonName) + } + let extensions = try Certificate.Extensions { + SubjectAlternativeNames(domains.map({ GeneralName.dnsName($0) })) + } + let extensionRequest = ExtensionRequest(extensions: extensions) + let attributes = try CertificateSigningRequest.Attributes( + [.init(extensionRequest)] + ) + let csr = try CertificateSigningRequest( + version: .v1, + subject: name, + privateKey: privateKey, + attributes: attributes, + signatureAlgorithm: .ecdsaWithSHA256 + ) + + print("\n ECDSA CSR='\(try! csr.serializeAsPEM().pemString)'") + print("\n ECDSA private key = '\(try! privateKey.serializeAsPEM().pemString)'") + + let p256RSA = try _CryptoExtras._RSA.Signing.PrivateKey(keySize: .bits2048) + let privateKeyRSA = Certificate.PrivateKey(p256RSA) + let csr2 = try CertificateSigningRequest( + version: .v1, + subject: name, + privateKey: privateKeyRSA, + attributes: attributes, + signatureAlgorithm: .sha256WithRSAEncryption ) - print("\n ECDSA CSR='\(try! csr.pemEncoded())'") - print("\n ECDSA private key = \(csr.privateKeyPem)") - let csr2 = try AcmeX509Csr.rsa(domains: ["www.nuw.run", "nuw.run"]) - print("\n RSA CSR='\(try! csr2.pemEncoded())'") - print("\n RSA private key = \(csr2.privateKeyPem)") + + print("\n RSA CSR='\(try! csr2.serializeAsPEM().pemString)'") + print("\n RSA private key = '\(try! privateKeyRSA.serializeAsPEM().pemString)'") } diff --git a/Tests/AcmeSwiftTests/OrderTests.swift b/Tests/AcmeSwiftTests/OrderTests.swift index f8eebdf..562aa60 100644 --- a/Tests/AcmeSwiftTests/OrderTests.swift +++ b/Tests/AcmeSwiftTests/OrderTests.swift @@ -2,6 +2,9 @@ import XCTest import AsyncHTTPClient import NIO import Logging +import SwiftASN1 +import X509 +import Crypto @testable import AcmeSwift @@ -100,14 +103,11 @@ final class OrderTests: XCTestCase { try await acme.orders.refresh(&order) print("\n => order: \(toJson(order))") - //let csr = try AcmeX509Csr.ecdsa(domains: domains) - let csr = try AcmeX509Csr.ecdsa(order: order) - - let finalized = try await acme.orders.finalize(order: order, withCsr: csr) + let (key, _, finalized) = try await acme.orders.finalizeWithEcdsa(order: order, domains: domains) let certs = try await acme.certificates.download(for: finalized) try certs.joined(separator: "\n").write(to: URL(fileURLWithPath: "cert.pem"), atomically: true, encoding: .utf8) - try csr.privateKeyPem.write(to: URL(fileURLWithPath: "key.pem"), atomically: true, encoding: .utf8) + try key.serializeAsPEM().pemString.write(to: URL(fileURLWithPath: "key.pem"), atomically: true, encoding: .utf8) } catch(let error) { print("\n•••• BOOM! \(error)")