|
|
@@ -14,14 +14,16 @@
|
|
|
* limitations under the License.
|
|
|
*/
|
|
|
|
|
|
-import Crypto
|
|
|
import Foundation
|
|
|
import GRPCCore
|
|
|
import GRPCNIOTransportHTTP2Posix
|
|
|
+import GRPCNIOTransportHTTP2TransportServices
|
|
|
import NIOSSL
|
|
|
-import SwiftASN1
|
|
|
import Testing
|
|
|
-import X509
|
|
|
+
|
|
|
+#if canImport(Network)
|
|
|
+import Network
|
|
|
+#endif
|
|
|
|
|
|
@Suite("HTTP/2 transport E2E tests with TLS enabled")
|
|
|
struct HTTP2TransportTLSEnabledTests {
|
|
|
@@ -29,8 +31,8 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
|
|
|
@Test(
|
|
|
"When using defaults, server does not perform client verification",
|
|
|
- arguments: [TransportKind.posix],
|
|
|
- [TransportKind.posix]
|
|
|
+ arguments: TransportKind.supported,
|
|
|
+ TransportKind.supported
|
|
|
)
|
|
|
func testRPC_Defaults_OK(
|
|
|
clientTransport: TransportKind,
|
|
|
@@ -58,8 +60,8 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
|
|
|
@Test(
|
|
|
"When using mTLS defaults, both client and server verify each others' certificates",
|
|
|
- arguments: [TransportKind.posix],
|
|
|
- [TransportKind.posix]
|
|
|
+ arguments: TransportKind.supported,
|
|
|
+ TransportKind.supported
|
|
|
)
|
|
|
func testRPC_mTLS_OK(
|
|
|
clientTransport: TransportKind,
|
|
|
@@ -89,8 +91,8 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
|
|
|
@Test(
|
|
|
"Error is surfaced when client fails server verification",
|
|
|
- arguments: [TransportKind.posix],
|
|
|
- [TransportKind.posix]
|
|
|
+ arguments: TransportKind.supported,
|
|
|
+ TransportKind.supported
|
|
|
)
|
|
|
// Verification should fail because the custom hostname is missing on the client.
|
|
|
func testClientFailsServerValidation(
|
|
|
@@ -98,43 +100,51 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
serverTransport: TransportKind
|
|
|
) async throws {
|
|
|
let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
|
|
|
- let clientConfig = self.makeMTLSClientConfig(
|
|
|
+ let clientTransportConfig = self.makeDefaultTLSClientConfig(
|
|
|
for: clientTransport,
|
|
|
certificateKeyPairs: certificateKeyPairs,
|
|
|
- serverHostname: "the-wrong-hostname"
|
|
|
+ authority: nil
|
|
|
)
|
|
|
-
|
|
|
- let serverConfig = self.makeMTLSServerConfig(
|
|
|
+ let serverTransportConfig = self.makeDefaultTLSServerConfig(
|
|
|
for: serverTransport,
|
|
|
- certificateKeyPairs: certificateKeyPairs,
|
|
|
- includeClientCertificateInTrustRoots: true
|
|
|
+ certificateKeyPairs: certificateKeyPairs
|
|
|
)
|
|
|
|
|
|
try await self.withClientAndServer(
|
|
|
- clientConfig: clientConfig,
|
|
|
- serverConfig: serverConfig
|
|
|
+ clientConfig: clientTransportConfig,
|
|
|
+ serverConfig: serverTransportConfig
|
|
|
) { control in
|
|
|
await #expect {
|
|
|
try await self.executeUnaryRPC(control: control)
|
|
|
} throws: { error in
|
|
|
- guard let rootError = error as? RPCError else {
|
|
|
- Issue.record("Should be an RPC error")
|
|
|
- return false
|
|
|
- }
|
|
|
+ let rootError = try #require(error as? RPCError)
|
|
|
#expect(rootError.code == .unavailable)
|
|
|
- #expect(
|
|
|
- rootError.message
|
|
|
- == "The server accepted the TCP connection but closed the connection before completing the HTTP/2 connection preface."
|
|
|
- )
|
|
|
|
|
|
- guard
|
|
|
- let sslError = rootError.cause as? NIOSSLExtraError,
|
|
|
- case .failedToValidateHostname = sslError
|
|
|
- else {
|
|
|
- Issue.record(
|
|
|
- "Should be a NIOSSLExtraError.failedToValidateHostname error, but was: \(String(describing: rootError.cause))"
|
|
|
+ switch clientTransport {
|
|
|
+ case .posix:
|
|
|
+ #expect(
|
|
|
+ rootError.message
|
|
|
+ == "The server accepted the TCP connection but closed the connection before completing the HTTP/2 connection preface."
|
|
|
)
|
|
|
- return false
|
|
|
+ let sslError = try #require(rootError.cause as? NIOSSLExtraError)
|
|
|
+ guard sslError == .failedToValidateHostname else {
|
|
|
+ Issue.record(
|
|
|
+ "Should be a NIOSSLExtraError.failedToValidateHostname error, but was: \(String(describing: rootError.cause))"
|
|
|
+ )
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices:
|
|
|
+ #expect(rootError.message.starts(with: "Could not establish a connection to"))
|
|
|
+ let nwError = try #require(rootError.cause as? NWError)
|
|
|
+ guard case .tls(Security.errSSLBadCert) = nwError else {
|
|
|
+ Issue.record(
|
|
|
+ "Should be a NWError.tls(-9808/errSSLBadCert) error, but was: \(String(describing: rootError.cause))"
|
|
|
+ )
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ #endif
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
@@ -144,51 +154,60 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
|
|
|
@Test(
|
|
|
"Error is surfaced when server fails client verification",
|
|
|
- arguments: [TransportKind.posix],
|
|
|
- [TransportKind.posix]
|
|
|
+ arguments: TransportKind.supported,
|
|
|
+ TransportKind.supported
|
|
|
)
|
|
|
- // Verification should fail because the server does not have trust roots containing the client cert.
|
|
|
+ // Verification should fail because the client does not offer a cert that
|
|
|
+ // the server can use for mutual verification.
|
|
|
func testServerFailsClientValidation(
|
|
|
clientTransport: TransportKind,
|
|
|
serverTransport: TransportKind
|
|
|
) async throws {
|
|
|
let certificateKeyPairs = try SelfSignedCertificateKeyPairs()
|
|
|
- let clientConfig = self.makeMTLSClientConfig(
|
|
|
+ let clientTransportConfig = self.makeDefaultTLSClientConfig(
|
|
|
for: clientTransport,
|
|
|
- certificateKeyPairs: certificateKeyPairs,
|
|
|
- serverHostname: "localhost"
|
|
|
+ certificateKeyPairs: certificateKeyPairs
|
|
|
)
|
|
|
- let serverConfig = self.makeMTLSServerConfig(
|
|
|
+ let serverTransportConfig = self.makeMTLSServerConfig(
|
|
|
for: serverTransport,
|
|
|
certificateKeyPairs: certificateKeyPairs,
|
|
|
- includeClientCertificateInTrustRoots: false
|
|
|
+ includeClientCertificateInTrustRoots: true
|
|
|
)
|
|
|
|
|
|
try await self.withClientAndServer(
|
|
|
- clientConfig: clientConfig,
|
|
|
- serverConfig: serverConfig
|
|
|
+ clientConfig: clientTransportConfig,
|
|
|
+ serverConfig: serverTransportConfig
|
|
|
) { control in
|
|
|
await #expect {
|
|
|
try await self.executeUnaryRPC(control: control)
|
|
|
} throws: { error in
|
|
|
- guard let rootError = error as? RPCError else {
|
|
|
- Issue.record("Should be an RPC error")
|
|
|
- return false
|
|
|
- }
|
|
|
+ let rootError = try #require(error as? RPCError)
|
|
|
#expect(rootError.code == .unavailable)
|
|
|
#expect(
|
|
|
rootError.message
|
|
|
== "The server accepted the TCP connection but closed the connection before completing the HTTP/2 connection preface."
|
|
|
)
|
|
|
|
|
|
- guard
|
|
|
- let sslError = rootError.cause as? NIOSSL.BoringSSLError,
|
|
|
- case .sslError = sslError
|
|
|
- else {
|
|
|
- Issue.record(
|
|
|
- "Should be a NIOSSL.sslError error, but was: \(String(describing: rootError.cause))"
|
|
|
- )
|
|
|
- return false
|
|
|
+ switch clientTransport {
|
|
|
+ case .posix:
|
|
|
+ let sslError = try #require(rootError.cause as? NIOSSL.BoringSSLError)
|
|
|
+ guard case .sslError = sslError else {
|
|
|
+ Issue.record(
|
|
|
+ "Should be a NIOSSL.sslError error, but was: \(String(describing: rootError.cause))"
|
|
|
+ )
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices:
|
|
|
+ let nwError = try #require(rootError.cause as? NWError)
|
|
|
+ guard case .tls(Security.errSSLPeerCertUnknown) = nwError else {
|
|
|
+ Issue.record(
|
|
|
+ "Should be a NWError.tls(-9829/errSSLPeerCertUnknown) error, but was: \(String(describing: rootError.cause))"
|
|
|
+ )
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ #endif
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
@@ -198,8 +217,26 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
|
|
|
// - MARK: Test Utilities
|
|
|
|
|
|
+ enum TLSEnabledTestsError: Error {
|
|
|
+ case failedToImportPKCS12
|
|
|
+ case unexpectedListeningAddress
|
|
|
+ case serverError(cause: any Error)
|
|
|
+ case clientError(cause: any Error)
|
|
|
+ }
|
|
|
+
|
|
|
enum TransportKind: Sendable {
|
|
|
case posix
|
|
|
+ #if canImport(Network)
|
|
|
+ case transportServices
|
|
|
+ #endif
|
|
|
+
|
|
|
+ static var supported: [TransportKind] {
|
|
|
+ #if canImport(Network)
|
|
|
+ return [.posix, .transportServices]
|
|
|
+ #else
|
|
|
+ return [.posix]
|
|
|
+ #endif
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
struct Config<Transport, Security> {
|
|
|
@@ -213,6 +250,14 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
HTTP2ClientTransport.Posix.TransportSecurity
|
|
|
>
|
|
|
case posix(Posix)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ typealias TransportServices = Config<
|
|
|
+ HTTP2ClientTransport.TransportServices.Config,
|
|
|
+ HTTP2ClientTransport.TransportServices.TransportSecurity
|
|
|
+ >
|
|
|
+ case transportServices(TransportServices)
|
|
|
+ #endif
|
|
|
}
|
|
|
|
|
|
enum ServerConfig {
|
|
|
@@ -221,6 +266,14 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
HTTP2ServerTransport.Posix.TransportSecurity
|
|
|
>
|
|
|
case posix(Posix)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ typealias TransportServices = Config<
|
|
|
+ HTTP2ServerTransport.TransportServices.Config,
|
|
|
+ HTTP2ServerTransport.TransportServices.TransportSecurity
|
|
|
+ >
|
|
|
+ case transportServices(TransportServices)
|
|
|
+ #endif
|
|
|
}
|
|
|
|
|
|
private func makeDefaultPlaintextPosixClientConfig() -> ClientConfig.Posix {
|
|
|
@@ -234,9 +287,23 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+ #if canImport(Network)
|
|
|
+ private func makeDefaultPlaintextTSClientConfig() -> ClientConfig.TransportServices {
|
|
|
+ ClientConfig.TransportServices(
|
|
|
+ security: .plaintext,
|
|
|
+ transport: .defaults { config in
|
|
|
+ config.backoff.initial = .milliseconds(100)
|
|
|
+ config.backoff.multiplier = 1
|
|
|
+ config.backoff.jitter = 0
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
private func makeDefaultTLSClientConfig(
|
|
|
for transportSecurity: TransportKind,
|
|
|
- certificateKeyPairs: SelfSignedCertificateKeyPairs
|
|
|
+ certificateKeyPairs: SelfSignedCertificateKeyPairs,
|
|
|
+ authority: String? = "localhost"
|
|
|
) -> ClientConfig {
|
|
|
switch transportSecurity {
|
|
|
case .posix:
|
|
|
@@ -246,11 +313,52 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
.bytes(certificateKeyPairs.server.certificate, format: .der)
|
|
|
])
|
|
|
}
|
|
|
- config.transport.http2.authority = "localhost"
|
|
|
+ config.transport.http2.authority = authority
|
|
|
return .posix(config)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices:
|
|
|
+ var config = self.makeDefaultPlaintextTSClientConfig()
|
|
|
+ config.security = .tls {
|
|
|
+ $0.trustRoots = .certificates([
|
|
|
+ .bytes(certificateKeyPairs.server.certificate, format: .der)
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ config.transport.http2.authority = authority
|
|
|
+ return .transportServices(config)
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #if canImport(Network)
|
|
|
+ private func makeSecIdentityProvider(
|
|
|
+ certificateBytes: [UInt8],
|
|
|
+ privateKeyBytes: [UInt8]
|
|
|
+ ) throws -> SecIdentity {
|
|
|
+ let password = "somepassword"
|
|
|
+ let bundle = NIOSSLPKCS12Bundle(
|
|
|
+ certificateChain: [try NIOSSLCertificate(bytes: certificateBytes, format: .der)],
|
|
|
+ privateKey: try NIOSSLPrivateKey(bytes: privateKeyBytes, format: .der)
|
|
|
+ )
|
|
|
+ let pkcs12Bytes = try bundle.serialize(passphrase: password.utf8)
|
|
|
+ let options = [kSecImportExportPassphrase as String: password]
|
|
|
+ var rawItems: CFArray?
|
|
|
+ let status = SecPKCS12Import(
|
|
|
+ Data(pkcs12Bytes) as CFData,
|
|
|
+ options as CFDictionary,
|
|
|
+ &rawItems
|
|
|
+ )
|
|
|
+ guard status == errSecSuccess else {
|
|
|
+ Issue.record("Failed to import PKCS12 bundle: status \(status).")
|
|
|
+ throw TLSEnabledTestsError.failedToImportPKCS12
|
|
|
+ }
|
|
|
+ let items = rawItems! as! [[String: Any]]
|
|
|
+ let firstItem = items[0]
|
|
|
+ let identity = firstItem[kSecImportItemIdentity as String] as! SecIdentity
|
|
|
+ return identity
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
private func makeMTLSClientConfig(
|
|
|
for transportKind: TransportKind,
|
|
|
certificateKeyPairs: SelfSignedCertificateKeyPairs,
|
|
|
@@ -269,6 +377,23 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
}
|
|
|
config.transport.http2.authority = serverHostname
|
|
|
return .posix(config)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices:
|
|
|
+ var config = self.makeDefaultPlaintextTSClientConfig()
|
|
|
+ config.security = .mTLS {
|
|
|
+ try self.makeSecIdentityProvider(
|
|
|
+ certificateBytes: certificateKeyPairs.client.certificate,
|
|
|
+ privateKeyBytes: certificateKeyPairs.client.key
|
|
|
+ )
|
|
|
+ } configure: {
|
|
|
+ $0.trustRoots = .certificates([
|
|
|
+ .bytes(certificateKeyPairs.server.certificate, format: .der)
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ config.transport.http2.authority = serverHostname
|
|
|
+ return .transportServices(config)
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -276,6 +401,12 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
ServerConfig.Posix(security: .plaintext, transport: .defaults)
|
|
|
}
|
|
|
|
|
|
+ #if canImport(Network)
|
|
|
+ private func makeDefaultPlaintextTSServerConfig() -> ServerConfig.TransportServices {
|
|
|
+ ServerConfig.TransportServices(security: .plaintext, transport: .defaults)
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+
|
|
|
private func makeDefaultTLSServerConfig(
|
|
|
for transportKind: TransportKind,
|
|
|
certificateKeyPairs: SelfSignedCertificateKeyPairs
|
|
|
@@ -288,6 +419,18 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
privateKey: .bytes(certificateKeyPairs.server.key, format: .der)
|
|
|
)
|
|
|
return .posix(config)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices:
|
|
|
+ var config = self.makeDefaultPlaintextTSServerConfig()
|
|
|
+ config.security = .tls {
|
|
|
+ try self.makeSecIdentityProvider(
|
|
|
+ certificateBytes: certificateKeyPairs.server.certificate,
|
|
|
+ privateKeyBytes: certificateKeyPairs.server.key
|
|
|
+ )
|
|
|
+ }
|
|
|
+ return .transportServices(config)
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -310,6 +453,24 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
}
|
|
|
}
|
|
|
return .posix(config)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices:
|
|
|
+ var config = self.makeDefaultPlaintextTSServerConfig()
|
|
|
+ config.security = .mTLS {
|
|
|
+ try self.makeSecIdentityProvider(
|
|
|
+ certificateBytes: certificateKeyPairs.server.certificate,
|
|
|
+ privateKeyBytes: certificateKeyPairs.server.key
|
|
|
+ )
|
|
|
+ } configure: {
|
|
|
+ if includeClientCertificateInTrustRoots {
|
|
|
+ $0.trustRoots = .certificates([
|
|
|
+ .bytes(certificateKeyPairs.client.certificate, format: .der)
|
|
|
+ ])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return .transportServices(config)
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -322,25 +483,33 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
let server = self.makeServer(config: serverConfig)
|
|
|
|
|
|
group.addTask {
|
|
|
- try await server.serve()
|
|
|
+ do {
|
|
|
+ try await server.serve()
|
|
|
+ } catch {
|
|
|
+ throw TLSEnabledTestsError.serverError(cause: error)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
guard let address = try await server.listeningAddress?.ipv4 else {
|
|
|
- Issue.record("Unexpected address to connect to")
|
|
|
- return
|
|
|
+ throw TLSEnabledTestsError.unexpectedListeningAddress
|
|
|
}
|
|
|
+
|
|
|
let target: any ResolvableTarget = .ipv4(host: address.host, port: address.port)
|
|
|
let client = try self.makeClient(config: clientConfig, target: target)
|
|
|
|
|
|
group.addTask {
|
|
|
- try await client.run()
|
|
|
+ do {
|
|
|
+ try await client.run()
|
|
|
+ } catch {
|
|
|
+ throw TLSEnabledTestsError.clientError(cause: error)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
let control = ControlClient(wrapping: client)
|
|
|
try await test(control)
|
|
|
|
|
|
- server.beginGracefulShutdown()
|
|
|
client.beginGracefulShutdown()
|
|
|
+ server.beginGracefulShutdown()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -349,7 +518,7 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
|
|
|
switch config {
|
|
|
case .posix(let config):
|
|
|
- let server = GRPCServer(
|
|
|
+ return GRPCServer(
|
|
|
transport: .http2NIOPosix(
|
|
|
address: .ipv4(host: "127.0.0.1", port: 0),
|
|
|
transportSecurity: config.security,
|
|
|
@@ -358,7 +527,17 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
services: services
|
|
|
)
|
|
|
|
|
|
- return server
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices(let config):
|
|
|
+ return GRPCServer(
|
|
|
+ transport: .http2NIOTS(
|
|
|
+ address: .ipv4(host: "127.0.0.1", port: 0),
|
|
|
+ transportSecurity: config.security,
|
|
|
+ config: config.transport
|
|
|
+ ),
|
|
|
+ services: services
|
|
|
+ )
|
|
|
+ #endif
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -376,6 +555,16 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
config: config.transport,
|
|
|
serviceConfig: ServiceConfig()
|
|
|
)
|
|
|
+
|
|
|
+ #if canImport(Network)
|
|
|
+ case .transportServices(let config):
|
|
|
+ transport = try HTTP2ClientTransport.TransportServices(
|
|
|
+ target: target,
|
|
|
+ transportSecurity: config.security,
|
|
|
+ config: config.transport,
|
|
|
+ serviceConfig: ServiceConfig()
|
|
|
+ )
|
|
|
+ #endif
|
|
|
}
|
|
|
|
|
|
return GRPCClient(transport: transport)
|
|
|
@@ -389,62 +578,3 @@ struct HTTP2TransportTLSEnabledTests {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-struct SelfSignedCertificateKeyPairs {
|
|
|
- struct CertificateKeyPair {
|
|
|
- let certificate: [UInt8]
|
|
|
- let key: [UInt8]
|
|
|
- }
|
|
|
-
|
|
|
- let server: CertificateKeyPair
|
|
|
- let client: CertificateKeyPair
|
|
|
-
|
|
|
- init() throws {
|
|
|
- let server = try Self.makeSelfSignedDERCertificateAndPrivateKey(name: "Server Certificate")
|
|
|
- let client = try Self.makeSelfSignedDERCertificateAndPrivateKey(name: "Client Certificate")
|
|
|
-
|
|
|
- self.server = CertificateKeyPair(certificate: server.cert, key: server.key)
|
|
|
- self.client = CertificateKeyPair(certificate: client.cert, key: client.key)
|
|
|
- }
|
|
|
-
|
|
|
- private static func makeSelfSignedDERCertificateAndPrivateKey(
|
|
|
- name: String
|
|
|
- ) throws -> (cert: [UInt8], key: [UInt8]) {
|
|
|
- let swiftCryptoKey = P256.Signing.PrivateKey()
|
|
|
- let key = Certificate.PrivateKey(swiftCryptoKey)
|
|
|
- let subjectName = try DistinguishedName { CommonName(name) }
|
|
|
- let issuerName = subjectName
|
|
|
- let now = Date()
|
|
|
- let extensions = try Certificate.Extensions {
|
|
|
- Critical(
|
|
|
- BasicConstraints.isCertificateAuthority(maxPathLength: nil)
|
|
|
- )
|
|
|
- Critical(
|
|
|
- KeyUsage(digitalSignature: true, keyCertSign: true)
|
|
|
- )
|
|
|
- Critical(
|
|
|
- try ExtendedKeyUsage([.serverAuth, .clientAuth])
|
|
|
- )
|
|
|
- SubjectAlternativeNames([.dnsName("localhost")])
|
|
|
- }
|
|
|
- let certificate = try Certificate(
|
|
|
- version: .v3,
|
|
|
- serialNumber: Certificate.SerialNumber(),
|
|
|
- publicKey: key.publicKey,
|
|
|
- notValidBefore: now.addingTimeInterval(-60 * 60),
|
|
|
- notValidAfter: now.addingTimeInterval(60 * 60 * 24 * 365),
|
|
|
- issuer: issuerName,
|
|
|
- subject: subjectName,
|
|
|
- signatureAlgorithm: .ecdsaWithSHA256,
|
|
|
- extensions: extensions,
|
|
|
- issuerPrivateKey: key
|
|
|
- )
|
|
|
-
|
|
|
- var serializer = DER.Serializer()
|
|
|
- try serializer.serialize(certificate)
|
|
|
-
|
|
|
- let certBytes = serializer.serializedBytes
|
|
|
- let keyBytes = try key.serializeAsPEM().derBytes
|
|
|
- return (certBytes, keyBytes)
|
|
|
- }
|
|
|
-}
|