Skip to content

Commit

Permalink
Support certificates with invalid RDN attribute values (#214)
Browse files Browse the repository at this point in the history
Some certificate authorities issue certificates with invalid RDN
attribute values. Certificates must use UTF8String instead of
PrintableString to represent strings that contains `*` or `@`, but
mistakes happen.

When a RDN attribute value is a PrintableString or UTF8String with an
invalid content, rather than rejecting the certificate, fall back to
storing the value as an Any.

## Rationale

Certificate authorities using the wrong string type is a common mistake.
For example, DigiCert issued intermediate certificates with invalid
ampersands to Wells Fargo & Company (golang/go#22970).

This PR adds *partial* support for certificates that contain invalid
PrintableString or UTF8String (less likely) values. When failing to
parse a PrintableString or UTF8String, the raw bytes are stored as an
Any (same as IA5String, BMPString, ...).

Though those certificates are now parsed without any error, they aren't
fully supported yet. Trying to convert the value to a String returns
`nil`, so the CN doesn't match. I don't know whether we should add
support for converting invalid PrintableString values (and
IA5String/BMPString/...) to String in the future. In the meantime,
developers who want to read the CN have access the raw Any value.
  • Loading branch information
baarde authored Dec 17, 2024
1 parent 5fd8063 commit 87d0a27
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 18 deletions.
31 changes: 13 additions & 18 deletions Sources/X509/RDNAttribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,16 @@ extension RelativeDistinguishedName.Attribute.Value {
extension RelativeDistinguishedName.Attribute.Value.Storage: DERParseable, DERSerializable {
@inlinable
init(derEncoded node: SwiftASN1.ASN1Node) throws {
switch node.identifier {
case ASN1UTF8String.defaultIdentifier:
self = .utf8(String(try ASN1UTF8String(derEncoded: node)))
case ASN1PrintableString.defaultIdentifier:
self = .printable(String(try ASN1PrintableString(derEncoded: node)))
default:
do {
switch node.identifier {
case ASN1UTF8String.defaultIdentifier:
self = .utf8(String(try ASN1UTF8String(derEncoded: node)))
case ASN1PrintableString.defaultIdentifier:
self = .printable(String(try ASN1PrintableString(derEncoded: node)))
default:
self = .any(ASN1Any(derEncoded: node))
}
} catch {
self = .any(ASN1Any(derEncoded: node))
}
}
Expand All @@ -149,19 +153,10 @@ extension RelativeDistinguishedName.Attribute.Value: CustomStringConvertible {
@inlinable
public var description: String {
let text: String
switch storage {
case .printable(let string), .utf8(let string):
if let string = String(self) {
text = string
case .any(let any):
do {
text = try String(ASN1PrintableString(asn1Any: any))
} catch {
do {
text = try String(ASN1UTF8String(asn1Any: any))
} catch {
text = String(describing: any)
}
}
} else {
text = String(describing: ASN1Any(self))
}

// This is a very slow way to do this, but until we have any evidence that
Expand Down
14 changes: 14 additions & 0 deletions Tests/X509Tests/DistinguishedNameTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -445,4 +445,18 @@ final class DistinguishedNameTests: XCTestCase {
XCTAssertEqual(String(example.value), result)
}
}

func testRDNAttributeValuesCanBeParsedWhenPrintableStringIsInvalid() throws {
// '&' is not allowed in PrintableString.
let value = try ASN1Any(erasing: ASN1UTF8String("Wells Fargo & Company"), withIdentifier: .printableString)

let attribute = try RelativeDistinguishedName.Attribute(derEncoded: [
0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x15, 0x57, 0x65, 0x6c, 0x6c, 0x73, 0x20,
0x46, 0x61, 0x72, 0x67, 0x6f, 0x20, 0x26, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79,
])

XCTAssertEqual(attribute.type, .RDNAttributeType.organizationName)
XCTAssertEqual(attribute.value, RelativeDistinguishedName.Attribute.Value(asn1Any: value))
XCTAssertNil(String(attribute.value))
}
}

0 comments on commit 87d0a27

Please sign in to comment.