Skip to content

Commit

Permalink
Support certificates with invalid RDN attribute values
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.
  • Loading branch information
baarde committed Dec 17, 2024
1 parent 5fd8063 commit 664d0af
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 664d0af

Please sign in to comment.