diff --git a/Sources/X509/CertificatePrivateKey.swift b/Sources/X509/CertificatePrivateKey.swift index a875653f..33b46d68 100644 --- a/Sources/X509/CertificatePrivateKey.swift +++ b/Sources/X509/CertificatePrivateKey.swift @@ -91,8 +91,14 @@ extension Certificate { } #endif + /// Use the private key to sign the provided bytes with a given signature algorithm. + /// + /// - Parameters: + /// - bytes: The data to create the signature for. + /// - signatureAlgorithm: The signature algorithm to use. + /// - Returns: The signature. @inlinable - internal func sign( + public func sign( bytes: Bytes, signatureAlgorithm: SignatureAlgorithm ) throws -> Signature { diff --git a/Sources/X509/Signature.swift b/Sources/X509/Signature.swift index 9fec295e..1787597c 100644 --- a/Sources/X509/Signature.swift +++ b/Sources/X509/Signature.swift @@ -129,22 +129,31 @@ extension Certificate.Signature { } @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) -extension ASN1BitString { +extension Certificate.Signature { + /// The raw byte representation of the signature. @inlinable - init(_ signature: Certificate.Signature) { - switch signature.backing { + public var rawRepresentation: [UInt8] { + switch self.backing { case .ecdsa(let sig): var serializer = DER.Serializer() try! serializer.serialize(sig) - self = ASN1BitString(bytes: serializer.serializedBytes[...]) - case .rsa(let sig): - self = ASN1BitString(bytes: ArraySlice(sig.rawRepresentation)) - case .ed25519(let sig): - self = ASN1BitString(bytes: ArraySlice(sig)) + return serializer.serializedBytes + case let .ed25519(data): + return .init(data) + case let .rsa(signature): + return .init(signature.rawRepresentation) } } } +@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) +extension ASN1BitString { + @inlinable + init(_ signature: Certificate.Signature) { + self.init(bytes: signature.rawRepresentation[...]) + } +} + @available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *) extension ASN1OctetString { @inlinable diff --git a/Tests/X509Tests/SignatureTests.swift b/Tests/X509Tests/SignatureTests.swift index efc9d0fe..3660dc91 100644 --- a/Tests/X509Tests/SignatureTests.swift +++ b/Tests/X509Tests/SignatureTests.swift @@ -50,6 +50,40 @@ final class SignatureTests: XCTestCase { ) #endif + func testRSASignatureBytes() throws { + let input = Array("Hello World".utf8) + let privateKey = Certificate.PrivateKey(Self.rsaKey) + + let expected = try Self.rsaKey.signature(for: SHA256.hash(data: input), padding: .insecurePKCS1v1_5) + let found = try privateKey.sign(bytes: input, signatureAlgorithm: .sha256WithRSAEncryption) + + XCTAssertEqual(.init(expected.rawRepresentation), found.rawRepresentation) + } + + func testEd25519SignatureBytes() throws { + let input = Array("Hello World".utf8) + + let expected = try Self.ed25519Key.signature(for: input) + let signature = try Certificate.Signature( + signatureAlgorithm: .ed25519, + signatureBytes: .init(bytes: Array(expected)[...]) + ) + + XCTAssertEqual(.init(expected), signature.rawRepresentation) + } + + func testECDSASignatureBytes() throws { + let input = Array("Hello World".utf8) + + let expected = try Self.p384Key.signature(for: SHA256.hash(data: input)) + let signature = try Certificate.Signature( + signatureAlgorithm: .ecdsaWithSHA256, + signatureBytes: .init(bytes: Array(expected.derRepresentation)[...]) + ) + + XCTAssertEqual(.init(expected.derRepresentation), signature.rawRepresentation) + } + func testP384Signature() throws { // This is the P384 signature over LetsEncrypt Intermediate E1. let signatureBytes: [UInt8] = [ @@ -75,6 +109,8 @@ final class SignatureTests: XCTestCase { return } + XCTAssertEqual(signature.rawRepresentation, .init(signatureBytes)) + // Validate that the signature is valid over the TBS certificate bytes. let issuingPublicKeyBytes: [UInt8] = [ 0x04, 0xCD, 0x9B, 0xD5, 0x9F, 0x80, 0x83, 0x0A, 0xEC, 0x09, 0x4A, 0xF3,