From ffdea92aee43909954470c86be25233743e99f25 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 27 Oct 2019 18:27:34 +0100 Subject: [PATCH 01/17] Added commonName access. #1 --- ASN1Decoder/X509Certificate.swift | 6 ++++++ ASN1DecoderTests/ASN1DecoderTests.swift | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index a7a3fde..f31426e 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -34,6 +34,7 @@ public class X509Certificate: CustomStringConvertible { private let OID_ExtendedKeyUsage = "2.5.29.37" private let OID_SubjectAltName = "2.5.29.17" private let OID_IssuerAltName = "2.5.29.18" + private let OID_CommonName = kSecOIDCommonName as String enum X509BlockPosition : Int { case version = 0 @@ -162,6 +163,11 @@ public class X509Certificate: CustomStringConvertible { } return nil } + + public var commonName: String? { + return self.subject(oid: OID_CommonName) + } + /// Gets the notBefore date from the validity period of the certificate. public var notBefore: Date? { diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index 39bdf73..ff352f6 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -34,11 +34,14 @@ class ASN1DecoderTests: XCTestCase { "0836BAA2556864172078584638D85C34") XCTAssertEqual(x509.subjectDistinguishedName, "CN=www.digicert.com, SERIALNUMBER=5299537-0142, OU=SRE, O=\"DigiCert, Inc.\", L=Lehi, ST=Utah, C=US") + + XCTAssertEqual(x509.commonName, "www.digicert.com") } func testDecoding() { var serialNumber = "" var subject = "" + var commonName = "" if let certData = Data(base64Encoded: cert) { do { @@ -48,6 +51,8 @@ class ASN1DecoderTests: XCTestCase { subject = x509.subjectDistinguishedName ?? "" + commonName = x509.commonName ?? "" + } catch { print(error) } @@ -56,6 +61,8 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(serialNumber, "59A2F004") XCTAssertEqual(subject, "CN=John Smith, L=New York, C=US, E=john@mail.com") + + XCTAssertEqual(commonName, "John Smith") } let cert = From 91b66058bba621fa252ef5694578eb82c68ac50e Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 27 Oct 2019 20:33:12 +0100 Subject: [PATCH 02/17] Added convenience getter for issuer and subject names. --- ASN1Decoder/X509Certificate.swift | 56 +++++++++++++++++++++---- ASN1DecoderTests/ASN1DecoderTests.swift | 16 ++++++- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index f31426e..8d36ee5 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -30,12 +30,24 @@ public class X509Certificate: CustomStringConvertible { private static let beginPemBlock = "-----BEGIN CERTIFICATE-----" private static let endPemBlock = "-----END CERTIFICATE-----" - private let OID_KeyUsage = "2.5.29.15" - private let OID_ExtendedKeyUsage = "2.5.29.37" - private let OID_SubjectAltName = "2.5.29.17" - private let OID_IssuerAltName = "2.5.29.18" - private let OID_CommonName = kSecOIDCommonName as String - + private let OID_KeyUsage = "2.5.29.15" + private let OID_ExtendedKeyUsage = "2.5.29.37" + private let OID_SubjectAltName = "2.5.29.17" + private let OID_IssuerAltName = "2.5.29.18" + private let OID_CommonName = kSecOIDCommonName as String + private let OID_DnQualifier = "2.5.4.46" + private let OID_SerialNumber = kSecOIDSerialNumber as String + private let OID_GivenName = kSecOIDGivenName as String + private let OID_Surname = kSecOIDSurname as String + private let OID_OrganizationalUnitName = kSecOIDOrganizationalUnitName as String + private let OID_OrganizationName = kSecOIDOrganizationName as String + private let OID_StreetAddress = kSecOIDStreetAddress as String + private let OID_LocalityName = kSecOIDLocalityName as String + private let OID_StateOrProvinceName = kSecOIDStateProvinceName as String + private let OID_CountryName = kSecOIDCountryName as String + private let OID_eMail = kSecOIDEmailAddress as String + + enum X509BlockPosition : Int { case version = 0 case serialNumber = 1 @@ -164,9 +176,35 @@ public class X509Certificate: CustomStringConvertible { return nil } - public var commonName: String? { - return self.subject(oid: OID_CommonName) - } + public var subjectCommonName: String? {return self.subject(oid: OID_CommonName)} + public var subjectDnQualifier: String? {return self.subject(oid: OID_DnQualifier )} + public var subjectSerialNumber: String? {return self.subject(oid: OID_SerialNumber )} + public var subjectGivenName: String? {return self.subject(oid: OID_GivenName )} + public var subjectSurname: String? {return self.subject(oid: OID_Surname )} + public var subjectOrganizationalUnitName: String? {return self.subject(oid: OID_OrganizationalUnitName )} + public var subjectOrganizationName: String? {return self.subject(oid: OID_OrganizationName )} + public var subjectStreetAddress: String? {return self.subject(oid: OID_StreetAddress )} + public var subjectLocalityName: String? {return self.subject(oid: OID_LocalityName )} + public var subjectStateOrProvinceName: String? {return self.subject(oid: OID_StateOrProvinceName )} + public var subjectCountryName: String? {return self.subject(oid: OID_CountryName )} + public var subjectEMail: String? {return self.subject(oid: OID_eMail )} + + + public var issuerCommonName: String? {return self.issuer(oid: OID_CommonName)} + public var issuerDnQualifier: String? {return self.issuer(oid: OID_DnQualifier )} + public var issuerSerialNumber: String? {return self.issuer(oid: OID_SerialNumber )} + public var issuerGivenName: String? {return self.issuer(oid: OID_GivenName )} + public var issuerSurname: String? {return self.issuer(oid: OID_Surname )} + public var issuerOrganizationalUnitName: String? {return self.issuer(oid: OID_OrganizationalUnitName )} + public var issuerOrganizationName: String? {return self.issuer(oid: OID_OrganizationName )} + public var issuerStreetAddress: String? {return self.issuer(oid: OID_StreetAddress )} + public var issuerLocalityName: String? {return self.issuer(oid: OID_LocalityName )} + public var issuerStateOrProvinceName: String? {return self.issuer(oid: OID_StateOrProvinceName )} + public var issuerCountryName: String? {return self.issuer(oid: OID_CountryName )} + public var issuerEMail: String? {return self.issuer(oid: OID_eMail )} + + + /// Gets the notBefore date from the validity period of the certificate. diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index ff352f6..fd17951 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -35,7 +35,19 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.subjectDistinguishedName, "CN=www.digicert.com, SERIALNUMBER=5299537-0142, OU=SRE, O=\"DigiCert, Inc.\", L=Lehi, ST=Utah, C=US") - XCTAssertEqual(x509.commonName, "www.digicert.com") + XCTAssertEqual(x509.subjectCommonName, "www.digicert.com") + XCTAssertEqual(x509.subjectSerialNumber, "5299537-0142") + XCTAssertEqual(x509.subjectOrganizationalUnitName, "SRE") + XCTAssertEqual(x509.subjectOrganizationName, "DigiCert, Inc.") + XCTAssertEqual(x509.subjectLocalityName, "Lehi") + XCTAssertEqual(x509.subjectStateOrProvinceName, "Utah") + XCTAssertEqual(x509.subjectCountryName, "US") + + XCTAssertEqual(x509.issuerDistinguishedName,"CN=DigiCert SHA2 Extended Validation Server CA, OU=www.digicert.com, O=DigiCert Inc, C=US") + XCTAssertEqual(x509.issuerCommonName, "DigiCert SHA2 Extended Validation Server CA") + XCTAssertEqual(x509.issuerOrganizationalUnitName, "www.digicert.com") + XCTAssertEqual(x509.issuerOrganizationName, "DigiCert Inc") + XCTAssertEqual(x509.issuerCountryName, "US") } func testDecoding() { @@ -51,7 +63,7 @@ class ASN1DecoderTests: XCTestCase { subject = x509.subjectDistinguishedName ?? "" - commonName = x509.commonName ?? "" + commonName = x509.subjectCommonName ?? "" } catch { print(error) From 1e024452455627753a180f1526a2aba4800d5bc5 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 27 Oct 2019 21:04:24 +0100 Subject: [PATCH 03/17] Removed usage of kSecOID* because not available on iOS. --- ASN1Decoder/X509Certificate.swift | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 8d36ee5..1bde43b 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -34,18 +34,20 @@ public class X509Certificate: CustomStringConvertible { private let OID_ExtendedKeyUsage = "2.5.29.37" private let OID_SubjectAltName = "2.5.29.17" private let OID_IssuerAltName = "2.5.29.18" - private let OID_CommonName = kSecOIDCommonName as String + + private let OID_CommonName = "2.5.4.3" + private let OID_Surname = "2.5.4.4" + private let OID_SerialNumber = "2.5.4.5" + private let OID_CountryName = "2.5.4.6" + private let OID_LocalityName = "2.5.4.7" + private let OID_StateOrProvinceName = "2.5.4.8" + private let OID_StreetAddress = "2.5.4.9" + private let OID_OrganizationName = "2.5.4.10" + private let OID_OrganizationalUnitName = "2.5.4.11" + private let OID_GivenName = "2.5.4.42" private let OID_DnQualifier = "2.5.4.46" - private let OID_SerialNumber = kSecOIDSerialNumber as String - private let OID_GivenName = kSecOIDGivenName as String - private let OID_Surname = kSecOIDSurname as String - private let OID_OrganizationalUnitName = kSecOIDOrganizationalUnitName as String - private let OID_OrganizationName = kSecOIDOrganizationName as String - private let OID_StreetAddress = kSecOIDStreetAddress as String - private let OID_LocalityName = kSecOIDLocalityName as String - private let OID_StateOrProvinceName = kSecOIDStateProvinceName as String - private let OID_CountryName = kSecOIDCountryName as String - private let OID_eMail = kSecOIDEmailAddress as String + private let OID_eMail = "1.2.840.113549.1.9.1" + enum X509BlockPosition : Int { From b2f2f0a76eb0c9e386380698600f5946eefd6f47 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 27 Oct 2019 21:52:00 +0100 Subject: [PATCH 04/17] Updated to latest DN constants. --- ASN1Decoder/X509Certificate.swift | 5 +++++ ASN1DecoderTests/ASN1DecoderTests.swift | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 065c568..0bbb066 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -134,6 +134,11 @@ public class X509Certificate: CustomStringConvertible { return nil } + public func issuer(dn: ASN1DistinguishedNames) -> String? { + return issuer(oid: dn.oid) + } + + /// Returns the subject (subject distinguished name) value from the certificate as a String. public var subjectDistinguishedName: String? { if let subjectBlock = block1[X509BlockPosition.subject] { diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index fd17951..bf28af9 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -35,19 +35,19 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.subjectDistinguishedName, "CN=www.digicert.com, SERIALNUMBER=5299537-0142, OU=SRE, O=\"DigiCert, Inc.\", L=Lehi, ST=Utah, C=US") - XCTAssertEqual(x509.subjectCommonName, "www.digicert.com") - XCTAssertEqual(x509.subjectSerialNumber, "5299537-0142") - XCTAssertEqual(x509.subjectOrganizationalUnitName, "SRE") - XCTAssertEqual(x509.subjectOrganizationName, "DigiCert, Inc.") - XCTAssertEqual(x509.subjectLocalityName, "Lehi") - XCTAssertEqual(x509.subjectStateOrProvinceName, "Utah") - XCTAssertEqual(x509.subjectCountryName, "US") + XCTAssertEqual(x509.subject(dn:.commonName), "www.digicert.com") + XCTAssertEqual(x509.subject(dn:.serialNumber), "5299537-0142") + XCTAssertEqual(x509.subject(dn:.organizationalUnitName), "SRE") + XCTAssertEqual(x509.subject(dn:.organizationName), "DigiCert, Inc.") + XCTAssertEqual(x509.subject(dn:.localityName), "Lehi") + XCTAssertEqual(x509.subject(dn:.stateOrProvinceName), "Utah") + XCTAssertEqual(x509.subject(dn:.countryName), "US") XCTAssertEqual(x509.issuerDistinguishedName,"CN=DigiCert SHA2 Extended Validation Server CA, OU=www.digicert.com, O=DigiCert Inc, C=US") - XCTAssertEqual(x509.issuerCommonName, "DigiCert SHA2 Extended Validation Server CA") - XCTAssertEqual(x509.issuerOrganizationalUnitName, "www.digicert.com") - XCTAssertEqual(x509.issuerOrganizationName, "DigiCert Inc") - XCTAssertEqual(x509.issuerCountryName, "US") + XCTAssertEqual(x509.issuer(dn:.commonName), "DigiCert SHA2 Extended Validation Server CA") + XCTAssertEqual(x509.issuer(dn:.organizationalUnitName), "www.digicert.com") + XCTAssertEqual(x509.issuer(dn:.organizationName), "DigiCert Inc") + XCTAssertEqual(x509.issuer(dn:.countryName), "US") } func testDecoding() { @@ -63,7 +63,7 @@ class ASN1DecoderTests: XCTestCase { subject = x509.subjectDistinguishedName ?? "" - commonName = x509.subjectCommonName ?? "" + commonName = x509.subject(dn:.commonName) ?? "" } catch { print(error) From e0027842dd5226dde0338911348063207a1c5b61 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 27 Oct 2019 21:59:29 +0100 Subject: [PATCH 05/17] Merged upstream master. --- ASN1Decoder/X509Certificate.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 7075fed..7801513 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -138,11 +138,6 @@ public class X509Certificate: CustomStringConvertible { return issuer(oid: dn.oid) } - public func issuer(dn: ASN1DistinguishedNames) -> String? { - return issuer(oid: dn.oid) - } - - /// Returns the subject (subject distinguished name) value from the certificate as a String. public var subjectDistinguishedName: String? { if let subjectBlock = block1[X509BlockPosition.subject] { From 82682e4a9f77965264c26fe66a55a9a3bc6cca0d Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 27 Oct 2019 22:07:38 +0100 Subject: [PATCH 06/17] Updated tests for issuer access. --- ASN1DecoderTests/ASN1DecoderTests.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index bf28af9..a35a2d2 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -53,7 +53,9 @@ class ASN1DecoderTests: XCTestCase { func testDecoding() { var serialNumber = "" var subject = "" - var commonName = "" + var subjectCommonName = "" + var issuer = "" + var issuerEMail = "" if let certData = Data(base64Encoded: cert) { do { @@ -63,7 +65,10 @@ class ASN1DecoderTests: XCTestCase { subject = x509.subjectDistinguishedName ?? "" - commonName = x509.subject(dn:.commonName) ?? "" + subjectCommonName = x509.subject(dn:.commonName) ?? "" + + issuer = x509.issuerDistinguishedName ?? "" + issuerEMail = x509.issuer(dn: .email) ?? "" } catch { print(error) @@ -73,8 +78,11 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(serialNumber, "59A2F004") XCTAssertEqual(subject, "CN=John Smith, L=New York, C=US, E=john@mail.com") + XCTAssertEqual(subjectCommonName, "John Smith") + + XCTAssertEqual(issuer, "CN=John Smith, L=New York, C=US, E=john@mail.com") + XCTAssertEqual(issuerEMail, "john@mail.com") - XCTAssertEqual(commonName, "John Smith") } let cert = From 95380525e226216acf64c2123d831ab70bed7114 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Mon, 28 Oct 2019 23:56:21 +0100 Subject: [PATCH 07/17] Added more tests. Especially for the extensions. --- ASN1DecoderTests/ASN1DecoderTests.swift | 55 +++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index a35a2d2..ce1d886 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -30,8 +30,10 @@ class ASN1DecoderTests: XCTestCase { func testDecodingPEM() throws { let x509 = try X509Certificate(data: certPEMData) - XCTAssertEqual(x509.serialNumber?.hexEncodedString(), - "0836BAA2556864172078584638D85C34") + + XCTAssertEqual(x509.version,3) + + XCTAssertEqual(x509.subjectDistinguishedName, "CN=www.digicert.com, SERIALNUMBER=5299537-0142, OU=SRE, O=\"DigiCert, Inc.\", L=Lehi, ST=Utah, C=US") @@ -48,6 +50,51 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.issuer(dn:.organizationalUnitName), "www.digicert.com") XCTAssertEqual(x509.issuer(dn:.organizationName), "DigiCert Inc") XCTAssertEqual(x509.issuer(dn:.countryName), "US") + + XCTAssertEqual(x509.serialNumber?.hexEncodedString(), + "0836BAA2556864172078584638D85C34") + + XCTAssertEqual(x509.notBefore?.description, "2018-06-26 00:00:00 +0000") + XCTAssertEqual(x509.notAfter?.description, "2020-06-30 12:00:00 +0000") + + + + + + XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) + XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) + + XCTAssertEqual(x509.keyUsage, [true, false, true, false, false, false, false, false]) // (2.5.29.15) + + XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) + + + XCTAssertEqual(x509.subjectAlternativeNames,["www.digicert.com", "digicert.com", "content.digicert.com", "www.origin.digicert.com", "login.digicert.com", "api.digicert.com", "ws.digicert.com"]) // (2.5.29.17) + XCTAssertEqual(x509.issuerAlternativeNames,[]) + + + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.19")?.value as? Bool, nil) // BasicConstraints (2.5.29.19) // FIXME How to read the basic constraints Bool and Integer? Wrong ASN.1 code in certificate? + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.31")?.valueAsStrings,[]) // CRLDistributionPoints (2.5.29.31) // FIXME How to read (2.5.29.31) + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.32")?.valueAsStrings,[]) // CertificatePolicies (2.5.29.32) // FIXME + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.35")?.valueAsStrings,[]) // AuthorityKeyIdentifier (2.5.29.35) // FIXME + XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 + XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME + XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.4.1.11129.2.4.2")?.valueAsStrings,[]) // Extended validation certificates (1.3.6.1.4.1.11129.2.4.2) // FIXME + + + + XCTAssertEqual(x509.publicKey?.algName, "rsaEncryption") + XCTAssertEqual(x509.publicKey!.key!.count*8,4096) + XCTAssertEqual(x509.publicKey?.algParams, nil) + XCTAssertEqual(x509.publicKey?.key?.hexEncodedString(), "CE9F85CA393030B7F69869B49C105D503B2563D0E568D4D9A5CA2CD63595B23E0D298B9DE0814A04F7C09E354933FBAB1C118A96358EA5DEA281E7AA49248A8D426A3D36858EF24D86FE34C88C5146A8D59822ADB78B8F87A9A5E2D7F1FF6961606B3935AA4CB200E41003FA79E9B1BD9B93A4FC804CFC16672EA5492C624EC7D8A1806D5D23D0EBEAF6A9FBC41A3D16AEDEDF6C11DD9CC5EE08C7B80B75A606DEFC6C61FDC1C9C29348AB72ADB917D50CB476C4B1CBE182336113C44D6031AEEF468990FD9A19A3C21BE79905A7A9484FA50E3A491DCA225DA563D7219665B19479C247A0583B093FB5EFEE713458C918D7ED3988D62DAF3651861967070D80A0C18D23EB6C0572D029E65F585994DF46E19335FDF699AF2182777F57D018B6A8E389D01237649C8BE99B41CC82F6A06029D05679E1252B73C98CF7DB87E558B3D2A79ECE41E34CB6BE8EE56D07756CA151953E0F847AC0E6D840C6796E2623461B40423320F0455011F67311DAF45863B92511CB1F2A2DF2D12B5CCF43885E5C09BCDF7237AEA229364875BEBDBB8F6A03221D333DFB796BD2844EF995B070CEDF26F9F525F4763C32C0688DD052FECE2E1487DF651F42C93ED480AAD399B61F04B1880BE20D19790DEEBA30464376FBB4DEC5004131EF5A7C3432BEC981B8ED9F40DE50A2D8C2C45683EB29AA81532475866DBF5121BFB79717AFEE722A39") + + + XCTAssertEqual(x509.sigAlgName, "sha256WithRSAEncryption") + XCTAssertEqual(x509.sigAlgParams?.hexEncodedString(), nil) + XCTAssertEqual(x509.signature?.hexEncodedString(separation: ":"), "8F:71:72:DE:D4:C8:C6:26:DC:1F:8A:1B:88:D5:2E:77:19:DA:24:14:07:25:F7:8A:2E:A1:6C:56:77:B0:12:7E:CB:9F:53:2C:6C:16:BA:31:0E:13:70:C5:DF:26:40:E1:FB:57:77:A1:65:38:A8:B7:A3:FE:C4:C6:4E:AD:8C:60:27:1E:42:5D:B7:0B:B7:4E:D1:64:74:F4:C3:F3:DF:D3:9D:A0:AB:B6:CF:19:B1:EC:AE:3B:65:5E:AD:4C:0E:7F:1C:F0:3F:85:9E:FD:AA:4A:01:38:7F:FF:70:43:58:0C:53:82:0A:A2:36:8E:E1:81:FD:15:8A:1A:70:0F:29:B9:75:25:2B:5A:41:0A:E0:8A:D2:32:72:93:20:2D:0F:DC:F8:A1:30:FF:64:B0:50:3A:64:C9:E1:5C:09:E6:B1:CD:09:F7:48:F1:A9:11:F4:E6:18:CB:1F:46:09:B7:96:62:FE:49:09:C2:32:CC:FC:AF:65:EE:9C:78:80:84:9D:11:A5:89:4F:C4:CE:BC:B2:5A:1A:B8:57:1F:F3:45:E0:60:A1:7E:B1:39:67:D6:D5:90:28:B5:AD:1E:B7:3A:3D:A5:25:A3:39:DA:EB:8F:52:3B:AB:46:C0:84:BD:5E:52:E5:C4:F0:54:A6:E8:CF:19:A2:05:BF:65:89:0E:1C:4D:AE") + XCTAssertEqual(x509.signature?.count,256) + + } func testDecoding() { @@ -162,6 +209,8 @@ j1I7q0bAhL1eUuXE8FSm6M8ZogW/ZYkOHE2u extension Data { func hexEncodedString(separation: String = "") -> String { - return reduce("") {$0 + String(format: "%02X\(separation)", $1)} + var hexString = reduce("") {$0 + String(format: "%02X\(separation)", $1)} + if separation != "" {hexString.removeLast()} + return hexString } } From ac5f2979d490a8f78864c5b3d907295364ffaa66 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Fri, 1 Nov 2019 11:02:50 +0100 Subject: [PATCH 08/17] Added access to encoded octets to calculate the fingerprint and signature. --- ASN1Decoder/X509Certificate.swift | 10 ++++++++++ ASN1DecoderTests/ASN1DecoderTests.swift | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 7801513..b0b8af8 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -83,6 +83,16 @@ public class X509Certificate: CustomStringConvertible { return asn1.reduce("") { $0 + "\($1.description)\n" } } + public var encodedTBSCertificate:Data? { + return self.block1.rawValue + } + + public var encodedCertificate:Data? { + var length = UInt16(self.asn1[0].rawValue!.count).bigEndian + return Data([UInt8(0x30),UInt8(0x82)]) + Data(bytes: &length, count: 2) + (self.asn1[0].rawValue ?? Data()) + } + + /// Checks that the given date is within the certificate's validity period. public func checkValidity(_ date: Date = Date()) -> Bool { if let notBefore = notBefore, let notAfter = notAfter { diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index ce1d886..b998138 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -24,6 +24,8 @@ import XCTest +import CryptoKit + @testable import ASN1Decoder class ASN1DecoderTests: XCTestCase { @@ -88,6 +90,16 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.publicKey?.algParams, nil) XCTAssertEqual(x509.publicKey?.key?.hexEncodedString(), "CE9F85CA393030B7F69869B49C105D503B2563D0E568D4D9A5CA2CD63595B23E0D298B9DE0814A04F7C09E354933FBAB1C118A96358EA5DEA281E7AA49248A8D426A3D36858EF24D86FE34C88C5146A8D59822ADB78B8F87A9A5E2D7F1FF6961606B3935AA4CB200E41003FA79E9B1BD9B93A4FC804CFC16672EA5492C624EC7D8A1806D5D23D0EBEAF6A9FBC41A3D16AEDEDF6C11DD9CC5EE08C7B80B75A606DEFC6C61FDC1C9C29348AB72ADB917D50CB476C4B1CBE182336113C44D6031AEEF468990FD9A19A3C21BE79905A7A9484FA50E3A491DCA225DA563D7219665B19479C247A0583B093FB5EFEE713458C918D7ED3988D62DAF3651861967070D80A0C18D23EB6C0572D029E65F585994DF46E19335FDF699AF2182777F57D018B6A8E389D01237649C8BE99B41CC82F6A06029D05679E1252B73C98CF7DB87E558B3D2A79ECE41E34CB6BE8EE56D07756CA151953E0F847AC0E6D840C6796E2623461B40423320F0455011F67311DAF45863B92511CB1F2A2DF2D12B5CCF43885E5C09BCDF7237AEA229364875BEBDBB8F6A03221D333DFB796BD2844EF995B070CEDF26F9F525F4763C32C0688DD052FECE2E1487DF651F42C93ED480AAD399B61F04B1880BE20D19790DEEBA30464376FBB4DEC5004131EF5A7C3432BEC981B8ED9F40DE50A2D8C2C45683EB29AA81532475866DBF5121BFB79717AFEE722A39") + if #available(OSX 10.15,iOS 13.0, *) { + + XCTAssertEqual(SHA256.hash(data: x509.encodedTBSCertificate!).hexEncodedString(separation: ":"),"8D:41:ED:64:43:1F:22:2E:FD:A7:0B:56:04:D2:7C:7D:E3:09:42:85:6D:3E:99:7F:C2:B8:F2:CA:D7:0C:85:07") + XCTAssertEqual(SHA256.hash(data: x509.encodedCertificate!).hexEncodedString(separation: ":"),"C7:32:93:B5:94:A3:52:18:4A:D8:7E:5A:95:FB:39:B7:3B:0F:F6:80:02:A4:AB:EA:5E:74:F3:50:24:55:DB:D3" ) + + } else { + // Fallback on earlier versions + } + + XCTAssertEqual(x509.sigAlgName, "sha256WithRSAEncryption") XCTAssertEqual(x509.sigAlgParams?.hexEncodedString(), nil) @@ -214,3 +226,13 @@ extension Data { return hexString } } + + +@available(OSX 10.15,iOS 13.0, *) +extension SHA256Digest { + func hexEncodedString(separation: String = "") -> String { + var hexString = reduce("") {$0 + String(format: "%02X\(separation)", $1)} + if separation != "" {hexString.removeLast()} + return hexString + } +} From 0cba81a2b286a1bd97bee4982ae2129b9a769fa2 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 3 Nov 2019 00:12:31 +0100 Subject: [PATCH 09/17] Added signature checking with public key from signing certificate. --- ASN1Decoder/X509Certificate.swift | 3 +- ASN1Decoder/X509PublicKey.swift | 66 ++++++++++++- ASN1DecoderTests/ASN1DecoderTests.swift | 120 ++++++++++++++++++++---- 3 files changed, 169 insertions(+), 20 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index b0b8af8..4cef80c 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -84,7 +84,8 @@ public class X509Certificate: CustomStringConvertible { } public var encodedTBSCertificate:Data? { - return self.block1.rawValue + var length = UInt16(self.block1.rawValue!.count).bigEndian + return Data([UInt8(0x30),UInt8(0x82)]) + Data(bytes: &length, count: 2) + (self.block1.rawValue ?? Data()) } public var encodedCertificate:Data? { diff --git a/ASN1Decoder/X509PublicKey.swift b/ASN1Decoder/X509PublicKey.swift index fac0c0c..c9df175 100644 --- a/ASN1Decoder/X509PublicKey.swift +++ b/ASN1Decoder/X509PublicKey.swift @@ -28,12 +28,42 @@ public class X509PublicKey { private let OID_ECPublicKey = "1.2.840.10045.2.1" private let OID_RSAEncryption = "1.2.840.113549.1.1.1" + private static let beginPemBlock = "-----BEGIN PUBLIC KEY-----" + private static let endPemBlock = "-----END PUBLIC KEY-----" + + var asn1:[ASN1Object] var pkBlock: ASN1Object! - init(pkBlock: ASN1Object) { + init(pkBlock: ASN1Object) { + self.asn1 = [pkBlock] self.pkBlock = pkBlock } + + public convenience init(data: Data) throws { + if String(data: data, encoding: .utf8)?.contains(X509PublicKey.beginPemBlock) ?? false { + try self.init(pem: data) + } else { + try self.init(der: data) + } + } + public init(der: Data) throws { + asn1 = try ASN1DERDecoder.decode(data: der) + guard asn1.count > 0, + let pkBlock = asn1.first?.sub(1) else { + throw ASN1Error.parseError + } + self.pkBlock = pkBlock + } + + public convenience init(pem: Data) throws { + guard let derData = X509PublicKey.decodeToDER(pem: pem) else { + throw ASN1Error.parseError + } + + try self.init(der: derData) + } + public var algOid: String? { return pkBlock.sub(0)?.sub(0)?.value as? String } @@ -70,4 +100,38 @@ public class X509PublicKey { return nil } } + + func encodedKey() -> Data? { + let keyData = self.asn1.first?.rawValue + var length = UInt16(keyData!.count).bigEndian + return Data([UInt8(0x30),UInt8(0x82)]) + Data(bytes: &length, count: 2) + (keyData!) + } + + // read possibile PEM encoding + private static func decodeToDER(pem pemData: Data) -> Data? { + if + let pem = String(data: pemData, encoding: .ascii), + pem.contains(beginPemBlock) { + + let lines = pem.components(separatedBy: .newlines) + var base64buffer = "" + var certLine = false + for line in lines { + if line == endPemBlock { + certLine = false + } + if certLine { + base64buffer.append(line) + } + if line == beginPemBlock { + certLine = true + } + } + if let derDataDecoded = Data(base64Encoded: base64buffer) { + return derDataDecoded + } + } + + return nil + } } diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index b998138..e92db33 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -26,10 +26,13 @@ import XCTest import CryptoKit + + @testable import ASN1Decoder class ASN1DecoderTests: XCTestCase { - + + @available(OSX 10.12, *) func testDecodingPEM() throws { let x509 = try X509Certificate(data: certPEMData) @@ -54,23 +57,23 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.issuer(dn:.countryName), "US") XCTAssertEqual(x509.serialNumber?.hexEncodedString(), - "0836BAA2556864172078584638D85C34") + "0836BAA2556864172078584638D85C34") XCTAssertEqual(x509.notBefore?.description, "2018-06-26 00:00:00 +0000") XCTAssertEqual(x509.notAfter?.description, "2020-06-30 12:00:00 +0000") - - + + XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) XCTAssertEqual(x509.keyUsage, [true, false, true, false, false, false, false, false]) // (2.5.29.15) - + XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) - - + + XCTAssertEqual(x509.subjectAlternativeNames,["www.digicert.com", "digicert.com", "content.digicert.com", "www.origin.digicert.com", "login.digicert.com", "api.digicert.com", "ws.digicert.com"]) // (2.5.29.17) XCTAssertEqual(x509.issuerAlternativeNames,[]) @@ -83,7 +86,7 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.4.1.11129.2.4.2")?.valueAsStrings,[]) // Extended validation certificates (1.3.6.1.4.1.11129.2.4.2) // FIXME - + XCTAssertEqual(x509.publicKey?.algName, "rsaEncryption") XCTAssertEqual(x509.publicKey!.key!.count*8,4096) @@ -92,23 +95,52 @@ class ASN1DecoderTests: XCTestCase { if #available(OSX 10.15,iOS 13.0, *) { - XCTAssertEqual(SHA256.hash(data: x509.encodedTBSCertificate!).hexEncodedString(separation: ":"),"8D:41:ED:64:43:1F:22:2E:FD:A7:0B:56:04:D2:7C:7D:E3:09:42:85:6D:3E:99:7F:C2:B8:F2:CA:D7:0C:85:07") + XCTAssertEqual(SHA256.hash(data: x509.encodedTBSCertificate!).hexEncodedString(separation: ":"),"83:BF:80:95:73:69:D2:77:CB:58:93:64:BC:40:C5:AD:91:B9:73:4E:AD:B7:BC:F6:96:2A:48:EF:7F:F9:02:1E") XCTAssertEqual(SHA256.hash(data: x509.encodedCertificate!).hexEncodedString(separation: ":"),"C7:32:93:B5:94:A3:52:18:4A:D8:7E:5A:95:FB:39:B7:3B:0F:F6:80:02:A4:AB:EA:5E:74:F3:50:24:55:DB:D3" ) - - } else { - // Fallback on earlier versions - } + } else { + // Fallback on earlier versions - + } + + XCTAssertEqual(x509.sigAlgName, "sha256WithRSAEncryption") - XCTAssertEqual(x509.sigAlgParams?.hexEncodedString(), nil) - XCTAssertEqual(x509.signature?.hexEncodedString(separation: ":"), "8F:71:72:DE:D4:C8:C6:26:DC:1F:8A:1B:88:D5:2E:77:19:DA:24:14:07:25:F7:8A:2E:A1:6C:56:77:B0:12:7E:CB:9F:53:2C:6C:16:BA:31:0E:13:70:C5:DF:26:40:E1:FB:57:77:A1:65:38:A8:B7:A3:FE:C4:C6:4E:AD:8C:60:27:1E:42:5D:B7:0B:B7:4E:D1:64:74:F4:C3:F3:DF:D3:9D:A0:AB:B6:CF:19:B1:EC:AE:3B:65:5E:AD:4C:0E:7F:1C:F0:3F:85:9E:FD:AA:4A:01:38:7F:FF:70:43:58:0C:53:82:0A:A2:36:8E:E1:81:FD:15:8A:1A:70:0F:29:B9:75:25:2B:5A:41:0A:E0:8A:D2:32:72:93:20:2D:0F:DC:F8:A1:30:FF:64:B0:50:3A:64:C9:E1:5C:09:E6:B1:CD:09:F7:48:F1:A9:11:F4:E6:18:CB:1F:46:09:B7:96:62:FE:49:09:C2:32:CC:FC:AF:65:EE:9C:78:80:84:9D:11:A5:89:4F:C4:CE:BC:B2:5A:1A:B8:57:1F:F3:45:E0:60:A1:7E:B1:39:67:D6:D5:90:28:B5:AD:1E:B7:3A:3D:A5:25:A3:39:DA:EB:8F:52:3B:AB:46:C0:84:BD:5E:52:E5:C4:F0:54:A6:E8:CF:19:A2:05:BF:65:89:0E:1C:4D:AE") - XCTAssertEqual(x509.signature?.count,256) + XCTAssertEqual(x509.sigAlgParams?.hexEncodedString(), nil) + XCTAssertEqual(x509.signature?.hexEncodedString(separation: ":"), "8F:71:72:DE:D4:C8:C6:26:DC:1F:8A:1B:88:D5:2E:77:19:DA:24:14:07:25:F7:8A:2E:A1:6C:56:77:B0:12:7E:CB:9F:53:2C:6C:16:BA:31:0E:13:70:C5:DF:26:40:E1:FB:57:77:A1:65:38:A8:B7:A3:FE:C4:C6:4E:AD:8C:60:27:1E:42:5D:B7:0B:B7:4E:D1:64:74:F4:C3:F3:DF:D3:9D:A0:AB:B6:CF:19:B1:EC:AE:3B:65:5E:AD:4C:0E:7F:1C:F0:3F:85:9E:FD:AA:4A:01:38:7F:FF:70:43:58:0C:53:82:0A:A2:36:8E:E1:81:FD:15:8A:1A:70:0F:29:B9:75:25:2B:5A:41:0A:E0:8A:D2:32:72:93:20:2D:0F:DC:F8:A1:30:FF:64:B0:50:3A:64:C9:E1:5C:09:E6:B1:CD:09:F7:48:F1:A9:11:F4:E6:18:CB:1F:46:09:B7:96:62:FE:49:09:C2:32:CC:FC:AF:65:EE:9C:78:80:84:9D:11:A5:89:4F:C4:CE:BC:B2:5A:1A:B8:57:1F:F3:45:E0:60:A1:7E:B1:39:67:D6:D5:90:28:B5:AD:1E:B7:3A:3D:A5:25:A3:39:DA:EB:8F:52:3B:AB:46:C0:84:BD:5E:52:E5:C4:F0:54:A6:E8:CF:19:A2:05:BF:65:89:0E:1C:4D:AE") + XCTAssertEqual(x509.signature?.count,256) } + + + + @available(OSX 10.12,iOS 10.0, *) + func testSignature() throws { + + let publicKeyCA = try X509PublicKey(data: publicKeyCaPEMData) + let x509 = try X509Certificate(data: certPEMData) + + let encodedKey = publicKeyCA.encodedKey() + + + // creating a SecureKey + var attributes: CFDictionary { + return [kSecAttrKeyType : kSecAttrKeyTypeRSA, + kSecAttrKeyClass : kSecAttrKeyClassPublic, + kSecAttrKeySizeInBits : 2048] as CFDictionary + } + + var error: Unmanaged? = nil + guard let secKey = SecKeyCreateWithData(encodedKey! as CFData, attributes, &error) else { + print(error.debugDescription) + throw error as! Error + } + + XCTAssertTrue(SecKeyVerifySignature(secKey, .rsaSignatureMessagePKCS1v15SHA256, x509.encodedTBSCertificate! as CFData, x509.signature! as CFData, &error)) + } + + func testDecoding() { var serialNumber = "" var subject = "" @@ -163,7 +195,7 @@ class ASN1DecoderTests: XCTestCase { "OeAp80GDRAHpjB3qYhzhebiRiM+Bbqva6f4bxNmDNQtL0jt0a8KeyQrFNdAhgjYk" + "AKTucThCu1laJKGKABK90dMoLtbJFxfRhjzmjX9TJGYJgCnRNDDnXpVUOspv2YeH" + "vC9gOdRhaA==" - + let certPEM = """ -----BEGIN CERTIFICATE----- MIIItzCCB5+gAwIBAgIQCDa6olVoZBcgeFhGONhcNDANBgkqhkiG9w0BAQsFADB1 @@ -216,6 +248,58 @@ j1I7q0bAhL1eUuXE8FSm6M8ZogW/ZYkOHE2u -----END CERTIFICATE----- """ var certPEMData: Data { return certPEM.data(using: .utf8)! } + + + let publicKeyCaPEM = """ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA11OkBFH4maYWSEtnJ6qT +SdA57QywsACH8WcohoWMjmPavLFAOOLT9eylBRi4PT7FmRcy7BiM+vEMpmQhhcsH +EDSwUogrH2ib0rGPErCz0ueIHx/vOHdUU1+AeT8uGqqoHksrDau3Y7k1t30UvFlL +31FK0qHiDOKQgodqrurXZNaYVej9rxpQbFS8EfL9SvKdu38O9NW+jhaJElXYwHE0 +7vbcLezEhyWGjdgh5LBNDIncOSYX3fbXlIXYBCFwnW9v/1y6GeFFy1ZXKH4cDUFX +qre4J7ux5Poq7yEjdRqtLZuGNYycd7VzrdiULeTzDJ3uwU5ifhfAcZ4s3vH5ECgZ +MwIDAQAB +-----END PUBLIC KEY----- +""" + + var publicKeyCaPEMData: Data { return publicKeyCaPEM.data(using: .utf8)! } + + + let CaPEM = """ +-----BEGIN CERTIFICATE----- +MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW +YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY +uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/ +LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy +/Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh +cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k +8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB +Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp +Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy +dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2 +MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j +b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW +gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh +hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg +4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa +2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs +1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1 +oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn +8TUoE6smftX3eg== +-----END CERTIFICATE----- +""" + + var CaPEMData: Data { return CaPEM.data(using: .utf8)! } + + + } From 87743f85a7fce8629701e9dc427563ed94fbc5a5 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Mon, 4 Nov 2019 00:16:51 +0100 Subject: [PATCH 10/17] Added CRL Distribution List. --- ASN1Decoder/ASN1Object.swift | 16 +++++++ ASN1Decoder/X509Certificate.swift | 59 +++++++++++++++++++++++++ ASN1Decoder/X509Extension.swift | 33 +++++++++++--- ASN1DecoderTests/ASN1DecoderTests.swift | 46 +++++++++++-------- 4 files changed, 131 insertions(+), 23 deletions(-) diff --git a/ASN1Decoder/ASN1Object.swift b/ASN1Decoder/ASN1Object.swift index e429f65..f5f4324 100644 --- a/ASN1Decoder/ASN1Object.swift +++ b/ASN1Decoder/ASN1Object.swift @@ -146,3 +146,19 @@ public class ASN1Object : CustomStringConvertible { "2.5.4.9" : "streetAddress" ] } + +public class ASN1GeneralNames { + var otherName:String? + var rfc822Name:String? + var URI:String? + + init(asn1Object: ASN1Object) { + + switch asn1Object.identifier?.tagNumber().rawValue { + case 6: + self.URI = asn1Object.value as? String + default: break + + } + } +} diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 4cef80c..3258dbe 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -212,6 +212,9 @@ public class X509Certificate: CustomStringConvertible { return nil } + + + /** Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15). ``` @@ -291,6 +294,30 @@ public class X509Certificate: CustomStringConvertible { .map(X509Extension.init) } + + public var crlDistributionPoints: [X509CrlDistributionPoint] { + var result: [X509CrlDistributionPoint] = [] + + guard let crlDistPointsObject = extensionObject(oid: "2.5.29.31") else { + return result + } + + + // instance of class + guard ((crlDistPointsObject.block.sub?.last?.sub?.last?.sub?.last)?.subCount())! > 0 else { + return result + } + + for crlDistPointObject in ((crlDistPointsObject.block.sub?.last?.sub?.last?.sub!)!) { + + result.append(X509CrlDistributionPoint(asn1Object: crlDistPointObject)) + } + + return result + } + + + // Format subject/issuer information in RFC1779 private func blockDistinguishedName(block: ASN1Object) -> String { var result = "" @@ -370,3 +397,35 @@ extension ASN1Object { return sub[index.rawValue] } } + + + + + +public class X509CrlDistributionPoint { + + var fullName:ASN1GeneralNames? + var nameRelativeToCRLIssuer:String? + var reasons:[Bool]? + var crlIssuer:ASN1GeneralNames? + + init(asn1Object: ASN1Object) { + + self.fullName = ASN1GeneralNames(asn1Object: (asn1Object.sub?.last?.sub?.last?.sub?.last)!) + + } + + + + // if let oidBlock = block1.findOid(OID_KeyUsage) { + // let data = oidBlock.parent?.sub?.last?.sub(0)?.value as? Data + // let bits: UInt8 = data?.first ?? 0 + // for i in 0...7 { + // let value = bits & UInt8(1 << i) != 0 + // result.insert(value, at: 0) + // } + // } + +} + + diff --git a/ASN1Decoder/X509Extension.swift b/ASN1Decoder/X509Extension.swift index 7329a5c..f3de114 100644 --- a/ASN1Decoder/X509Extension.swift +++ b/ASN1Decoder/X509Extension.swift @@ -56,14 +56,37 @@ public class X509Extension { var valueAsBlock: ASN1Object? { return block.sub?.last } - + var valueAsStrings: [String] { - var result: [String] = [] - for item in block.sub?.last?.sub?.last?.sub ?? [] { - if let name = item.value as? String { - result.append(name) + var result: [String] = [] + for item in block.sub?.last?.sub?.last?.sub ?? [] { + if let name = item.value as? String { + result.append(name) + } + } + return result + } + + var values: [Any] { + return self.findLeafs(asn1object: self.valueAsBlock) + } + + public func findLeafs(asn1object:ASN1Object?) -> [Any] { + + var result:[Any] = [] + for child in asn1object?.sub ?? [] { + if let value = child.value { + result.append(value) + } + else { + result = result+(findLeafs(asn1object: child)) } } return result } + } + + + + diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index e92db33..02c2b7c 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -66,28 +66,11 @@ class ASN1DecoderTests: XCTestCase { - XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) - XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) - - XCTAssertEqual(x509.keyUsage, [true, false, true, false, false, false, false, false]) // (2.5.29.15) - - XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) - + XCTAssertEqual(x509.subjectAlternativeNames,["www.digicert.com", "digicert.com", "content.digicert.com", "www.origin.digicert.com", "login.digicert.com", "api.digicert.com", "ws.digicert.com"]) // (2.5.29.17) XCTAssertEqual(x509.issuerAlternativeNames,[]) - - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.19")?.value as? Bool, nil) // BasicConstraints (2.5.29.19) // FIXME How to read the basic constraints Bool and Integer? Wrong ASN.1 code in certificate? - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.31")?.valueAsStrings,[]) // CRLDistributionPoints (2.5.29.31) // FIXME How to read (2.5.29.31) - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.32")?.valueAsStrings,[]) // CertificatePolicies (2.5.29.32) // FIXME - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.35")?.valueAsStrings,[]) // AuthorityKeyIdentifier (2.5.29.35) // FIXME - XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 - XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME - XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.4.1.11129.2.4.2")?.valueAsStrings,[]) // Extended validation certificates (1.3.6.1.4.1.11129.2.4.2) // FIXME - - - XCTAssertEqual(x509.publicKey?.algName, "rsaEncryption") XCTAssertEqual(x509.publicKey!.key!.count*8,4096) XCTAssertEqual(x509.publicKey?.algParams, nil) @@ -140,6 +123,33 @@ class ASN1DecoderTests: XCTestCase { } + func testExtensions() throws { + + let x509 = try X509Certificate(data: certPEMData) + + + + + XCTAssertEqual(x509.crlDistributionPoints[0].fullName?.URI,"http://crl3.digicert.com/sha2-ev-server-g2.crl") + XCTAssertEqual(x509.crlDistributionPoints[1].fullName?.URI,"http://crl4.digicert.com/sha2-ev-server-g2.crl") + + XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) + XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) + + XCTAssertEqual(x509.keyUsage, [true, false, true, false, false, false, false, false]) // (2.5.29.15) + + XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) + + + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.19")?.value as? Bool, nil) // BasicConstraints (2.5.29.19) // FIXME How to read the basic constraints Bool and Integer? Wrong ASN.1 code in certificate? + + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.32")?.valueAsStrings,[]) // CertificatePolicies (2.5.29.32) // FIXME + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.35")?.valueAsStrings,[]) // AuthorityKeyIdentifier (2.5.29.35) // FIXME + XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 + XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME + XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.4.1.11129.2.4.2")?.valueAsStrings,[]) // Extended validation certificates (1.3.6.1.4.1.11129.2.4.2) // FIXME + + } func testDecoding() { var serialNumber = "" From 9a9f6f71f63440d96bc2dbc966fa9d89535372ef Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Mon, 4 Nov 2019 20:15:29 +0100 Subject: [PATCH 11/17] Added BasicConstraints --- ASN1Decoder/X509Certificate.swift | 36 ++++++++++++++++++++++--- ASN1DecoderTests/ASN1DecoderTests.swift | 6 ++--- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 3258dbe..3d7ea5c 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -295,8 +295,12 @@ public class X509Certificate: CustomStringConvertible { } - public var crlDistributionPoints: [X509CrlDistributionPoint] { - var result: [X509CrlDistributionPoint] = [] + public var basicConstraints:X509ExtBasicContraints { + return X509ExtBasicContraints(asn1Object:(extensionObject(oid: "2.5.29.19")?.block)!) + } + + public var crlDistributionPoints: [X509ExtCrlDistributionPoint] { + var result: [X509ExtCrlDistributionPoint] = [] guard let crlDistPointsObject = extensionObject(oid: "2.5.29.31") else { return result @@ -310,7 +314,7 @@ public class X509Certificate: CustomStringConvertible { for crlDistPointObject in ((crlDistPointsObject.block.sub?.last?.sub?.last?.sub!)!) { - result.append(X509CrlDistributionPoint(asn1Object: crlDistPointObject)) + result.append(X509ExtCrlDistributionPoint(asn1Object: crlDistPointObject)) } return result @@ -402,7 +406,7 @@ extension ASN1Object { -public class X509CrlDistributionPoint { +public class X509ExtCrlDistributionPoint { var fullName:ASN1GeneralNames? var nameRelativeToCRLIssuer:String? @@ -429,3 +433,27 @@ public class X509CrlDistributionPoint { } +public class X509ExtBasicContraints { + + var isCA:Bool? = false + var pathLengthConstraint:Int? = nil + + + + init(asn1Object: ASN1Object) { + + + if let cAValue = asn1Object.sub?.last?.sub?.first?.value { + self.isCA = (cAValue as! Bool) + } + + + if let pathValue = asn1Object.sub?[2].value { + self.pathLengthConstraint = (pathValue as! Int) + } + } + + +} + + diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index 02c2b7c..35e5e47 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -133,6 +133,9 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.crlDistributionPoints[0].fullName?.URI,"http://crl3.digicert.com/sha2-ev-server-g2.crl") XCTAssertEqual(x509.crlDistributionPoints[1].fullName?.URI,"http://crl4.digicert.com/sha2-ev-server-g2.crl") + XCTAssertEqual(x509.basicConstraints.isCA, false ) + XCTAssertEqual(x509.basicConstraints.pathLengthConstraint, nil ) + XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) @@ -140,9 +143,6 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) - - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.19")?.value as? Bool, nil) // BasicConstraints (2.5.29.19) // FIXME How to read the basic constraints Bool and Integer? Wrong ASN.1 code in certificate? - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.32")?.valueAsStrings,[]) // CertificatePolicies (2.5.29.32) // FIXME XCTAssertEqual(x509.extensionObject(oid: "2.5.29.35")?.valueAsStrings,[]) // AuthorityKeyIdentifier (2.5.29.35) // FIXME XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 From ca7ab74fc27f938b68e186d1e09e61336e48c914 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Mon, 4 Nov 2019 23:47:03 +0100 Subject: [PATCH 12/17] Added CertificatePolicies extension --- ASN1Decoder/X509Certificate.swift | 58 +++++++++++++++++++++++++ ASN1DecoderTests/ASN1DecoderTests.swift | 13 ++++-- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index 3d7ea5c..e8728c1 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -322,6 +322,28 @@ public class X509Certificate: CustomStringConvertible { + public var certificatePolicies: [X509ExtCertficatePolicy] { + var result: [X509ExtCertficatePolicy] = [] + + guard let certficatePoliciesObject = extensionObject(oid: "2.5.29.32") else { + return result + } + + + // instance of class + guard ((certficatePoliciesObject.block.sub?.last?.sub?.last)?.subCount())! > 0 else { + return result + } + + for certficatePolicyObject in ((certficatePoliciesObject.block.sub?.last?.sub?.last?.sub!)!) { + + result.append(X509ExtCertficatePolicy(asn1Object: certficatePolicyObject)) + } + + return result + } + + // Format subject/issuer information in RFC1779 private func blockDistinguishedName(block: ASN1Object) -> String { var result = "" @@ -433,6 +455,42 @@ public class X509ExtCrlDistributionPoint { } +public class X509ExtCertficatePolicy{ + + var identifier:String? + var qualifierInfo:X509ExtCertficatePolicyQualifierInfo? + + init(asn1Object: ASN1Object) { + + self.identifier = (asn1Object.sub?[0].value as! String) + + guard asn1Object.subCount() > 1 else { + return + } + + self.qualifierInfo = X509ExtCertficatePolicyQualifierInfo(asn1Object: (asn1Object.sub?[1])!) + + } +} + + +public class X509ExtCertficatePolicyQualifierInfo { + + var identifier:String? + var qualifier:String? + + init(asn1Object: ASN1Object) { + + self.identifier = (asn1Object.sub?.first?.sub?.first?.value as! String) + self.qualifier = (asn1Object.sub?.first?.sub?.last?.value as! String) + + } +} + + + + + public class X509ExtBasicContraints { var isCA:Bool? = false diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index 35e5e47..6067688 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -128,13 +128,18 @@ class ASN1DecoderTests: XCTestCase { let x509 = try X509Certificate(data: certPEMData) - + XCTAssertEqual(x509.basicConstraints.isCA, false ) + XCTAssertEqual(x509.basicConstraints.pathLengthConstraint, nil ) XCTAssertEqual(x509.crlDistributionPoints[0].fullName?.URI,"http://crl3.digicert.com/sha2-ev-server-g2.crl") XCTAssertEqual(x509.crlDistributionPoints[1].fullName?.URI,"http://crl4.digicert.com/sha2-ev-server-g2.crl") - XCTAssertEqual(x509.basicConstraints.isCA, false ) - XCTAssertEqual(x509.basicConstraints.pathLengthConstraint, nil ) + XCTAssertEqual(x509.certificatePolicies[0].identifier,"2.16.840.1.114412.2.1") + XCTAssertEqual(x509.certificatePolicies[0].qualifierInfo?.identifier,"1.3.6.1.5.5.7.2.1") + XCTAssertEqual(x509.certificatePolicies[0].qualifierInfo?.qualifier,"https://www.digicert.com/CPS") + + XCTAssertEqual(x509.certificatePolicies[1].identifier,"2.23.140.1.1") + XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) @@ -143,7 +148,7 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.32")?.valueAsStrings,[]) // CertificatePolicies (2.5.29.32) // FIXME + XCTAssertEqual(x509.extensionObject(oid: "2.5.29.35")?.valueAsStrings,[]) // AuthorityKeyIdentifier (2.5.29.35) // FIXME XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME From 72c1c21beb927141ba6a5c3e2f11fe0e7b4311b3 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Wed, 6 Nov 2019 21:10:52 +0100 Subject: [PATCH 13/17] Added Authority Key Identifier access --- ASN1Decoder/X509Certificate.swift | 26 +++++++++++++++++++++++++ ASN1DecoderTests/ASN1DecoderTests.swift | 3 ++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index e8728c1..a7cb198 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -299,6 +299,10 @@ public class X509Certificate: CustomStringConvertible { return X509ExtBasicContraints(asn1Object:(extensionObject(oid: "2.5.29.19")?.block)!) } + public var authorityKeyIdentifier:X509ExtAuthorityKeyIdentifier { + return X509ExtAuthorityKeyIdentifier(asn1Object:(extensionObject(oid: "2.5.29.35")?.block)!) + } + public var crlDistributionPoints: [X509ExtCrlDistributionPoint] { var result: [X509ExtCrlDistributionPoint] = [] @@ -515,3 +519,25 @@ public class X509ExtBasicContraints { } +public class X509ExtAuthorityKeyIdentifier{ + + var identifier:Data? + var issuer:ASN1GeneralNames? + var serialNumber:String? + + init(asn1Object: ASN1Object) { + + + guard asn1Object.subCount() > 1 else { + return + } + + self.identifier = (asn1Object.sub?[1].sub?.first?.sub?.first?.value as! Data) + + guard (asn1Object.sub?[1].sub?.first?.subCount())! > 1 else { + return + } + self.issuer = ASN1GeneralNames(asn1Object: (asn1Object.sub?[1].sub?.first?.sub?[1])!) + + } +} diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index 6067688..051d81b 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -140,6 +140,7 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.certificatePolicies[1].identifier,"2.23.140.1.1") + XCTAssertEqual(x509.authorityKeyIdentifier.identifier?.hexEncodedString(separation: ":"),"3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F") XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) @@ -149,7 +150,7 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) - XCTAssertEqual(x509.extensionObject(oid: "2.5.29.35")?.valueAsStrings,[]) // AuthorityKeyIdentifier (2.5.29.35) // FIXME + XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.4.1.11129.2.4.2")?.valueAsStrings,[]) // Extended validation certificates (1.3.6.1.4.1.11129.2.4.2) // FIXME From 591e8e588d972b0d3b3f1a345002e13886f1ddcc Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Wed, 6 Nov 2019 23:41:29 +0100 Subject: [PATCH 14/17] Added Subject Key Identifier --- ASN1Decoder/X509Certificate.swift | 4 ++++ ASN1DecoderTests/ASN1DecoderTests.swift | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index a7cb198..e3484df 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -303,6 +303,10 @@ public class X509Certificate: CustomStringConvertible { return X509ExtAuthorityKeyIdentifier(asn1Object:(extensionObject(oid: "2.5.29.35")?.block)!) } + public var subjectKeyIdentifier:Data? { + return extensionObject(oid: "2.5.29.14")?.block.sub?.last?.sub?.first?.rawValue ?? nil + } + public var crlDistributionPoints: [X509ExtCrlDistributionPoint] { var result: [X509ExtCrlDistributionPoint] = [] diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index 051d81b..b75ddc6 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -140,7 +140,8 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.certificatePolicies[1].identifier,"2.23.140.1.1") - XCTAssertEqual(x509.authorityKeyIdentifier.identifier?.hexEncodedString(separation: ":"),"3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F") + XCTAssertEqual(x509.authorityKeyIdentifier.identifier?.hexEncodedString(separation: ":"),"3D:D3:50:A5:D6:A0:AD:EE:F3:4A:60:0A:65:D3:21:D4:F8:F8:D6:0F") + XCTAssertEqual(x509.subjectKeyIdentifier!.hexEncodedString(separation: ":"),"6C:B0:43:56:FE:3D:E8:12:EC:D9:12:F5:63:D5:C4:CA:07:AF:B0:76") XCTAssertEqual(x509.nonCriticalExtensionOIDs,["2.5.29.35", "2.5.29.14", "2.5.29.17", "2.5.29.37", "2.5.29.31", "2.5.29.32", "1.3.6.1.5.5.7.1.1", "1.3.6.1.4.1.11129.2.4.2"]) XCTAssertEqual(x509.criticalExtensionOIDs,["2.5.29.15", "2.5.29.19"]) From fb94003f98876a576dae678289ef6757c7f13ea8 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sat, 9 Nov 2019 10:03:06 +0100 Subject: [PATCH 15/17] Removed duplicate test for SubjectKeyIdentifier --- ASN1DecoderTests/ASN1DecoderTests.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/ASN1DecoderTests/ASN1DecoderTests.swift b/ASN1DecoderTests/ASN1DecoderTests.swift index b75ddc6..5762d9c 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -151,8 +151,6 @@ class ASN1DecoderTests: XCTestCase { XCTAssertEqual(x509.extendedKeyUsage,["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]) // (2.5.29.37) - - XCTAssertEqual((x509.extensionObject(oid: "2.5.29.14")?.value as? Data)?.hexEncodedString(), nil) // SubjectKeyIdentifier (2.5.29.14) // FIXME should be 6CB04356FE3DE812ECD912F563D5C4CA07AFB076 XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.5.5.7.1.1")?.valueAsStrings,[]) // AuthorityInfoAccess (1.3.6.1.5.5.7.1.1) // FIXME XCTAssertEqual(x509.extensionObject(oid: "1.3.6.1.4.1.11129.2.4.2")?.valueAsStrings,[]) // Extended validation certificates (1.3.6.1.4.1.11129.2.4.2) // FIXME From 465f9f1f07727d5ee96d5a22dbed3ee4d9e0c13e Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sat, 16 Nov 2019 17:07:10 +0100 Subject: [PATCH 16/17] Do not try to decode octet string recursively. Let the application decide, if the content is again an ASN1 object tree., e.g. extensions. --- ASN1Decoder/ASN1Decoder.swift | 7 +------ ASN1Decoder/X509Certificate.swift | 14 +++++--------- ASN1Decoder/X509Extension.swift | 12 +++++++++++- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ASN1Decoder/ASN1Decoder.swift b/ASN1Decoder/ASN1Decoder.swift index 5f66dd7..0c7a9eb 100644 --- a/ASN1Decoder/ASN1Decoder.swift +++ b/ASN1Decoder/ASN1Decoder.swift @@ -140,18 +140,13 @@ public class ASN1DERDecoder { case .octetString: - do { - var subIterator = contentData.makeIterator() - asn1obj.sub = try parse(iterator: &subIterator) - } catch { if let str = String(data: contentData, encoding: .utf8) { asn1obj.value = str } else { asn1obj.value = contentData } - } - + default: print("unsupported tag: \(asn1obj.identifier!.tagNumber())") diff --git a/ASN1Decoder/X509Certificate.swift b/ASN1Decoder/X509Certificate.swift index e3484df..7e7887f 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -296,7 +296,7 @@ public class X509Certificate: CustomStringConvertible { public var basicConstraints:X509ExtBasicContraints { - return X509ExtBasicContraints(asn1Object:(extensionObject(oid: "2.5.29.19")?.block)!) + return X509ExtBasicContraints(extObject:extensionObject(oid: "2.5.29.19")) } public var authorityKeyIdentifier:X509ExtAuthorityKeyIdentifier { @@ -506,20 +506,16 @@ public class X509ExtBasicContraints { - init(asn1Object: ASN1Object) { - + init(extObject: X509Extension?) { - if let cAValue = asn1Object.sub?.last?.sub?.first?.value { + if let cAValue = extObject?.valueAsBlock?.sub?.first?.sub?.first?.value { self.isCA = (cAValue as! Bool) } - - - if let pathValue = asn1Object.sub?[2].value { + + if let pathValue = extObject?.valueAsBlock?.sub?.first?.sub?.last?.value { self.pathLengthConstraint = (pathValue as! Int) } } - - } diff --git a/ASN1Decoder/X509Extension.swift b/ASN1Decoder/X509Extension.swift index f3de114..6d31b98 100644 --- a/ASN1Decoder/X509Extension.swift +++ b/ASN1Decoder/X509Extension.swift @@ -27,7 +27,17 @@ public class X509Extension { let block: ASN1Object - init(block: ASN1Object) { + init(block: ASN1Object) { + + do { + let extBlock = try ASN1DERDecoder.decode(data: block.sub?.last?.rawValue ?? Data()) + extBlock[0].parent = block.sub?.last + block.sub?.last?.sub = extBlock + } + catch { + + } + self.block = block } From 8098c92e041e0a8cb40fc11bce5c66951a7272da Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Sun, 1 Dec 2019 12:35:00 +0100 Subject: [PATCH 17/17] OCTETSTRING is always Data. Never a String. --- ASN1Decoder/ASN1Decoder.swift | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ASN1Decoder/ASN1Decoder.swift b/ASN1Decoder/ASN1Decoder.swift index 0c7a9eb..c095cc9 100644 --- a/ASN1Decoder/ASN1Decoder.swift +++ b/ASN1Decoder/ASN1Decoder.swift @@ -140,12 +140,7 @@ public class ASN1DERDecoder { case .octetString: - if let str = String(data: contentData, encoding: .utf8) { - asn1obj.value = str - } - else { - asn1obj.value = contentData - } + asn1obj.value = contentData default: