diff --git a/Package.swift b/Package.swift index 9b61fedb..ff64ad3d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.9 +// swift-tools-version:6.0 import PackageDescription let package = Package( @@ -22,10 +22,16 @@ let package = Package( targets: [ .target( name: "TrustKit", + dependencies: ["TrustKitSwift"], path: "TrustKit", + exclude: ["Swift"], resources: [.process("PrivacyInfo.xcprivacy")], publicHeadersPath: "public", cSettings: [.define("NS_BLOCK_ASSERTIONS", to: "1", .when(configuration: .release))] ), + .target( + name: "TrustKitSwift", + path: "TrustKit/Swift" + ), ] ) diff --git a/TrustKit.xcodeproj/project.pbxproj b/TrustKit.xcodeproj/project.pbxproj index 7caf3c57..c5d36371 100644 --- a/TrustKit.xcodeproj/project.pbxproj +++ b/TrustKit.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 66CF699E1BD05B45270DFD2E /* TSKEndToEndSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBADAA81F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m */; }; 67731757DF2837DC809F1135 /* GoodRootCA.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05D51E28AAA40045D8B3 /* GoodRootCA.der */; }; 67C02585489EE40A186A5A35 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */; }; + DFA90B767A90476814B30C29 /* Secp521r1TestCA.der in Resources */ = {isa = PBXBuildFile; fileRef = D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */; }; 6947D4B221FF8E10DE1E76B1 /* anchor-ca.cert.pem in Resources */ = {isa = PBXBuildFile; fileRef = FCC1DD051EECD19E00AB3D81 /* anchor-ca.cert.pem */; }; 6B032D401AF1AEC200EAFA69 /* TSKReporterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B032D3F1AF1AEB600EAFA69 /* TSKReporterTests.m */; }; 6B2B06AD1B05154A00FC749E /* TSKBackgroundReporter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B2B06AC1B05154A00FC749E /* TSKBackgroundReporter.h */; }; @@ -94,6 +95,7 @@ 7C9060CC7B837D9C4A227DF7 /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C15F99F1B16094D00F06C0E /* TSKPinFailureReport.m */; }; 7D9DD27EB4A45E73F274D7F4 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05C41E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der */; }; 7E0DCB5F043EC6726C2936B5 /* pinning_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = B005E3E729B85EBA007C3D84 /* pinning_utils.m */; }; + 45B5FB2AF8D093EDE71C825D /* SubjectPublicKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */; }; 805B476B7164A3EDD62F3F24 /* TSKPinningValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = 7033D35C248FE84100BDFF50 /* TSKPinningValidator.h */; settings = {ATTRIBUTES = (Public, ); }; }; 82B218B6BA21627354BF9F46 /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CE919211AEA077F002B29AE /* ssl_pin_verifier.m */; }; 8418436F3F12D407F7D14221 /* TSKSPKIHashCache.h in Headers */ = {isa = PBXBuildFile; fileRef = FC1A09081E57AC450055B12C /* TSKSPKIHashCache.h */; }; @@ -194,8 +196,11 @@ 8CBA05E21E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05D71E28AAA40045D8B3 /* www.good.com.selfsigned.der */; }; 8CBA05E31E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05D71E28AAA40045D8B3 /* www.good.com.selfsigned.der */; }; 8CBA05E51E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */; }; + 32D48D404CE5B9DCB8012B9E /* Secp521r1TestCA.der in Resources */ = {isa = PBXBuildFile; fileRef = D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */; }; 8CBA05E61E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */; }; + 2391BA800486FCAECBDF4B3C /* Secp521r1TestCA.der in Resources */ = {isa = PBXBuildFile; fileRef = D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */; }; 8CBA05E71E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */; }; + D055C49F2F445E93B9B7ACD9 /* Secp521r1TestCA.der in Resources */ = {isa = PBXBuildFile; fileRef = D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */; }; 8CBADAA91F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBADAA81F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m */; }; 8CBADAAA1F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBADAA81F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m */; }; 8CBADAAB1F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CBADAA81F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m */; }; @@ -257,6 +262,7 @@ 983CC0C818D2101B84C82776 /* www.good.com.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05D61E28AAA40045D8B3 /* www.good.com.der */; }; 9A5120235297DD14E140CABE /* Corporation Service Company RSA OV SSL CA.der in Resources */ = {isa = PBXBuildFile; fileRef = 91B276432B9A463E004B41A7 /* Corporation Service Company RSA OV SSL CA.der */; }; A1545F2242505B24A34908CE /* GeoTrust_Primary_CA_G2_ECC.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */; }; + A5848AD19F06AE3B82E2A61D /* Secp521r1TestCA.der in Resources */ = {isa = PBXBuildFile; fileRef = D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */; }; A24BC90C183D5AE61637717A /* OCMock in Frameworks */ = {isa = PBXBuildFile; productRef = 10E47E25291C2EAB4E7C79C8 /* OCMock */; }; A53B7497AB0709DE758E15F3 /* TSKNSURLConnectionDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CD5F72F1BC5ED4A005801D8 /* TSKNSURLConnectionDelegateProxy.h */; }; A5A3331EF9F962289F145FD8 /* www.good.com.selfsigned.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05D71E28AAA40045D8B3 /* www.good.com.selfsigned.der */; }; @@ -265,9 +271,13 @@ AE59E8A5C3487566AADBDFB2 /* www.good.com.der in Resources */ = {isa = PBXBuildFile; fileRef = 8CBA05D61E28AAA40045D8B3 /* www.good.com.der */; }; AED4268613023DF853EB22EC /* Corporation Service Company RSA OV SSL CA.der in Resources */ = {isa = PBXBuildFile; fileRef = 91B276432B9A463E004B41A7 /* Corporation Service Company RSA OV SSL CA.der */; }; B005E3E829B85EBA007C3D84 /* pinning_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = B005E3E729B85EBA007C3D84 /* pinning_utils.m */; }; + BDA43C6C288EEF13E3A3D6B3 /* SubjectPublicKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */; }; B005E3EB29B85EBA007C3D84 /* pinning_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = B005E3E729B85EBA007C3D84 /* pinning_utils.m */; }; + 83D3CE8E6A8308E00B957A40 /* SubjectPublicKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */; }; B005E3ED29B85EBA007C3D84 /* pinning_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = B005E3E729B85EBA007C3D84 /* pinning_utils.m */; }; + F2BFBF8001DFD807C061A666 /* SubjectPublicKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */; }; B005E3EF29B85EBA007C3D84 /* pinning_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = B005E3E729B85EBA007C3D84 /* pinning_utils.m */; }; + B3A8FCA3E62F7CD5FDEA0B92 /* SubjectPublicKeyInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */; }; B005E3F129B8C2F8007C3D84 /* pinning_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = B005E3F029B85ED0007C3D84 /* pinning_utils.h */; }; B005E3F229B8C2F8007C3D84 /* pinning_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = B005E3F029B85ED0007C3D84 /* pinning_utils.h */; }; B005E3F329B8C2F9007C3D84 /* pinning_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = B005E3F029B85ED0007C3D84 /* pinning_utils.h */; }; @@ -412,6 +422,7 @@ 8CBA05D61E28AAA40045D8B3 /* www.good.com.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = www.good.com.der; path = Certificates/RSA_4096/www.good.com.der; sourceTree = ""; }; 8CBA05D71E28AAA40045D8B3 /* www.good.com.selfsigned.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = www.good.com.selfsigned.der; path = Certificates/RSA_4096/www.good.com.selfsigned.der; sourceTree = ""; }; 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = GeoTrust_Primary_CA_G2_ECC.der; path = Certificates/ECDSA_sec384r1/GeoTrust_Primary_CA_G2_ECC.der; sourceTree = ""; }; + D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */ = {isa = PBXFileReference; lastKnownFileType = file; name = Secp521r1TestCA.der; path = Certificates/ECDSA_sec521r1/Secp521r1TestCA.der; sourceTree = ""; }; 8CBADAA81F2A850F00FCD7FB /* TSKEndToEndSwizzlingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKEndToEndSwizzlingTests.m; sourceTree = ""; }; 8CC5D24E1D6E64D10074F515 /* TrustKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TrustKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8CC78B1E1B1B586F00523A25 /* TSKCertificateUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSKCertificateUtils.h; sourceTree = ""; }; @@ -434,6 +445,7 @@ 8CF27AA11F01BB7B009369B0 /* TSKLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSKLoggerTests.m; sourceTree = ""; }; 91B276432B9A463E004B41A7 /* Corporation Service Company RSA OV SSL CA.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Corporation Service Company RSA OV SSL CA.der"; sourceTree = ""; }; B005E3E729B85EBA007C3D84 /* pinning_utils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = pinning_utils.m; path = Pinning/pinning_utils.m; sourceTree = ""; }; + 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SubjectPublicKeyInfo.swift; path = Swift/SubjectPublicKeyInfo.swift; sourceTree = ""; }; B005E3F029B85ED0007C3D84 /* pinning_utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = pinning_utils.h; path = Pinning/pinning_utils.h; sourceTree = ""; }; B0C703E4281D53491D783E5F /* TrustKit visionOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "TrustKit visionOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F28762BAB30A8001B604A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; @@ -665,6 +677,7 @@ isa = PBXGroup; children = ( 8CBA05E41E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der */, + D2E379C6EA9EB04E33F5ED0F /* Secp521r1TestCA.der */, ); name = "ECDSA sec384r1"; sourceTree = ""; @@ -717,6 +730,7 @@ FC1A09091E57AC450055B12C /* TSKSPKIHashCache.m */, FC1A09121E57C6820055B12C /* TSKPublicKeyAlgorithm.h */, B005E3E729B85EBA007C3D84 /* pinning_utils.m */, + 5E84F4FFC49B4AC9B23F1EF4 /* SubjectPublicKeyInfo.swift */, B005E3F029B85ED0007C3D84 /* pinning_utils.h */, ); name = Pinning; @@ -1205,6 +1219,7 @@ files = ( CAB30AE3F25878920301AFB7 /* anchor-fake.yahoo.com.cert.pem in Resources */, 67C02585489EE40A186A5A35 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + DFA90B767A90476814B30C29 /* Secp521r1TestCA.der in Resources */, A7E4C7FEF227A79892290A1B /* anchor-intermediate.cert.pem in Resources */, 72B6AEAF956C7653D6A0A567 /* www.good.com.selfsigned.der in Resources */, 6257932D020646705F9CFC8F /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, @@ -1234,6 +1249,7 @@ files = ( 03655B98EBD2E5FA7E022C95 /* anchor-fake.yahoo.com.cert.pem in Resources */, A1545F2242505B24A34908CE /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + A5848AD19F06AE3B82E2A61D /* Secp521r1TestCA.der in Resources */, 1D82C78917FF8BFF2048EA13 /* anchor-intermediate.cert.pem in Resources */, A5A3331EF9F962289F145FD8 /* www.good.com.selfsigned.der in Resources */, 7D9DD27EB4A45E73F274D7F4 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, @@ -1263,6 +1279,7 @@ files = ( FCC1DD091EECD19E00AB3D81 /* anchor-fake.yahoo.com.cert.pem in Resources */, 8CBA05E51E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + 32D48D404CE5B9DCB8012B9E /* Secp521r1TestCA.der in Resources */, FCC1DD0A1EECD19E00AB3D81 /* anchor-intermediate.cert.pem in Resources */, 8CBA05E11E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */, 8CBA05C71E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, @@ -1292,6 +1309,7 @@ files = ( FC049B3E1EECD1B100FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */, 8CBA05E71E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + D055C49F2F445E93B9B7ACD9 /* Secp521r1TestCA.der in Resources */, FC049B3F1EECD1B100FDC5F4 /* anchor-intermediate.cert.pem in Resources */, 8CBA05E31E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */, 8CBA05C91E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, @@ -1321,6 +1339,7 @@ files = ( FC049B3B1EECD1B000FDC5F4 /* anchor-fake.yahoo.com.cert.pem in Resources */, 8CBA05E61E294CC30045D8B3 /* GeoTrust_Primary_CA_G2_ECC.der in Resources */, + 2391BA800486FCAECBDF4B3C /* Secp521r1TestCA.der in Resources */, FC049B3C1EECD1B000FDC5F4 /* anchor-intermediate.cert.pem in Resources */, 8CBA05E21E28AAA40045D8B3 /* www.good.com.selfsigned.der in Resources */, 8CBA05C81E28AA6C0045D8B3 /* GlobalSignDomainValidationCA-SHA256-G2.der in Resources */, @@ -1377,6 +1396,7 @@ 6BEE9B751CA48C25F2D78954 /* TSKReportsRateLimiter.m in Sources */, BA4033A9744CF186E7E38480 /* parse_configuration.m in Sources */, 7E0DCB5F043EC6726C2936B5 /* pinning_utils.m in Sources */, + 45B5FB2AF8D093EDE71C825D /* SubjectPublicKeyInfo.swift in Sources */, 2C3C580D8BBA830D9CE101FD /* TSKBackgroundReporter.m in Sources */, 6C7FCCD6E6E97546DF3EEFCE /* TSKNSURLSessionDelegateProxy.m in Sources */, 26024026147AF4313E2B54BB /* configuration_utils.m in Sources */, @@ -1401,6 +1421,7 @@ 8C9EBE031B619BBE00CA7EE0 /* TSKReportsRateLimiter.m in Sources */, 8C5D98B31CEFF079008E654B /* parse_configuration.m in Sources */, B005E3E829B85EBA007C3D84 /* pinning_utils.m in Sources */, + BDA43C6C288EEF13E3A3D6B3 /* SubjectPublicKeyInfo.swift in Sources */, 6B2B06AF1B05157400FC749E /* TSKBackgroundReporter.m in Sources */, 8CD5F74B1BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.m in Sources */, 8C4346DA1E5B894A008023F9 /* configuration_utils.m in Sources */, @@ -1444,6 +1465,7 @@ 8C84CB921D6E0981009B3E7D /* TSKReportsRateLimiter.m in Sources */, 8C84CB931D6E0981009B3E7D /* parse_configuration.m in Sources */, B005E3ED29B85EBA007C3D84 /* pinning_utils.m in Sources */, + F2BFBF8001DFD807C061A666 /* SubjectPublicKeyInfo.swift in Sources */, 8C84CB941D6E0981009B3E7D /* TSKBackgroundReporter.m in Sources */, 8C84CB951D6E0981009B3E7D /* TSKNSURLSessionDelegateProxy.m in Sources */, 8C4346DD1E5B894A008023F9 /* configuration_utils.m in Sources */, @@ -1486,6 +1508,7 @@ 8CA6CC1A1BAE2B6600BDA419 /* TSKBackgroundReporter.m in Sources */, 8CA6CC1C1BAE2B6600BDA419 /* TSKPinFailureReport.m in Sources */, B005E3EB29B85EBA007C3D84 /* pinning_utils.m in Sources */, + 83D3CE8E6A8308E00B957A40 /* SubjectPublicKeyInfo.swift in Sources */, 8CD5F74D1BCB535E005801D8 /* TSKNSURLSessionDelegateProxy.m in Sources */, 8C5D98B51CEFF079008E654B /* parse_configuration.m in Sources */, 8C4346DC1E5B894A008023F9 /* configuration_utils.m in Sources */, @@ -1530,6 +1553,7 @@ 8CC5D2271D6E64D10074F515 /* TSKReportsRateLimiter.m in Sources */, 8CC5D2281D6E64D10074F515 /* parse_configuration.m in Sources */, B005E3EF29B85EBA007C3D84 /* pinning_utils.m in Sources */, + B3A8FCA3E62F7CD5FDEA0B92 /* SubjectPublicKeyInfo.swift in Sources */, 8CC5D22A1D6E64D10074F515 /* TSKBackgroundReporter.m in Sources */, 8CC5D22B1D6E64D10074F515 /* TSKNSURLSessionDelegateProxy.m in Sources */, 8C4346DE1E5B894A008023F9 /* configuration_utils.m in Sources */, @@ -1747,6 +1771,7 @@ ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; + "EXCLUDED_ARCHS[sdk=*simulator*]" = "arm64e arm64e.x1"; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1769,7 +1794,6 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = "$(SRCROOT)/**"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 3.0.8; @@ -1780,6 +1804,7 @@ SDKROOT = macosx; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 15.0; VALID_ARCHS = "$(ARCHS_STANDARD)"; @@ -1835,6 +1860,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; + "EXCLUDED_ARCHS[sdk=*simulator*]" = "arm64e arm64e.x1"; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = YES; @@ -1852,7 +1878,6 @@ GCC_WARN_UNKNOWN_PRAGMAS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = "$(SRCROOT)/**"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; MACOSX_DEPLOYMENT_TARGET = 12.0; MARKETING_VERSION = 3.0.8; @@ -1863,6 +1888,7 @@ SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx"; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TVOS_DEPLOYMENT_TARGET = 15.0; VALIDATE_PRODUCT = YES; diff --git a/TrustKit/Pinning/TSKSPKIHashCache.h b/TrustKit/Pinning/TSKSPKIHashCache.h index ff6d4f7d..9ae33a3d 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.h +++ b/TrustKit/Pinning/TSKSPKIHashCache.h @@ -25,7 +25,8 @@ NS_ASSUME_NONNULL_BEGIN // The identifier used for the default shared hash cache. Use this identifier // in the TSKSPKIHashCache constructor to use the shared cache. -static NSString * const kTSKSPKISharedHashCacheIdentifier = @"spki-hash.cache"; +// Versioned so that hashes cached by TrustKit 3.x, which rebuilt the SPKI from the key, are not reused. +static NSString * const kTSKSPKISharedHashCacheIdentifier = @"spki-hash-v2.cache"; // Each key is a raw certificate data (for easy lookup) and each value is the certificate's raw SPKI data typedef NSMutableDictionary SPKICacheDictionnary; diff --git a/TrustKit/Pinning/TSKSPKIHashCache.m b/TrustKit/Pinning/TSKSPKIHashCache.m index 5ee28ddc..4b9f2440 100644 --- a/TrustKit/Pinning/TSKSPKIHashCache.m +++ b/TrustKit/Pinning/TSKSPKIHashCache.m @@ -13,122 +13,11 @@ #import "TSKSPKIHashCache.h" #import "../TSKLog.h" #import -#import "pinning_utils.h" - -#pragma mark Missing ASN1 SPKI Headers - -// These are the ASN1 headers for the Subject Public Key Info section of a certificate -// TODO(AD): Are they returned by the new iOS API https://developer.apple.com/documentation/security/2963103-seccertificatecopykey ? -static const unsigned char rsa2048Asn1Header[] = -{ - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00 -}; - -static const unsigned char rsa3072Asn1Header[] = -{ - 0x30, 0x82, 0x01, 0xA2, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, - 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8F, 0x00 -}; - -static const unsigned char rsa4096Asn1Header[] = -{ - 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00 -}; - -static const unsigned char ecDsaSecp256r1Asn1Header[] = -{ - 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, - 0x42, 0x00 -}; - -static const unsigned char ecDsaSecp384r1Asn1Header[] = -{ - 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00 -}; - - -static BOOL isKeySupported(NSString *publicKeyType, NSNumber *publicKeySize) -{ - if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 2048)) - { - return YES; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 3072)) - { - return YES; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 4096)) - { - return YES; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeECSECPrimeRandom]) && ([publicKeySize integerValue] == 256)) - { - return YES; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeECSECPrimeRandom]) && ([publicKeySize integerValue] == 384)) - { - return YES; - } - return NO; -} - - -static char *getAsn1HeaderBytes(NSString *publicKeyType, NSNumber *publicKeySize) -{ - if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 2048)) - { - return (char *)rsa2048Asn1Header; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 3072)) - { - return (char *)rsa3072Asn1Header; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 4096)) - { - return (char *)rsa4096Asn1Header; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeECSECPrimeRandom]) && ([publicKeySize integerValue] == 256)) - { - return (char *)ecDsaSecp256r1Asn1Header; - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeECSECPrimeRandom]) && ([publicKeySize integerValue] == 384)) - { - return (char *)ecDsaSecp384r1Asn1Header; - } - - @throw([NSException exceptionWithName:@"Unsupported public key algorithm" reason:@"Tried to generate the SPKI hash for an unsupported key algorithm" userInfo:nil]); -} - -static unsigned int getAsn1HeaderSize(NSString *publicKeyType, NSNumber *publicKeySize) -{ - if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 2048)) - { - return sizeof(rsa2048Asn1Header); - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 3072)) - { - return sizeof(rsa3072Asn1Header); - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeRSA]) && ([publicKeySize integerValue] == 4096)) - { - return sizeof(rsa4096Asn1Header); - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeECSECPrimeRandom]) && ([publicKeySize integerValue] == 256)) - { - return sizeof(ecDsaSecp256r1Asn1Header); - } - else if (([publicKeyType isEqualToString:(NSString *)kSecAttrKeyTypeECSECPrimeRandom]) && ([publicKeySize integerValue] == 384)) - { - return sizeof(ecDsaSecp384r1Asn1Header); - } - - @throw([NSException exceptionWithName:@"Unsupported public key algorithm" reason:@"Tried to generate the SPKI hash for an unsupported key algorithm" userInfo:nil]); -} - +#if SWIFT_PACKAGE +@import TrustKitSwift; +#else +#import +#endif @interface TSKSPKIHashCache () @@ -234,54 +123,15 @@ - (NSData *)_hashSubjectPublicKeyInfoFromCertificate:(SecCertificateRef)certific // We didn't this certificate in the cache so we need to generate its SPKI hash os_log_debug(TSKLogHandle(), "Generating Subject Public Key Info hash..."); - // First extract the public key - SecKeyRef publicKey = [self copyPublicKeyFromCertificate:certificate]; - if (publicKey == nil) - { - os_log_error(TSKLogHandle(), "Could not copy the public key from the certificate"); - return nil; - } - - // Obtain the public key bytes from the key reference - NSData *publicKeyData = (__bridge_transfer NSData *)SecKeyCopyExternalRepresentation(publicKey, NULL); - if (publicKeyData == nil) + NSData *subjectPublicKeyInfo = [TSKSubjectPublicKeyInfo dataFromCertificate:certificateData]; + if (subjectPublicKeyInfo == nil) { - os_log_error(TSKLogHandle(), "Could not extract the public key bytes"); - CFRelease(publicKey); + os_log_error(TSKLogHandle(), "Could not read the Subject Public Key Info from the certificate"); return nil; } - // Obtain the SPKI header based on the key's algorithm - CFDictionaryRef publicKeyAttributes = SecKeyCopyAttributes(publicKey); - NSString *publicKeyType = CFDictionaryGetValue(publicKeyAttributes, kSecAttrKeyType); - NSNumber *publicKeysize = CFDictionaryGetValue(publicKeyAttributes, kSecAttrKeySizeInBits); - CFRelease(publicKeyAttributes); - - if (!isKeySupported(publicKeyType, publicKeysize)) - { - os_log_error(TSKLogHandle(), "Public key algorithm or length is not supported"); - CFRelease(publicKey); - return nil; - } - - char *asn1HeaderBytes = getAsn1HeaderBytes(publicKeyType, publicKeysize); - unsigned int asn1HeaderSize = getAsn1HeaderSize(publicKeyType, publicKeysize); - - CFRelease(publicKey); - - // Generate a hash of the subject public key info NSMutableData *subjectPublicKeyInfoHash = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH]; - CC_SHA256_CTX shaCtx; - CC_SHA256_Init(&shaCtx); - - // Add the missing ASN1 header for public keys to re-create the subject public key info - CC_SHA256_Update(&shaCtx, asn1HeaderBytes, asn1HeaderSize); - - - // Add the public key - CC_SHA256_Update(&shaCtx, [publicKeyData bytes], (unsigned int)[publicKeyData length]); - CC_SHA256_Final((unsigned char *)[subjectPublicKeyInfoHash bytes], &shaCtx); - + CC_SHA256([subjectPublicKeyInfo bytes], (CC_LONG)[subjectPublicKeyInfo length], [subjectPublicKeyInfoHash mutableBytes]); // Store the hash in our memory cache self->_spkiCache[certificateData] = subjectPublicKeyInfoHash; @@ -339,41 +189,6 @@ - (SPKICacheDictionnary *)loadSPKICacheFromFileSystem return spkiCache; } -- (SecKeyRef)copyPublicKeyFromCertificate:(SecCertificateRef)certificate -{ - OSStatus status; - - // Create an X509 trust using the using the certificate - SecTrustRef trust; - SecPolicyRef policy = SecPolicyCreateBasicX509(); - status = SecTrustCreateWithCertificates(certificate, policy, &trust); - CFRelease(policy); - - if (status != errSecSuccess) - { - os_log_error(TSKLogHandle(), "Could not create trust from certificate, got status %d", (int)status); - return nil; - } - - // Get a public key reference for the certificate from the trust - // The certificate chain must be evaluated first in order to be able - // to determine which is the leaf certificate of the chain, and only - // then SecTrustCopyKey can be called - NSError *error = NULL; - SecTrustResultType trustResult = 0; - evaluateCertificateChainTrust(trust, &trustResult, &error); - if ((error != NULL) && (trustResult != kSecTrustResultRecoverableTrustFailure)) - { - os_log_error(TSKLogHandle(), "Could not evaluate trust for the certificate: %{public}@", [error localizedDescription]); - CFRelease(trust); - return nil; - } - - SecKeyRef publicKey = copyKey(trust); - CFRelease(trust); - return publicKey; -} - - (NSURL *)SPKICachePath { NSURL *cachesDirUrl = [NSFileManager.defaultManager URLsForDirectory:NSCachesDirectory diff --git a/TrustKit/Pinning/pinning_utils.h b/TrustKit/Pinning/pinning_utils.h index 9b11ab24..64b426cc 100644 --- a/TrustKit/Pinning/pinning_utils.h +++ b/TrustKit/Pinning/pinning_utils.h @@ -44,12 +44,4 @@ void evaluateCertificateChainTrust(SecTrustRef serverTrust, SecTrustResultType * */ SecCertificateRef getCertificateAtIndex(SecTrustRef serverTrust, CFIndex index); -/** - Returns the public key for a leaf certificate after it has been evaluated. - - @param serverTrust The trust management object to evaluate - @return The leaf certificate's public key, or nil if the public key could not be extracted - */ -SecKeyRef copyKey(SecTrustRef serverTrust); - #endif diff --git a/TrustKit/Pinning/pinning_utils.m b/TrustKit/Pinning/pinning_utils.m index f977a774..65ab4b19 100644 --- a/TrustKit/Pinning/pinning_utils.m +++ b/TrustKit/Pinning/pinning_utils.m @@ -34,7 +34,3 @@ SecCertificateRef getCertificateAtIndex(SecTrustRef serverTrust, CFIndex index) CFRelease(certs); return certificate; } - -SecKeyRef copyKey(SecTrustRef serverTrust) { - return SecTrustCopyKey(serverTrust); -} diff --git a/TrustKit/Swift/SubjectPublicKeyInfo.swift b/TrustKit/Swift/SubjectPublicKeyInfo.swift new file mode 100644 index 00000000..2529f8a9 --- /dev/null +++ b/TrustKit/Swift/SubjectPublicKeyInfo.swift @@ -0,0 +1,80 @@ +/* + + SubjectPublicKeyInfo.swift + TrustKit + + Copyright 2026 The TrustKit Project Authors + Licensed under the MIT license, see associated LICENSE file for terms. + See AUTHORS file for the list of project authors. + + */ + +import Foundation + +@objc(TSKSubjectPublicKeyInfo) +public final class SubjectPublicKeyInfo: NSObject { + /// Returns the DER-encoded Subject Public Key Info of a DER-encoded certificate, exactly as it + /// is encoded in the certificate, or nil if the certificate could not be read. + @objc public static func data(fromCertificate certificate: Data) -> Data? { + // An Array keeps every read below bounds-checked, including in release builds + let der = [UInt8](certificate) + guard let subjectPublicKeyInfo = subjectPublicKeyInfoRange(in: der) else { + return nil + } + return Data(der[subjectPublicKeyInfo]) + } +} + +private struct DERElement { + let tag: UInt8 + let content: Range + let element: Range +} + +private func subjectPublicKeyInfoRange(in der: [UInt8]) -> Range? { + guard let certificate = readElement(der, in: der.indices), certificate.tag == 0x30, + let tbsCertificate = readElement(der, in: certificate.content), tbsCertificate.tag == 0x30 else { + return nil + } + + // RFC 5280 4.1: an optional [0] version, then serialNumber, signature, issuer, validity and subject + var remaining = tbsCertificate.content + if let version = readElement(der, in: remaining), version.tag == 0xa0 { + remaining = version.element.upperBound..) -> DERElement? { + guard range.count >= 2 else { + return nil + } + let tag = der[range.lowerBound] + var contentStart = range.lowerBound + 2 + var length = Int(der[range.lowerBound + 1]) + if length & 0x80 != 0 { + // DER has no indefinite length (0x80), and no certificate needs more than 3 length bytes + let lengthBytes = length & 0x7f + guard (1...3).contains(lengthBytes), range.upperBound - contentStart >= lengthBytes else { + return nil + } + length = der[contentStart..= length else { + return nil + } + return DERElement(tag: tag, + content: contentStart.. #import "../TrustKit/Reporting/reporting_utils.h" #import "TSKCertificateUtils.h" @@ -113,6 +114,40 @@ - (void)testExtractEcDsaSecp384r1 } +- (void)testExtractEcDsaSecp521r1 +{ + // Ensure a secp521r1 key is properly extracted from its certificate + SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"Secp521r1TestCA"]; + + NSData *spkiHash = [spkiCache hashSubjectPublicKeyInfoFromCertificate:certificate]; + NSString *spkiPin = [spkiHash base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + + XCTAssertEqualObjects(spkiPin, @"JDbjp5T1MJ6GUN/fY/voVivkg/5a+kiWCTF7yBFL4DM="); + CFRelease(certificate); +} + + +- (void)testSubjectPublicKeyInfoFromMalformedCertificate +{ + SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"www.good.com"]; + NSData *certificateData = (__bridge_transfer NSData *)SecCertificateCopyData(certificate); + CFRelease(certificate); + + XCTAssertNotNil([TSKSubjectPublicKeyInfo dataFromCertificate:certificateData]); + for (NSUInteger length = 0; length < [certificateData length]; length++) + { + XCTAssertNil([TSKSubjectPublicKeyInfo dataFromCertificate:[certificateData subdataWithRange:NSMakeRange(0, length)]], @"Truncated to %lu bytes", (unsigned long)length); + } + + const uint8_t indefiniteLength[] = {0x30, 0x80, 0x30, 0x80, 0x00, 0x00, 0x00, 0x00}; + const uint8_t fiveByteLength[] = {0x30, 0x85, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00}; + const uint8_t notASequence[] = {0x31, 0x02, 0x30, 0x00}; + XCTAssertNil([TSKSubjectPublicKeyInfo dataFromCertificate:[NSData dataWithBytes:indefiniteLength length:sizeof(indefiniteLength)]]); + XCTAssertNil([TSKSubjectPublicKeyInfo dataFromCertificate:[NSData dataWithBytes:fiveByteLength length:sizeof(fiveByteLength)]]); + XCTAssertNil([TSKSubjectPublicKeyInfo dataFromCertificate:[NSData dataWithBytes:notASequence length:sizeof(notASequence)]]); +} + + - (void)testSPKICacheMissOffMainThreadDoesNotWaitForMainThread { SecCertificateRef certificate = [TSKCertificateUtils createCertificateFromDer:@"www.globalsign.com"]; diff --git a/docs/getting-started.md b/docs/getting-started.md index ca54504b..d58b8689 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -38,12 +38,15 @@ certificate's Subject Public Key Info; this is the same as what is described in the [HTTP Public Key Pinning specification](https://developer.mozilla.org/en-US/docs/Web/Security/Public_Key_Pinning). -To generate such values, a Python helper script is available within the project's -repository; it can be used to generate the pin configuration from a PEM or DER -certificate: +To generate such values, run the `get_pin_from_certificate.sh` script from the project's +repository on a PEM or DER certificate: - $ python get_pin_from_certificate.py ca.pem - $ python get_pin_from_certificate.py --type DER ca.der + $ ./get_pin_from_certificate.sh ca.pem + $ ./get_pin_from_certificate.sh ca.der + +The script hashes the Subject Public Key Info exactly as it is encoded in the certificate, as +TrustKit does. Avoid re-encoding the key first, for example with `openssl pkey`: for some +certificates that changes the bytes, and the resulting pin won't match. ## Deploying TrustKit @@ -70,6 +73,16 @@ Add the following line to your Cartfile: Then, follow the instructions from the [Carthage README](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application). +#### Prebuilt static XCFramework + +Each [GitHub release](https://github.com/datatheorem/TrustKit/releases) includes `TrustKit-static.zip`, +which contains a static `TrustKit.xcframework`. + +Part of TrustKit is written in Swift. If your app has no Swift code of its own and its deployment +target is earlier than iOS 16, tvOS 16, Mac Catalyst 16 or macOS 13, linking fails with +`Undefined symbols … __swift_FORCE_LOAD_$_swiftCompatibility56`. To fix this, add an empty Swift +file to your app target; Xcode then links the Swift libraries that TrustKit needs on those OS versions. + #### CocoaPods TrustKit 4.0 and later are not published to CocoaPods. Use the Swift Package diff --git a/generate_test_certificates.py b/generate_test_certificates.py deleted file mode 100644 index cb4d3a32..00000000 --- a/generate_test_certificates.py +++ /dev/null @@ -1,11 +0,0 @@ -import trustme - -# Create a CA -ca = trustme.CA() - -# Issue a cert signed by this CA -server_cert = ca.issue_cert(u"www.good.com") - -# Save the PEM-encoded data to a file -ca.cert_pem.write_to_path("GoodRootCA.pem") -server_cert.private_key_and_cert_chain_pem.write_to_path("www.good.com.pem") diff --git a/get_pin_from_certificate.py b/get_pin_from_certificate.py deleted file mode 100644 index 4050a7f7..00000000 --- a/get_pin_from_certificate.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python -"""Helper script to generate a TrustKit or HPKP pin from a PEM/DER certificate file. -""" -from __future__ import print_function -from subprocess import Popen, PIPE -from sys import stdin -import os.path -import argparse -import hashlib -import base64 -import platform -import io - - -class SupportedKeyAlgorithmsEnum(object): - RSA_2048 = 1 - RSA_4096 = 2 - ECDSA_SECP256R1 = 3 - ECDSA_SECP384R1 = 4 - - -if __name__ == '__main__': - # Parse the command line - parser = argparse.ArgumentParser(description='Generate HPKP / TrustKit SSL Pins.') - parser.add_argument('certificate', metavar='FILE', nargs='?', help='certificate file to read, if empty, stdin is used') - parser.add_argument('--type', dest='type', action='store', default='PEM', - help='Certificate file type; "PEM" (default) or "DER".') - args = parser.parse_args() - - if not args.certificate: - certificate = stdin.read() - elif os.path.isfile(args.certificate): - with io.open(args.certificate, 'rb') as certFile: - certificate = certFile.read() - else: - raise ValueError('Could not open certificate file {}'.format(args.certificate)) - - if args.type not in ['DER', 'PEM']: - raise ValueError('Invalid certificate type {}; expected DER or PEM'.format(args.type)) - - # Parse the certificate and print its information - p1 = Popen('openssl x509 -inform {} -text -noout'.format(args.type), shell=True, stdin=PIPE, stdout=PIPE) - certificate_txt = p1.communicate(input=certificate)[0].decode('utf-8') - - print('\nCERTIFICATE INFO\n----------------') - p1 = Popen('openssl x509 -subject -issuer -fingerprint -sha1 -noout -inform {}'.format( - args.type), shell=True, stdin=PIPE, stdout=PIPE) - print(p1.communicate(input=certificate)[0]) - - # Extract the certificate key's algorithm - # Tested on the output of OpenSSL 0.9.8zh and OpenSSL 1.0.2i - alg_txt = certificate_txt.split('Public Key Algorithm:')[1].split('\n')[0].strip() - key_algorithm = None - if alg_txt == 'id-ecPublicKey': - if 'prime256v1' in certificate_txt: - key_algorithm = SupportedKeyAlgorithmsEnum.ECDSA_SECP256R1 - if 'secp384r1' in certificate_txt: - key_algorithm = SupportedKeyAlgorithmsEnum.ECDSA_SECP384R1 - elif alg_txt == 'rsaEncryption': - if 'Key: (2048 bit)' in certificate_txt: - key_algorithm = SupportedKeyAlgorithmsEnum.RSA_2048 - elif 'Key: (4096 bit)' in certificate_txt: - key_algorithm = SupportedKeyAlgorithmsEnum.RSA_4096 - - if key_algorithm is None: - raise ValueError('Error: Certificate key algorithm not supported: {}.'.format(alg_txt)) - - - # Generate the Subject Public Key Info hash - if key_algorithm == SupportedKeyAlgorithmsEnum.ECDSA_SECP256R1: - # The OpenSSL command is different for ECDSA secp256 - openssl_alg = 'ec' - elif key_algorithm == SupportedKeyAlgorithmsEnum.ECDSA_SECP384R1: - # The OpenSSL command is different for ECDSA secp384 - openssl_alg = 'ec' - elif key_algorithm == SupportedKeyAlgorithmsEnum.RSA_2048: - openssl_alg = 'rsa' - elif key_algorithm == SupportedKeyAlgorithmsEnum.RSA_4096: - openssl_alg = 'rsa' - else: - raise ValueError('Unexpected key algorithm') - - if platform.system() == 'Windows': - cmd_redirects = '2>nul' - else: - cmd_redirects = '-in /dev/stdin 2>/dev/null' - - p1 = Popen('openssl x509 -pubkey -noout -inform {} ' - '| openssl {} -outform DER -pubin {}'.format(args.type, openssl_alg, cmd_redirects), - shell=True, stdin=PIPE, stdout=PIPE) - spki = p1.communicate(input=certificate)[0] - - spki_hash = hashlib.sha256(spki).digest() - hpkp_pin = base64.b64encode(spki_hash) - - print('\nTRUSTKIT CONFIGURATION\n----------------------') - print('kTSKPublicKeyHashes: @[@"{}"] // You will also need to configure a backup pin'.format(hpkp_pin)) diff --git a/get_pin_from_certificate.sh b/get_pin_from_certificate.sh new file mode 100755 index 00000000..4439ab11 --- /dev/null +++ b/get_pin_from_certificate.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +if [[ $# -ne 1 ]]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# The LibreSSL that ships with macOS doesn't detect DER input on its own +FORMAT=PEM +openssl x509 -inform PEM -in "$1" -noout 2>/dev/null || FORMAT=DER + +DER="$(mktemp)" +trap 'rm -f "$DER"' EXIT +openssl x509 -inform "$FORMAT" -in "$1" -outform DER -out "$DER" + +# TrustKit hashes the Subject Public Key Info exactly as the certificate encodes it; re-encoding the key with +# `openssl pkey` can change those bytes. It is tbsCertificate's 7th field, or its 6th without the optional version. +read -r OFFSET LENGTH < <(openssl asn1parse -inform DER -in "$DER" \ + | sed -E 's/^ *([0-9]+):d=([0-9]+) +hl= *([0-9]+) +l= *([0-9]+) +(.*)$/\1 \2 \3 \4 \5/' \ + | awk '$2 == 2 { n++; if (n == 1 && /cont \[ 0 \]/) skip = 1; if (n == 6 + skip) { if (/SEQUENCE/) print $1, $3 + $4; exit } }') + +PIN="$(tail -c +$((OFFSET + 1)) "$DER" | head -c "$LENGTH" \ + | openssl dgst -sha256 -binary \ + | openssl enc -base64)" +echo "$PIN"