diff --git a/ASN1Decoder/ASN1Decoder.swift b/ASN1Decoder/ASN1Decoder.swift index 5f66dd7..c095cc9 100644 --- a/ASN1Decoder/ASN1Decoder.swift +++ b/ASN1Decoder/ASN1Decoder.swift @@ -140,18 +140,8 @@ 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 - } - } - + asn1obj.value = contentData + default: print("unsupported tag: \(asn1obj.identifier!.tagNumber())") 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 7801513..7e7887f 100644 --- a/ASN1Decoder/X509Certificate.swift +++ b/ASN1Decoder/X509Certificate.swift @@ -83,6 +83,17 @@ public class X509Certificate: CustomStringConvertible { return asn1.reduce("") { $0 + "\($1.description)\n" } } + public var encodedTBSCertificate:Data? { + 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? { + 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 { @@ -201,6 +212,9 @@ public class X509Certificate: CustomStringConvertible { return nil } + + + /** Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15). ``` @@ -280,6 +294,64 @@ public class X509Certificate: CustomStringConvertible { .map(X509Extension.init) } + + public var basicConstraints:X509ExtBasicContraints { + return X509ExtBasicContraints(extObject:extensionObject(oid: "2.5.29.19")) + } + + public var authorityKeyIdentifier:X509ExtAuthorityKeyIdentifier { + 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] = [] + + 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(X509ExtCrlDistributionPoint(asn1Object: crlDistPointObject)) + } + + return result + } + + + + 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 = "" @@ -359,3 +431,113 @@ extension ASN1Object { return sub[index.rawValue] } } + + + + + +public class X509ExtCrlDistributionPoint { + + 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) + // } + // } + +} + + +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 + var pathLengthConstraint:Int? = nil + + + + init(extObject: X509Extension?) { + + if let cAValue = extObject?.valueAsBlock?.sub?.first?.sub?.first?.value { + self.isCA = (cAValue as! Bool) + } + + if let pathValue = extObject?.valueAsBlock?.sub?.first?.sub?.last?.value { + self.pathLengthConstraint = (pathValue as! Int) + } + } +} + + +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/ASN1Decoder/X509Extension.swift b/ASN1Decoder/X509Extension.swift index 7329a5c..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 } @@ -56,14 +66,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/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 39bdf73..5762d9c 100644 --- a/ASN1DecoderTests/ASN1DecoderTests.swift +++ b/ASN1DecoderTests/ASN1DecoderTests.swift @@ -24,21 +24,144 @@ import XCTest +import CryptoKit + + + @testable import ASN1Decoder class ASN1DecoderTests: XCTestCase { - + + @available(OSX 10.12, *) 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") + + 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.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") + + 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.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.publicKey?.algName, "rsaEncryption") + XCTAssertEqual(x509.publicKey!.key!.count*8,4096) + 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: ":"),"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 + + } + + + 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) + + + } + + + + @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 testExtensions() throws { + + 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.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.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"]) + + 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: "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 = "" var subject = "" + var subjectCommonName = "" + var issuer = "" + var issuerEMail = "" if let certData = Data(base64Encoded: cert) { do { @@ -48,6 +171,11 @@ class ASN1DecoderTests: XCTestCase { subject = x509.subjectDistinguishedName ?? "" + subjectCommonName = x509.subject(dn:.commonName) ?? "" + + issuer = x509.issuerDistinguishedName ?? "" + issuerEMail = x509.issuer(dn: .email) ?? "" + } catch { print(error) } @@ -56,6 +184,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") + } let cert = @@ -77,7 +210,7 @@ class ASN1DecoderTests: XCTestCase { "OeAp80GDRAHpjB3qYhzhebiRiM+Bbqva6f4bxNmDNQtL0jt0a8KeyQrFNdAhgjYk" + "AKTucThCu1laJKGKABK90dMoLtbJFxfRhjzmjX9TJGYJgCnRNDDnXpVUOspv2YeH" + "vC9gOdRhaA==" - + let certPEM = """ -----BEGIN CERTIFICATE----- MIIItzCCB5+gAwIBAgIQCDa6olVoZBcgeFhGONhcNDANBgkqhkiG9w0BAQsFADB1 @@ -130,11 +263,75 @@ 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)! } + + + } 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 + } +} + + +@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 } }