apple / swift-certificates

An implementation of X.509 for Swift
https://swiftpackageindex.com/apple/swift-certificates/main/documentation/x509
Apache License 2.0
207 stars 44 forks source link

Using `GeneralName.directoryName` results in a corrupt certificate #177

Open andreasley opened 1 month ago

andreasley commented 1 month ago

Commit hash: d93756ba92cde1891de0f433884a7b197bb28d4b

I'm trying to create a Certificate that is signed by a (pre-existing, imported) CA certificate. To make a valid chain, I'm specifying an Authority Key Identifier for the new Certificate by passing the CA certificate's issuer as a DistinguishedName into a GeneralName.directoryName.

The resulting certificate is malformed, which is clearly visible when analyzing it with the following command:

$ openssl x509 -in /path/to/generated/cert.pem -text -noout

Actual output (varies with each generation!):

        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                0...../CA......|ob...@...%.....

Expected output:

        X509v3 extensions:
            X509v3 Authority Key Identifier: 
                DirName:/C=Some Country/O=Some Org/CN=CA

I suspect the problem could originate in swift-asn1 (maybe here?).

Minimal reproducible example:

import Foundation
import X509
import Crypto

let leafPrivateKey = P256.Signing.PrivateKey()

let now = Date()

let issuer = try DistinguishedName {
    CommonName("CA")
    OrganizationName("Some Org")
    CountryName("Some Country")
}

let leafCertificate = try Certificate(
    version: .v3,
    serialNumber: Certificate.SerialNumber(),
    publicKey: Certificate.PublicKey(leafPrivateKey.publicKey),
    notValidBefore: now.addingTimeInterval(-60 * 60 * 24),
    notValidAfter: now.addingTimeInterval(60 * 60 * 24 * 395),
    issuer: .init(),
    subject: .init(),
    signatureAlgorithm: .ecdsaWithSHA512,
    extensions: try Certificate.Extensions {
        AuthorityKeyIdentifier(authorityCertIssuer: [.directoryName(issuer)], authorityCertSerialNumber: Certificate.SerialNumber())
    },
    issuerPrivateKey: Certificate.PrivateKey(leafPrivateKey)
)

if let certFilePemData = try leafCertificate.serializeAsPEM().pemString.data(using: .ascii) {
    let temporaryCertFileURL = URL(string: "/path/to/generated/cert.pem")!
    try certFilePemData.write(to: temporaryCertFileURL)
}

Technical details:

$ swift --version swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4) Target: arm64-apple-macosx14.0

Operating system: macOS 14.5

$ uname -a Darwin Kernel Version 23.5.0: Wed May 1 20:12:58 PDT 2024; root:xnu-10063.121.3~5/RELEASE_ARM64_T6000 arm64

Lukasa commented 1 month ago

Thanks for filing this! You're right that we don't seem to be doing what OpenSSL expects, but I don't think this is a swift-asn1 bug. I think this is an issue in the way we serialize this specific field. I know what we're doing differently, but I'm doing a bit of digging to try to work out why we're doing it differently.

Lukasa commented 1 month ago

Ok so I've got this, the issue here is the implicit tagging of general name. I think I have a fix in flight but it's triggering a bit of a weird Swift behaviour, so I'm going to chat with the Swift team quickly to work out how we could work around it.