├── .gitignore ├── .swiftlint.yml ├── ASN1Decoder macOS ├── ASN1Decoder.h └── Info.plist ├── ASN1Decoder.podspec ├── ASN1Decoder.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ ├── ASN1Decoder macOS.xcscheme │ └── ASN1Decoder.xcscheme ├── ASN1Decoder ├── ASN1Decoder.h ├── ASN1Decoder.swift ├── ASN1DistinguishedNames.swift ├── ASN1Encoder.swift ├── ASN1Identifier.swift ├── ASN1Object.swift ├── Info.plist ├── OID.swift ├── PKCS7.swift ├── PKCS7_AppleReceipt.swift ├── PKCS7_Signature.swift ├── X509Certificate.swift ├── X509Extension.swift ├── X509ExtensionAltName.swift ├── X509ExtensionClasses.swift └── X509PublicKey.swift ├── ASN1DecoderTests ├── ASN1AppleReceiptTest.swift ├── ASN1DecoderAlternativeNames.swift ├── ASN1DecoderCertificateV1.swift ├── ASN1DecoderExtensions.swift ├── ASN1DecoderPkcs7SignatureTests.swift ├── ASN1DecoderX509DecoderTests.swift ├── ASN1DecoderX509SignatureTests.swift ├── Extensions.swift └── Info.plist ├── LICENSE ├── Package.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | # OS X 6 | .DS_Store 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | # Package.pins 43 | .build/ 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | 53 | # Carthage 54 | # 55 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 56 | # Carthage/Checkouts 57 | 58 | Carthage/Build 59 | 60 | # fastlane 61 | # 62 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 63 | # screenshots whenever they are needed. 64 | # For more information about the recommended setup visit: 65 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 66 | 67 | fastlane/report.xml 68 | fastlane/Preview.html 69 | fastlane/screenshots 70 | fastlane/test_output 71 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: 2 | - identifier_name 3 | - redundant_string_enum_value 4 | - trailing_whitespace 5 | - line_length 6 | - multiple_closures_with_trailing_closure 7 | - function_parameter_count 8 | analyzer_rules: # Rules run by `swiftlint analyze` (experimental) 9 | - explicit_self 10 | cyclomatic_complexity: 11 | ignores_case_statements: true 12 | type_body_length: 13 | warning: 500 14 | error: 600 15 | file_length: 16 | warning: 700 17 | error: 1200 18 | type_name: 19 | min_length: 1 20 | max_length: 50 21 | identifier_name: 22 | min_length: 1 23 | max_length: 50 24 | -------------------------------------------------------------------------------- /ASN1Decoder macOS/ASN1Decoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Decoder_macOS.h 3 | // ASN1Decoder macOS 4 | // 5 | // Created by Filippo Maguolo on 18/08/2019. 6 | // Copyright © 2019 Filippo Maguolo. All rights reserved. 7 | // 8 | -------------------------------------------------------------------------------- /ASN1Decoder macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2019 Filippo Maguolo. All rights reserved. 23 | 24 | 25 | -------------------------------------------------------------------------------- /ASN1Decoder.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "ASN1Decoder" 3 | s.version = "1.10.0" 4 | s.summary = "ASN1 DER Decoder for X.509 certificate" 5 | s.description = "ASN1 DER Decoder to parse X.509 certificate" 6 | s.homepage = "https://github.com/filom/ASN1Decoder" 7 | s.license = { :type => "MIT", :file => "LICENSE" } 8 | s.author = { "Filippo Maguolo" => "maguolo.ios@outlook.com" } 9 | s.ios.deployment_target = "12.0" 10 | s.osx.deployment_target = "10.13" 11 | s.tvos.deployment_target = "13.0" 12 | s.source = { :git => "https://github.com/filom/ASN1Decoder.git", :tag => s.version } 13 | s.source_files = "ASN1Decoder/*.swift" 14 | s.swift_version = '5.0' 15 | s.frameworks = "Foundation" 16 | end 17 | -------------------------------------------------------------------------------- /ASN1Decoder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 411C71E32393E64600857C13 /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 411C71E22393E64600857C13 /* OID.swift */; }; 11 | 411C71E42393E64F00857C13 /* OID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 411C71E22393E64600857C13 /* OID.swift */; }; 12 | 412A9E421F55C6500099110C /* ASN1Decoder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 412A9E381F55C6500099110C /* ASN1Decoder.framework */; }; 13 | 412A9E471F55C6500099110C /* ASN1DecoderX509DecoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E461F55C6500099110C /* ASN1DecoderX509DecoderTests.swift */; }; 14 | 412A9E491F55C6500099110C /* ASN1Decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 412A9E3B1F55C6500099110C /* ASN1Decoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | 412A9E561F55C6830099110C /* ASN1Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E521F55C6830099110C /* ASN1Decoder.swift */; }; 16 | 412A9E571F55C6830099110C /* ASN1Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E531F55C6830099110C /* ASN1Identifier.swift */; }; 17 | 412A9E581F55C6830099110C /* ASN1Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E541F55C6830099110C /* ASN1Object.swift */; }; 18 | 412A9E591F55C6830099110C /* X509Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E551F55C6830099110C /* X509Certificate.swift */; }; 19 | 413610EB254B261700452768 /* ASN1Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 413610EA254B261700452768 /* ASN1Encoder.swift */; }; 20 | 413610EF254B2A7800452768 /* ASN1Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 413610EA254B261700452768 /* ASN1Encoder.swift */; }; 21 | 413CB0292AB78CB200FD5FEA /* ASN1DecoderX509SignatureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 413CB0282AB78CB200FD5FEA /* ASN1DecoderX509SignatureTests.swift */; }; 22 | 413CB02D2AB78F0600FD5FEA /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 413CB02C2AB78F0600FD5FEA /* Extensions.swift */; }; 23 | 41409EEE25536A6C0048709B /* X509ExtensionClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41409EED25536A6C0048709B /* X509ExtensionClasses.swift */; }; 24 | 41409EEF25536A6C0048709B /* X509ExtensionClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41409EED25536A6C0048709B /* X509ExtensionClasses.swift */; }; 25 | 41409EF425536B300048709B /* ASN1DecoderExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41409EF325536B300048709B /* ASN1DecoderExtensions.swift */; }; 26 | 4168884C268B294300F40E7C /* ASN1DecoderCertificateV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4168884B268B294300F40E7C /* ASN1DecoderCertificateV1.swift */; }; 27 | 41A2003323661989005EC955 /* X509PublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2003223661989005EC955 /* X509PublicKey.swift */; }; 28 | 41A2003523661A0D005EC955 /* X509Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2003423661A0D005EC955 /* X509Extension.swift */; }; 29 | 41A2003623661A31005EC955 /* X509Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2003423661A0D005EC955 /* X509Extension.swift */; }; 30 | 41A2003723661A34005EC955 /* X509PublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2003223661989005EC955 /* X509PublicKey.swift */; }; 31 | 41A2003923661DA8005EC955 /* ASN1DistinguishedNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2003823661DA8005EC955 /* ASN1DistinguishedNames.swift */; }; 32 | 41A2003A23661DAB005EC955 /* ASN1DistinguishedNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A2003823661DA8005EC955 /* ASN1DistinguishedNames.swift */; }; 33 | 41B11CD1202E16060009670B /* PKCS7_AppleReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41B11CD0202E16060009670B /* PKCS7_AppleReceipt.swift */; }; 34 | 41B703C4239D2EC600904B6B /* ASN1AppleReceiptTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41B703C3239D2EC600904B6B /* ASN1AppleReceiptTest.swift */; }; 35 | 41D14F732538608E00B92428 /* ASN1DecoderAlternativeNames.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D14F722538608E00B92428 /* ASN1DecoderAlternativeNames.swift */; }; 36 | 41D14F7B2538638F00B92428 /* X509ExtensionAltName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D14F7A2538634900B92428 /* X509ExtensionAltName.swift */; }; 37 | 41D14F7F2538639000B92428 /* X509ExtensionAltName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D14F7A2538634900B92428 /* X509ExtensionAltName.swift */; }; 38 | 41D14FE31F58C15A0058DE7A /* PKCS7.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D14FE21F58C1590058DE7A /* PKCS7.swift */; }; 39 | 41ECF197230985DC00588953 /* ASN1Decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 41ECF195230985DC00588953 /* ASN1Decoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 40 | 41ECF1DE23099DD900588953 /* ASN1Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E521F55C6830099110C /* ASN1Decoder.swift */; }; 41 | 41ECF1DF23099DD900588953 /* ASN1Identifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E531F55C6830099110C /* ASN1Identifier.swift */; }; 42 | 41ECF1E023099DD900588953 /* ASN1Object.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E541F55C6830099110C /* ASN1Object.swift */; }; 43 | 41ECF1E123099DD900588953 /* X509Certificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 412A9E551F55C6830099110C /* X509Certificate.swift */; }; 44 | 41ECF1E223099DD900588953 /* PKCS7.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41D14FE21F58C1590058DE7A /* PKCS7.swift */; }; 45 | 41ECF1E323099DD900588953 /* PKCS7_AppleReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41B11CD0202E16060009670B /* PKCS7_AppleReceipt.swift */; }; 46 | 78312B2424C8C8DB008EB688 /* PKCS7_Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78312B2324C8C8DB008EB688 /* PKCS7_Signature.swift */; }; 47 | 78312B2524C8C8DB008EB688 /* PKCS7_Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78312B2324C8C8DB008EB688 /* PKCS7_Signature.swift */; }; 48 | 78312B2724C8C8ED008EB688 /* ASN1DecoderPkcs7SignatureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78312B2624C8C8ED008EB688 /* ASN1DecoderPkcs7SignatureTests.swift */; }; 49 | /* End PBXBuildFile section */ 50 | 51 | /* Begin PBXContainerItemProxy section */ 52 | 412A9E431F55C6500099110C /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 412A9E2F1F55C6500099110C /* Project object */; 55 | proxyType = 1; 56 | remoteGlobalIDString = 412A9E371F55C6500099110C; 57 | remoteInfo = ASN1Decoder; 58 | }; 59 | /* End PBXContainerItemProxy section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | 411C71E22393E64600857C13 /* OID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OID.swift; sourceTree = ""; }; 63 | 412A9E381F55C6500099110C /* ASN1Decoder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ASN1Decoder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 412A9E3B1F55C6500099110C /* ASN1Decoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASN1Decoder.h; sourceTree = ""; }; 65 | 412A9E3C1F55C6500099110C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | 412A9E411F55C6500099110C /* ASN1DecoderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ASN1DecoderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | 412A9E461F55C6500099110C /* ASN1DecoderX509DecoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DecoderX509DecoderTests.swift; sourceTree = ""; }; 68 | 412A9E481F55C6500099110C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 412A9E521F55C6830099110C /* ASN1Decoder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Decoder.swift; sourceTree = ""; }; 70 | 412A9E531F55C6830099110C /* ASN1Identifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Identifier.swift; sourceTree = ""; }; 71 | 412A9E541F55C6830099110C /* ASN1Object.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1Object.swift; sourceTree = ""; }; 72 | 412A9E551F55C6830099110C /* X509Certificate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = X509Certificate.swift; sourceTree = ""; }; 73 | 413610EA254B261700452768 /* ASN1Encoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1Encoder.swift; sourceTree = ""; }; 74 | 413CB0282AB78CB200FD5FEA /* ASN1DecoderX509SignatureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DecoderX509SignatureTests.swift; sourceTree = ""; }; 75 | 413CB02C2AB78F0600FD5FEA /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; 76 | 41409EED25536A6C0048709B /* X509ExtensionClasses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509ExtensionClasses.swift; sourceTree = ""; }; 77 | 41409EF325536B300048709B /* ASN1DecoderExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DecoderExtensions.swift; sourceTree = ""; }; 78 | 414F5E2C1F55EFB700449E72 /* ASN1Decoder.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = ASN1Decoder.podspec; sourceTree = ""; }; 79 | 4168884B268B294300F40E7C /* ASN1DecoderCertificateV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DecoderCertificateV1.swift; sourceTree = ""; }; 80 | 41A2003223661989005EC955 /* X509PublicKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509PublicKey.swift; sourceTree = ""; }; 81 | 41A2003423661A0D005EC955 /* X509Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509Extension.swift; sourceTree = ""; }; 82 | 41A2003823661DA8005EC955 /* ASN1DistinguishedNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DistinguishedNames.swift; sourceTree = ""; }; 83 | 41B11CD0202E16060009670B /* PKCS7_AppleReceipt.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7_AppleReceipt.swift; sourceTree = ""; }; 84 | 41B703C3239D2EC600904B6B /* ASN1AppleReceiptTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1AppleReceiptTest.swift; sourceTree = ""; }; 85 | 41D14F722538608E00B92428 /* ASN1DecoderAlternativeNames.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ASN1DecoderAlternativeNames.swift; sourceTree = ""; }; 86 | 41D14F7A2538634900B92428 /* X509ExtensionAltName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = X509ExtensionAltName.swift; sourceTree = ""; }; 87 | 41D14FE21F58C1590058DE7A /* PKCS7.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7.swift; sourceTree = ""; }; 88 | 41ECF193230985DC00588953 /* ASN1Decoder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ASN1Decoder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 89 | 41ECF195230985DC00588953 /* ASN1Decoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASN1Decoder.h; sourceTree = ""; }; 90 | 41ECF196230985DC00588953 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 91 | 78312B2324C8C8DB008EB688 /* PKCS7_Signature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PKCS7_Signature.swift; sourceTree = ""; }; 92 | 78312B2624C8C8ED008EB688 /* ASN1DecoderPkcs7SignatureTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ASN1DecoderPkcs7SignatureTests.swift; sourceTree = ""; }; 93 | /* End PBXFileReference section */ 94 | 95 | /* Begin PBXFrameworksBuildPhase section */ 96 | 412A9E341F55C6500099110C /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | ); 101 | runOnlyForDeploymentPostprocessing = 0; 102 | }; 103 | 412A9E3E1F55C6500099110C /* Frameworks */ = { 104 | isa = PBXFrameworksBuildPhase; 105 | buildActionMask = 2147483647; 106 | files = ( 107 | 412A9E421F55C6500099110C /* ASN1Decoder.framework in Frameworks */, 108 | ); 109 | runOnlyForDeploymentPostprocessing = 0; 110 | }; 111 | 41ECF190230985DC00588953 /* Frameworks */ = { 112 | isa = PBXFrameworksBuildPhase; 113 | buildActionMask = 2147483647; 114 | files = ( 115 | ); 116 | runOnlyForDeploymentPostprocessing = 0; 117 | }; 118 | /* End PBXFrameworksBuildPhase section */ 119 | 120 | /* Begin PBXGroup section */ 121 | 412A9E2E1F55C6500099110C = { 122 | isa = PBXGroup; 123 | children = ( 124 | 412A9E3A1F55C6500099110C /* ASN1Decoder */, 125 | 412A9E451F55C6500099110C /* ASN1DecoderTests */, 126 | 41ECF194230985DC00588953 /* ASN1Decoder macOS */, 127 | 412A9E391F55C6500099110C /* Products */, 128 | 414F5E2C1F55EFB700449E72 /* ASN1Decoder.podspec */, 129 | ); 130 | sourceTree = ""; 131 | }; 132 | 412A9E391F55C6500099110C /* Products */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 412A9E381F55C6500099110C /* ASN1Decoder.framework */, 136 | 412A9E411F55C6500099110C /* ASN1DecoderTests.xctest */, 137 | 41ECF193230985DC00588953 /* ASN1Decoder.framework */, 138 | ); 139 | name = Products; 140 | sourceTree = ""; 141 | }; 142 | 412A9E3A1F55C6500099110C /* ASN1Decoder */ = { 143 | isa = PBXGroup; 144 | children = ( 145 | 412A9E3B1F55C6500099110C /* ASN1Decoder.h */, 146 | 412A9E521F55C6830099110C /* ASN1Decoder.swift */, 147 | 413610EA254B261700452768 /* ASN1Encoder.swift */, 148 | 412A9E531F55C6830099110C /* ASN1Identifier.swift */, 149 | 412A9E541F55C6830099110C /* ASN1Object.swift */, 150 | 41A2003823661DA8005EC955 /* ASN1DistinguishedNames.swift */, 151 | 411C71E22393E64600857C13 /* OID.swift */, 152 | 412A9E551F55C6830099110C /* X509Certificate.swift */, 153 | 41A2003223661989005EC955 /* X509PublicKey.swift */, 154 | 41A2003423661A0D005EC955 /* X509Extension.swift */, 155 | 41409EED25536A6C0048709B /* X509ExtensionClasses.swift */, 156 | 41D14F7A2538634900B92428 /* X509ExtensionAltName.swift */, 157 | 41D14FE21F58C1590058DE7A /* PKCS7.swift */, 158 | 41B11CD0202E16060009670B /* PKCS7_AppleReceipt.swift */, 159 | 78312B2324C8C8DB008EB688 /* PKCS7_Signature.swift */, 160 | 412A9E3C1F55C6500099110C /* Info.plist */, 161 | ); 162 | path = ASN1Decoder; 163 | sourceTree = ""; 164 | }; 165 | 412A9E451F55C6500099110C /* ASN1DecoderTests */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 412A9E461F55C6500099110C /* ASN1DecoderX509DecoderTests.swift */, 169 | 413CB0282AB78CB200FD5FEA /* ASN1DecoderX509SignatureTests.swift */, 170 | 41D14F722538608E00B92428 /* ASN1DecoderAlternativeNames.swift */, 171 | 41409EF325536B300048709B /* ASN1DecoderExtensions.swift */, 172 | 4168884B268B294300F40E7C /* ASN1DecoderCertificateV1.swift */, 173 | 78312B2624C8C8ED008EB688 /* ASN1DecoderPkcs7SignatureTests.swift */, 174 | 41B703C3239D2EC600904B6B /* ASN1AppleReceiptTest.swift */, 175 | 413CB02C2AB78F0600FD5FEA /* Extensions.swift */, 176 | 412A9E481F55C6500099110C /* Info.plist */, 177 | ); 178 | path = ASN1DecoderTests; 179 | sourceTree = ""; 180 | }; 181 | 41ECF194230985DC00588953 /* ASN1Decoder macOS */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | 41ECF195230985DC00588953 /* ASN1Decoder.h */, 185 | 41ECF196230985DC00588953 /* Info.plist */, 186 | ); 187 | path = "ASN1Decoder macOS"; 188 | sourceTree = ""; 189 | }; 190 | /* End PBXGroup section */ 191 | 192 | /* Begin PBXHeadersBuildPhase section */ 193 | 412A9E351F55C6500099110C /* Headers */ = { 194 | isa = PBXHeadersBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 412A9E491F55C6500099110C /* ASN1Decoder.h in Headers */, 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | }; 201 | 41ECF18E230985DC00588953 /* Headers */ = { 202 | isa = PBXHeadersBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | 41ECF197230985DC00588953 /* ASN1Decoder.h in Headers */, 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXHeadersBuildPhase section */ 210 | 211 | /* Begin PBXNativeTarget section */ 212 | 412A9E371F55C6500099110C /* ASN1Decoder */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = 412A9E4C1F55C6500099110C /* Build configuration list for PBXNativeTarget "ASN1Decoder" */; 215 | buildPhases = ( 216 | 412A9E331F55C6500099110C /* Sources */, 217 | 412A9E341F55C6500099110C /* Frameworks */, 218 | 412A9E351F55C6500099110C /* Headers */, 219 | 412A9E361F55C6500099110C /* Resources */, 220 | 415CDBF22370B8FC0029A0F4 /* ShellScript */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | ); 226 | name = ASN1Decoder; 227 | productName = ASN1Decoder; 228 | productReference = 412A9E381F55C6500099110C /* ASN1Decoder.framework */; 229 | productType = "com.apple.product-type.framework"; 230 | }; 231 | 412A9E401F55C6500099110C /* ASN1DecoderTests */ = { 232 | isa = PBXNativeTarget; 233 | buildConfigurationList = 412A9E4F1F55C6500099110C /* Build configuration list for PBXNativeTarget "ASN1DecoderTests" */; 234 | buildPhases = ( 235 | 412A9E3D1F55C6500099110C /* Sources */, 236 | 412A9E3E1F55C6500099110C /* Frameworks */, 237 | 412A9E3F1F55C6500099110C /* Resources */, 238 | ); 239 | buildRules = ( 240 | ); 241 | dependencies = ( 242 | 412A9E441F55C6500099110C /* PBXTargetDependency */, 243 | ); 244 | name = ASN1DecoderTests; 245 | productName = ASN1DecoderTests; 246 | productReference = 412A9E411F55C6500099110C /* ASN1DecoderTests.xctest */; 247 | productType = "com.apple.product-type.bundle.unit-test"; 248 | }; 249 | 41ECF192230985DC00588953 /* ASN1Decoder macOS */ = { 250 | isa = PBXNativeTarget; 251 | buildConfigurationList = 41ECF19A230985DC00588953 /* Build configuration list for PBXNativeTarget "ASN1Decoder macOS" */; 252 | buildPhases = ( 253 | 41ECF18E230985DC00588953 /* Headers */, 254 | 41ECF18F230985DC00588953 /* Sources */, 255 | 41ECF190230985DC00588953 /* Frameworks */, 256 | 41ECF191230985DC00588953 /* Resources */, 257 | ); 258 | buildRules = ( 259 | ); 260 | dependencies = ( 261 | ); 262 | name = "ASN1Decoder macOS"; 263 | productName = "ASN1Decoder macOS"; 264 | productReference = 41ECF193230985DC00588953 /* ASN1Decoder.framework */; 265 | productType = "com.apple.product-type.framework"; 266 | }; 267 | /* End PBXNativeTarget section */ 268 | 269 | /* Begin PBXProject section */ 270 | 412A9E2F1F55C6500099110C /* Project object */ = { 271 | isa = PBXProject; 272 | attributes = { 273 | LastSwiftUpdateCheck = 0830; 274 | LastUpgradeCheck = 1020; 275 | ORGANIZATIONNAME = "Filippo Maguolo"; 276 | TargetAttributes = { 277 | 412A9E371F55C6500099110C = { 278 | CreatedOnToolsVersion = 8.3.3; 279 | DevelopmentTeam = YS96L92VL6; 280 | LastSwiftMigration = 1020; 281 | ProvisioningStyle = Automatic; 282 | }; 283 | 412A9E401F55C6500099110C = { 284 | CreatedOnToolsVersion = 8.3.3; 285 | DevelopmentTeam = YS96L92VL6; 286 | LastSwiftMigration = 1020; 287 | ProvisioningStyle = Automatic; 288 | }; 289 | 41ECF192230985DC00588953 = { 290 | CreatedOnToolsVersion = 10.3; 291 | DevelopmentTeam = YS96L92VL6; 292 | ProvisioningStyle = Automatic; 293 | }; 294 | }; 295 | }; 296 | buildConfigurationList = 412A9E321F55C6500099110C /* Build configuration list for PBXProject "ASN1Decoder" */; 297 | compatibilityVersion = "Xcode 3.2"; 298 | developmentRegion = en; 299 | hasScannedForEncodings = 0; 300 | knownRegions = ( 301 | en, 302 | Base, 303 | ); 304 | mainGroup = 412A9E2E1F55C6500099110C; 305 | productRefGroup = 412A9E391F55C6500099110C /* Products */; 306 | projectDirPath = ""; 307 | projectRoot = ""; 308 | targets = ( 309 | 412A9E371F55C6500099110C /* ASN1Decoder */, 310 | 41ECF192230985DC00588953 /* ASN1Decoder macOS */, 311 | 412A9E401F55C6500099110C /* ASN1DecoderTests */, 312 | ); 313 | }; 314 | /* End PBXProject section */ 315 | 316 | /* Begin PBXResourcesBuildPhase section */ 317 | 412A9E361F55C6500099110C /* Resources */ = { 318 | isa = PBXResourcesBuildPhase; 319 | buildActionMask = 2147483647; 320 | files = ( 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | 412A9E3F1F55C6500099110C /* Resources */ = { 325 | isa = PBXResourcesBuildPhase; 326 | buildActionMask = 2147483647; 327 | files = ( 328 | ); 329 | runOnlyForDeploymentPostprocessing = 0; 330 | }; 331 | 41ECF191230985DC00588953 /* Resources */ = { 332 | isa = PBXResourcesBuildPhase; 333 | buildActionMask = 2147483647; 334 | files = ( 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | /* End PBXResourcesBuildPhase section */ 339 | 340 | /* Begin PBXShellScriptBuildPhase section */ 341 | 415CDBF22370B8FC0029A0F4 /* ShellScript */ = { 342 | isa = PBXShellScriptBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | ); 346 | inputFileListPaths = ( 347 | ); 348 | inputPaths = ( 349 | ); 350 | outputFileListPaths = ( 351 | ); 352 | outputPaths = ( 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | shellPath = /bin/sh; 356 | shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 357 | }; 358 | /* End PBXShellScriptBuildPhase section */ 359 | 360 | /* Begin PBXSourcesBuildPhase section */ 361 | 412A9E331F55C6500099110C /* Sources */ = { 362 | isa = PBXSourcesBuildPhase; 363 | buildActionMask = 2147483647; 364 | files = ( 365 | 41D14FE31F58C15A0058DE7A /* PKCS7.swift in Sources */, 366 | 78312B2424C8C8DB008EB688 /* PKCS7_Signature.swift in Sources */, 367 | 41D14F7B2538638F00B92428 /* X509ExtensionAltName.swift in Sources */, 368 | 41A2003523661A0D005EC955 /* X509Extension.swift in Sources */, 369 | 412A9E581F55C6830099110C /* ASN1Object.swift in Sources */, 370 | 41B11CD1202E16060009670B /* PKCS7_AppleReceipt.swift in Sources */, 371 | 41409EEE25536A6C0048709B /* X509ExtensionClasses.swift in Sources */, 372 | 413610EB254B261700452768 /* ASN1Encoder.swift in Sources */, 373 | 411C71E32393E64600857C13 /* OID.swift in Sources */, 374 | 41A2003323661989005EC955 /* X509PublicKey.swift in Sources */, 375 | 412A9E591F55C6830099110C /* X509Certificate.swift in Sources */, 376 | 41A2003923661DA8005EC955 /* ASN1DistinguishedNames.swift in Sources */, 377 | 412A9E561F55C6830099110C /* ASN1Decoder.swift in Sources */, 378 | 412A9E571F55C6830099110C /* ASN1Identifier.swift in Sources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | 412A9E3D1F55C6500099110C /* Sources */ = { 383 | isa = PBXSourcesBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | 41B703C4239D2EC600904B6B /* ASN1AppleReceiptTest.swift in Sources */, 387 | 413CB0292AB78CB200FD5FEA /* ASN1DecoderX509SignatureTests.swift in Sources */, 388 | 41409EF425536B300048709B /* ASN1DecoderExtensions.swift in Sources */, 389 | 413CB02D2AB78F0600FD5FEA /* Extensions.swift in Sources */, 390 | 4168884C268B294300F40E7C /* ASN1DecoderCertificateV1.swift in Sources */, 391 | 41D14F732538608E00B92428 /* ASN1DecoderAlternativeNames.swift in Sources */, 392 | 78312B2724C8C8ED008EB688 /* ASN1DecoderPkcs7SignatureTests.swift in Sources */, 393 | 412A9E471F55C6500099110C /* ASN1DecoderX509DecoderTests.swift in Sources */, 394 | ); 395 | runOnlyForDeploymentPostprocessing = 0; 396 | }; 397 | 41ECF18F230985DC00588953 /* Sources */ = { 398 | isa = PBXSourcesBuildPhase; 399 | buildActionMask = 2147483647; 400 | files = ( 401 | 41ECF1E223099DD900588953 /* PKCS7.swift in Sources */, 402 | 78312B2524C8C8DB008EB688 /* PKCS7_Signature.swift in Sources */, 403 | 41D14F7F2538639000B92428 /* X509ExtensionAltName.swift in Sources */, 404 | 41ECF1E023099DD900588953 /* ASN1Object.swift in Sources */, 405 | 41A2003623661A31005EC955 /* X509Extension.swift in Sources */, 406 | 41ECF1E323099DD900588953 /* PKCS7_AppleReceipt.swift in Sources */, 407 | 41409EEF25536A6C0048709B /* X509ExtensionClasses.swift in Sources */, 408 | 413610EF254B2A7800452768 /* ASN1Encoder.swift in Sources */, 409 | 411C71E42393E64F00857C13 /* OID.swift in Sources */, 410 | 41A2003723661A34005EC955 /* X509PublicKey.swift in Sources */, 411 | 41ECF1E123099DD900588953 /* X509Certificate.swift in Sources */, 412 | 41A2003A23661DAB005EC955 /* ASN1DistinguishedNames.swift in Sources */, 413 | 41ECF1DE23099DD900588953 /* ASN1Decoder.swift in Sources */, 414 | 41ECF1DF23099DD900588953 /* ASN1Identifier.swift in Sources */, 415 | ); 416 | runOnlyForDeploymentPostprocessing = 0; 417 | }; 418 | /* End PBXSourcesBuildPhase section */ 419 | 420 | /* Begin PBXTargetDependency section */ 421 | 412A9E441F55C6500099110C /* PBXTargetDependency */ = { 422 | isa = PBXTargetDependency; 423 | target = 412A9E371F55C6500099110C /* ASN1Decoder */; 424 | targetProxy = 412A9E431F55C6500099110C /* PBXContainerItemProxy */; 425 | }; 426 | /* End PBXTargetDependency section */ 427 | 428 | /* Begin XCBuildConfiguration section */ 429 | 412A9E4A1F55C6500099110C /* Debug */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | ALWAYS_SEARCH_USER_PATHS = NO; 433 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 434 | CLANG_ANALYZER_NONNULL = YES; 435 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 436 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 437 | CLANG_CXX_LIBRARY = "libc++"; 438 | CLANG_ENABLE_MODULES = YES; 439 | CLANG_ENABLE_OBJC_ARC = YES; 440 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 441 | CLANG_WARN_BOOL_CONVERSION = YES; 442 | CLANG_WARN_COMMA = YES; 443 | CLANG_WARN_CONSTANT_CONVERSION = YES; 444 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 445 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 446 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 447 | CLANG_WARN_EMPTY_BODY = YES; 448 | CLANG_WARN_ENUM_CONVERSION = YES; 449 | CLANG_WARN_INFINITE_RECURSION = YES; 450 | CLANG_WARN_INT_CONVERSION = YES; 451 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 452 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 453 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 454 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 455 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 456 | CLANG_WARN_STRICT_PROTOTYPES = YES; 457 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 458 | CLANG_WARN_UNREACHABLE_CODE = YES; 459 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 460 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 461 | COPY_PHASE_STRIP = NO; 462 | CURRENT_PROJECT_VERSION = 1; 463 | DEBUG_INFORMATION_FORMAT = dwarf; 464 | ENABLE_STRICT_OBJC_MSGSEND = YES; 465 | ENABLE_TESTABILITY = YES; 466 | GCC_C_LANGUAGE_STANDARD = gnu99; 467 | GCC_DYNAMIC_NO_PIC = NO; 468 | GCC_NO_COMMON_BLOCKS = YES; 469 | GCC_OPTIMIZATION_LEVEL = 0; 470 | GCC_PREPROCESSOR_DEFINITIONS = ( 471 | "DEBUG=1", 472 | "$(inherited)", 473 | ); 474 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 475 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 476 | GCC_WARN_UNDECLARED_SELECTOR = YES; 477 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 478 | GCC_WARN_UNUSED_FUNCTION = YES; 479 | GCC_WARN_UNUSED_VARIABLE = YES; 480 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 481 | MTL_ENABLE_DEBUG_INFO = YES; 482 | ONLY_ACTIVE_ARCH = YES; 483 | PRODUCT_NAME = ASN1Decoder; 484 | SDKROOT = iphoneos; 485 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 486 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 487 | TARGETED_DEVICE_FAMILY = "1,2"; 488 | VERSIONING_SYSTEM = "apple-generic"; 489 | VERSION_INFO_PREFIX = ""; 490 | }; 491 | name = Debug; 492 | }; 493 | 412A9E4B1F55C6500099110C /* Release */ = { 494 | isa = XCBuildConfiguration; 495 | buildSettings = { 496 | ALWAYS_SEARCH_USER_PATHS = NO; 497 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 498 | CLANG_ANALYZER_NONNULL = YES; 499 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 500 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 501 | CLANG_CXX_LIBRARY = "libc++"; 502 | CLANG_ENABLE_MODULES = YES; 503 | CLANG_ENABLE_OBJC_ARC = YES; 504 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 505 | CLANG_WARN_BOOL_CONVERSION = YES; 506 | CLANG_WARN_COMMA = YES; 507 | CLANG_WARN_CONSTANT_CONVERSION = YES; 508 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 509 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 510 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 511 | CLANG_WARN_EMPTY_BODY = YES; 512 | CLANG_WARN_ENUM_CONVERSION = YES; 513 | CLANG_WARN_INFINITE_RECURSION = YES; 514 | CLANG_WARN_INT_CONVERSION = YES; 515 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 516 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 517 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 518 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 519 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 520 | CLANG_WARN_STRICT_PROTOTYPES = YES; 521 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 522 | CLANG_WARN_UNREACHABLE_CODE = YES; 523 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 524 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 525 | COPY_PHASE_STRIP = NO; 526 | CURRENT_PROJECT_VERSION = 1; 527 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 528 | ENABLE_NS_ASSERTIONS = NO; 529 | ENABLE_STRICT_OBJC_MSGSEND = YES; 530 | GCC_C_LANGUAGE_STANDARD = gnu99; 531 | GCC_NO_COMMON_BLOCKS = YES; 532 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 533 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 534 | GCC_WARN_UNDECLARED_SELECTOR = YES; 535 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 536 | GCC_WARN_UNUSED_FUNCTION = YES; 537 | GCC_WARN_UNUSED_VARIABLE = YES; 538 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 539 | MTL_ENABLE_DEBUG_INFO = NO; 540 | PRODUCT_NAME = ASN1Decoder; 541 | SDKROOT = iphoneos; 542 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 543 | TARGETED_DEVICE_FAMILY = "1,2"; 544 | VALIDATE_PRODUCT = YES; 545 | VERSIONING_SYSTEM = "apple-generic"; 546 | VERSION_INFO_PREFIX = ""; 547 | }; 548 | name = Release; 549 | }; 550 | 412A9E4D1F55C6500099110C /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | CLANG_ENABLE_MODULES = YES; 554 | CODE_SIGN_IDENTITY = ""; 555 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 556 | CODE_SIGN_STYLE = Automatic; 557 | DEFINES_MODULE = YES; 558 | DEVELOPMENT_TEAM = YS96L92VL6; 559 | DYLIB_COMPATIBILITY_VERSION = 1; 560 | DYLIB_CURRENT_VERSION = 1; 561 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 562 | INFOPLIST_FILE = ASN1Decoder/Info.plist; 563 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 564 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 565 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 566 | PRODUCT_BUNDLE_IDENTIFIER = net.filippomaguolo.ASN1Decoder; 567 | PROVISIONING_PROFILE_SPECIFIER = ""; 568 | SKIP_INSTALL = YES; 569 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; 570 | SUPPORTS_MACCATALYST = YES; 571 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; 572 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 573 | SWIFT_VERSION = 5.0; 574 | TARGETED_DEVICE_FAMILY = "1,2,3"; 575 | TVOS_DEPLOYMENT_TARGET = 13.0; 576 | }; 577 | name = Debug; 578 | }; 579 | 412A9E4E1F55C6500099110C /* Release */ = { 580 | isa = XCBuildConfiguration; 581 | buildSettings = { 582 | CLANG_ENABLE_MODULES = YES; 583 | CODE_SIGN_IDENTITY = ""; 584 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 585 | CODE_SIGN_STYLE = Automatic; 586 | DEFINES_MODULE = YES; 587 | DEVELOPMENT_TEAM = YS96L92VL6; 588 | DYLIB_COMPATIBILITY_VERSION = 1; 589 | DYLIB_CURRENT_VERSION = 1; 590 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 591 | INFOPLIST_FILE = ASN1Decoder/Info.plist; 592 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 593 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 594 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 595 | PRODUCT_BUNDLE_IDENTIFIER = net.filippomaguolo.ASN1Decoder; 596 | PROVISIONING_PROFILE_SPECIFIER = ""; 597 | SKIP_INSTALL = YES; 598 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; 599 | SUPPORTS_MACCATALYST = YES; 600 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; 601 | SWIFT_VERSION = 5.0; 602 | TARGETED_DEVICE_FAMILY = "1,2,3"; 603 | TVOS_DEPLOYMENT_TARGET = 13.0; 604 | }; 605 | name = Release; 606 | }; 607 | 412A9E501F55C6500099110C /* Debug */ = { 608 | isa = XCBuildConfiguration; 609 | buildSettings = { 610 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 611 | DEVELOPMENT_TEAM = YS96L92VL6; 612 | INFOPLIST_FILE = ASN1DecoderTests/Info.plist; 613 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 614 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 615 | PRODUCT_BUNDLE_IDENTIFIER = net.filippomaguolo.ASN1DecoderTests; 616 | PRODUCT_NAME = "$(TARGET_NAME)"; 617 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; 618 | SUPPORTS_MACCATALYST = YES; 619 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; 620 | SWIFT_VERSION = 5.0; 621 | TARGETED_DEVICE_FAMILY = "1,2,3"; 622 | TVOS_DEPLOYMENT_TARGET = 13.0; 623 | }; 624 | name = Debug; 625 | }; 626 | 412A9E511F55C6500099110C /* Release */ = { 627 | isa = XCBuildConfiguration; 628 | buildSettings = { 629 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 630 | DEVELOPMENT_TEAM = YS96L92VL6; 631 | INFOPLIST_FILE = ASN1DecoderTests/Info.plist; 632 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 633 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 634 | PRODUCT_BUNDLE_IDENTIFIER = net.filippomaguolo.ASN1DecoderTests; 635 | PRODUCT_NAME = "$(TARGET_NAME)"; 636 | SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator"; 637 | SUPPORTS_MACCATALYST = YES; 638 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; 639 | SWIFT_VERSION = 5.0; 640 | TARGETED_DEVICE_FAMILY = "1,2,3"; 641 | TVOS_DEPLOYMENT_TARGET = 13.0; 642 | }; 643 | name = Release; 644 | }; 645 | 41ECF198230985DC00588953 /* Debug */ = { 646 | isa = XCBuildConfiguration; 647 | buildSettings = { 648 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 649 | CLANG_ENABLE_OBJC_WEAK = YES; 650 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 651 | CODE_SIGN_IDENTITY = "Mac Developer"; 652 | CODE_SIGN_STYLE = Automatic; 653 | COMBINE_HIDPI_IMAGES = YES; 654 | DEFINES_MODULE = YES; 655 | DEVELOPMENT_TEAM = YS96L92VL6; 656 | DYLIB_COMPATIBILITY_VERSION = 1; 657 | DYLIB_CURRENT_VERSION = 1; 658 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 659 | FRAMEWORK_VERSION = A; 660 | GCC_C_LANGUAGE_STANDARD = gnu11; 661 | INFOPLIST_FILE = "ASN1Decoder macOS/Info.plist"; 662 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 663 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 664 | MACOSX_DEPLOYMENT_TARGET = 10.13; 665 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 666 | MTL_FAST_MATH = YES; 667 | PRODUCT_BUNDLE_IDENTIFIER = net.filippomaguolo.ASN1Decoder; 668 | SDKROOT = macosx; 669 | SKIP_INSTALL = YES; 670 | SWIFT_VERSION = 5.0; 671 | }; 672 | name = Debug; 673 | }; 674 | 41ECF199230985DC00588953 /* Release */ = { 675 | isa = XCBuildConfiguration; 676 | buildSettings = { 677 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 678 | CLANG_ENABLE_OBJC_WEAK = YES; 679 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 680 | CODE_SIGN_IDENTITY = "Mac Developer"; 681 | CODE_SIGN_STYLE = Automatic; 682 | COMBINE_HIDPI_IMAGES = YES; 683 | DEFINES_MODULE = YES; 684 | DEVELOPMENT_TEAM = YS96L92VL6; 685 | DYLIB_COMPATIBILITY_VERSION = 1; 686 | DYLIB_CURRENT_VERSION = 1; 687 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 688 | FRAMEWORK_VERSION = A; 689 | GCC_C_LANGUAGE_STANDARD = gnu11; 690 | INFOPLIST_FILE = "ASN1Decoder macOS/Info.plist"; 691 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 692 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 693 | MACOSX_DEPLOYMENT_TARGET = 10.13; 694 | MTL_FAST_MATH = YES; 695 | PRODUCT_BUNDLE_IDENTIFIER = net.filippomaguolo.ASN1Decoder; 696 | SDKROOT = macosx; 697 | SKIP_INSTALL = YES; 698 | SWIFT_VERSION = 5.0; 699 | }; 700 | name = Release; 701 | }; 702 | /* End XCBuildConfiguration section */ 703 | 704 | /* Begin XCConfigurationList section */ 705 | 412A9E321F55C6500099110C /* Build configuration list for PBXProject "ASN1Decoder" */ = { 706 | isa = XCConfigurationList; 707 | buildConfigurations = ( 708 | 412A9E4A1F55C6500099110C /* Debug */, 709 | 412A9E4B1F55C6500099110C /* Release */, 710 | ); 711 | defaultConfigurationIsVisible = 0; 712 | defaultConfigurationName = Release; 713 | }; 714 | 412A9E4C1F55C6500099110C /* Build configuration list for PBXNativeTarget "ASN1Decoder" */ = { 715 | isa = XCConfigurationList; 716 | buildConfigurations = ( 717 | 412A9E4D1F55C6500099110C /* Debug */, 718 | 412A9E4E1F55C6500099110C /* Release */, 719 | ); 720 | defaultConfigurationIsVisible = 0; 721 | defaultConfigurationName = Release; 722 | }; 723 | 412A9E4F1F55C6500099110C /* Build configuration list for PBXNativeTarget "ASN1DecoderTests" */ = { 724 | isa = XCConfigurationList; 725 | buildConfigurations = ( 726 | 412A9E501F55C6500099110C /* Debug */, 727 | 412A9E511F55C6500099110C /* Release */, 728 | ); 729 | defaultConfigurationIsVisible = 0; 730 | defaultConfigurationName = Release; 731 | }; 732 | 41ECF19A230985DC00588953 /* Build configuration list for PBXNativeTarget "ASN1Decoder macOS" */ = { 733 | isa = XCConfigurationList; 734 | buildConfigurations = ( 735 | 41ECF198230985DC00588953 /* Debug */, 736 | 41ECF199230985DC00588953 /* Release */, 737 | ); 738 | defaultConfigurationIsVisible = 0; 739 | defaultConfigurationName = Release; 740 | }; 741 | /* End XCConfigurationList section */ 742 | }; 743 | rootObject = 412A9E2F1F55C6500099110C /* Project object */; 744 | } 745 | -------------------------------------------------------------------------------- /ASN1Decoder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ASN1Decoder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ASN1Decoder.xcodeproj/xcshareddata/xcschemes/ASN1Decoder macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ASN1Decoder.xcodeproj/xcshareddata/xcschemes/ASN1Decoder.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ASN1Decoder/ASN1Decoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Decoder.h 3 | // ASN1Decoder 4 | // 5 | // Created by Filippo Maguolo on 8/29/17. 6 | // Copyright © 2017 Filippo Maguolo. All rights reserved. 7 | // 8 | 9 | -------------------------------------------------------------------------------- /ASN1Decoder/ASN1Decoder.swift: -------------------------------------------------------------------------------- 1 | // swiftlint:disable function_body_length 2 | // 3 | // ASN1DERDecoder.swift 4 | // 5 | // Copyright © 2017 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public class ASN1DERDecoder { 28 | 29 | public static func decode(data: Data) throws -> [ASN1Object] { 30 | var iterator = data.makeIterator() 31 | return try parse(iterator: &iterator) 32 | } 33 | 34 | private static func parse(iterator: inout Data.Iterator) throws -> [ASN1Object] { 35 | 36 | var result: [ASN1Object] = [] 37 | 38 | while let nextValue = iterator.next() { 39 | 40 | let asn1obj = ASN1Object() 41 | asn1obj.identifier = ASN1Identifier(rawValue: nextValue) 42 | 43 | if asn1obj.identifier!.isConstructed() { 44 | 45 | let contentData = try loadSubContent(iterator: &iterator) 46 | 47 | if contentData.isEmpty { 48 | asn1obj.sub = try parse(iterator: &iterator) 49 | } else { 50 | var subIterator = contentData.makeIterator() 51 | asn1obj.sub = try parse(iterator: &subIterator) 52 | } 53 | 54 | asn1obj.value = nil 55 | 56 | asn1obj.rawValue = Data(contentData) 57 | 58 | for item in asn1obj.sub! { 59 | item.parent = asn1obj 60 | } 61 | } else { 62 | 63 | if asn1obj.identifier!.typeClass() == .universal { 64 | 65 | var contentData = try loadSubContent(iterator: &iterator) 66 | 67 | asn1obj.rawValue = Data(contentData) 68 | 69 | // decode the content data with come more convenient format 70 | 71 | switch asn1obj.identifier!.tagNumber() { 72 | 73 | case .endOfContent: 74 | return result 75 | 76 | case .boolean: 77 | if let value = contentData.first { 78 | asn1obj.value = value > 0 ? true : false 79 | 80 | } 81 | 82 | case .integer: 83 | while contentData.first == 0 { 84 | contentData.remove(at: 0) // remove not significant digit 85 | } 86 | asn1obj.value = contentData 87 | 88 | case .null: 89 | asn1obj.value = nil 90 | 91 | case .objectIdentifier: 92 | asn1obj.value = decodeOid(contentData: &contentData) 93 | 94 | case .utf8String, 95 | .printableString, 96 | .numericString, 97 | .generalString, 98 | .universalString, 99 | .characterString, 100 | .t61String: 101 | 102 | asn1obj.value = String(data: contentData, encoding: .utf8) 103 | 104 | case .bmpString: 105 | asn1obj.value = String(data: contentData, encoding: .unicode) 106 | 107 | case .visibleString, 108 | .ia5String: 109 | 110 | asn1obj.value = String(data: contentData, encoding: .ascii) 111 | 112 | case .utcTime: 113 | asn1obj.value = dateFormatter(contentData: &contentData, 114 | formats: ["yyMMddHHmmssZ", "yyMMddHHmmZ"]) 115 | 116 | case .generalizedTime: 117 | asn1obj.value = dateFormatter(contentData: &contentData, 118 | formats: ["yyyyMMddHHmmssZ"]) 119 | 120 | case .bitString: 121 | if contentData.count > 0 { 122 | _ = contentData.remove(at: 0) // unused bits 123 | } 124 | asn1obj.value = contentData 125 | 126 | case .octetString: 127 | do { 128 | var subIterator = contentData.makeIterator() 129 | asn1obj.sub = try parse(iterator: &subIterator) 130 | } catch { 131 | if let str = String(data: contentData, encoding: .utf8) { 132 | asn1obj.value = str 133 | } else { 134 | asn1obj.value = contentData 135 | } 136 | } 137 | 138 | default: 139 | print("unsupported tag: \(asn1obj.identifier!.tagNumber())") 140 | asn1obj.value = contentData 141 | } 142 | } else { 143 | // custom/private tag 144 | 145 | let contentData = try loadSubContent(iterator: &iterator) 146 | asn1obj.rawValue = Data(contentData) 147 | 148 | if let str = String(data: contentData, encoding: .utf8) { 149 | asn1obj.value = str 150 | } else { 151 | asn1obj.value = contentData 152 | } 153 | } 154 | } 155 | result.append(asn1obj) 156 | } 157 | return result 158 | } 159 | 160 | // Decode DER OID bytes to String with dot notation 161 | static func decodeOid(contentData: inout Data) -> String? { 162 | if contentData.isEmpty { 163 | return nil 164 | } 165 | 166 | var oid: String = "" 167 | 168 | let first = Int(contentData.remove(at: 0)) 169 | oid.append("\(first / 40).\(first % 40)") 170 | 171 | var t = 0 172 | while contentData.count > 0 { 173 | let n = Int(contentData.remove(at: 0)) 174 | t = (t << 7) | (n & 0x7F) 175 | if (n & 0x80) == 0 { 176 | oid.append(".\(t)") 177 | t = 0 178 | } 179 | } 180 | return oid 181 | } 182 | 183 | private static func dateFormatter(contentData: inout Data, formats: [String]) -> Date? { 184 | guard let str = String(data: contentData, encoding: .utf8) else { return nil } 185 | for format in formats { 186 | let fmt = DateFormatter() 187 | fmt.locale = Locale(identifier: "en_US_POSIX") 188 | fmt.dateFormat = format 189 | if let dt = fmt.date(from: str) { 190 | return dt 191 | } 192 | } 193 | return nil 194 | } 195 | } 196 | 197 | enum ASN1Error: Error { 198 | case parseError 199 | case outOfBuffer 200 | } 201 | 202 | extension Data { 203 | public var uint64Value: UInt64? { 204 | guard count <= 8, !isEmpty else { // check if suitable for UInt64 205 | return nil 206 | } 207 | 208 | var value: UInt64 = 0 209 | for (index, byte) in self.enumerated() { 210 | value += UInt64(byte) << UInt64(8*(count-index-1)) 211 | } 212 | return value 213 | } 214 | } 215 | 216 | extension Data { 217 | public var sequenceContent: Data { 218 | var iterator = self.makeIterator() 219 | _ = iterator.next() 220 | do { 221 | return try loadSubContent(iterator: &iterator) 222 | } catch { 223 | return self 224 | } 225 | } 226 | } 227 | 228 | // Decode the number of bytes of the content 229 | private func getContentLength(iterator: inout Data.Iterator) -> UInt64 { 230 | let first = iterator.next() 231 | 232 | guard first != nil else { 233 | return 0 234 | } 235 | 236 | if (first! & 0x80) != 0 { // long 237 | let octetsToRead = first! - 0x80 238 | var data = Data() 239 | for _ in 0.. Data { 253 | 254 | let len = getContentLength(iterator: &iterator) 255 | 256 | guard len < Int.max else { 257 | return Data() 258 | } 259 | 260 | var byteArray: [UInt8] = [] 261 | 262 | for _ in 0.. String? { 57 | var result: String? 58 | for sub in block.sub ?? [] { 59 | if let subOid = sub.sub(0)?.sub(0), subOid.identifier?.tagNumber() == .objectIdentifier, 60 | let oidString = subOid.value as? String, let value = sub.sub(0)?.sub(1)?.value as? String { 61 | if result == nil { 62 | result = "" 63 | } else { 64 | result?.append(separator) 65 | } 66 | if let oid = OID(rawValue: oidString) { 67 | if let representation = shortRepresentation(oid: oid) { 68 | result?.append(representation) 69 | } else { 70 | result?.append("\(oid)") 71 | } 72 | } else { 73 | result?.append(oidString) 74 | } 75 | result?.append("=") 76 | result?.append(quote(string: value)) 77 | } 78 | } 79 | return result 80 | } 81 | 82 | class func quote(string: String) -> String { 83 | let specialChar = ",+=\n<>#;\\" 84 | if string.contains(where: { specialChar.contains($0) }) { 85 | return "\"" + string + "\"" 86 | } else { 87 | return string 88 | } 89 | } 90 | 91 | class func shortRepresentation(oid: OID) -> String? { 92 | switch oid { 93 | case .commonName: return "CN" 94 | case .dnQualifier: return "DNQ" 95 | case .serialNumber: return "SERIALNUMBER" 96 | case .givenName: return "GIVENNAME" 97 | case .surname: return "SURNAME" 98 | case .organizationalUnitName: return "OU" 99 | case .organizationName: return "O" 100 | case .streetAddress: return "STREET" 101 | case .localityName: return "L" 102 | case .stateOrProvinceName: return "ST" 103 | case .countryName: return "C" 104 | case .emailAddress: return "E" 105 | case .domainComponent: return "DC" 106 | case .jurisdictionLocalityName: return "jurisdictionL" 107 | case .jurisdictionStateOrProvinceName: return "jurisdictionST" 108 | case .jurisdictionCountryName: return "jurisdictionC" 109 | default: return nil 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /ASN1Decoder/ASN1Encoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Encoder.swift 3 | // ASN1Decoder 4 | // 5 | // Copyright © 2020 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import Foundation 26 | 27 | public class ASN1DEREncoder { 28 | 29 | public static func encodeSequence(content: Data) -> Data { 30 | var encoded = Data() 31 | encoded.append(ASN1Identifier.constructedTag | ASN1Identifier.TagNumber.sequence.rawValue) 32 | encoded.append(contentLength(of: content.count)) 33 | encoded.append(content) 34 | return encoded 35 | } 36 | 37 | private static func contentLength(of size: Int) -> Data { 38 | if size >= 128 { 39 | var lenBytes = byteArray(from: size) 40 | while lenBytes.first == 0 { lenBytes.removeFirst() } 41 | let len: UInt8 = 0x80 | UInt8(lenBytes.count) 42 | return Data([len] + lenBytes) 43 | } else { 44 | return Data([UInt8(size)]) 45 | } 46 | } 47 | 48 | private static func byteArray(from value: T) -> [UInt8] where T: FixedWidthInteger { 49 | return withUnsafeBytes(of: value.bigEndian, Array.init) 50 | } 51 | 52 | } 53 | 54 | extension Data { 55 | public var derEncodedSequence: Data { 56 | return ASN1DEREncoder.encodeSequence(content: self) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ASN1Decoder/ASN1Identifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Identifier.swift 3 | // 4 | // Copyright © 2017 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class ASN1Identifier: CustomStringConvertible { 27 | 28 | public enum Class: UInt8 { 29 | case universal = 0x00 30 | case application = 0x40 31 | case contextSpecific = 0x80 32 | case `private` = 0xC0 33 | } 34 | 35 | public enum TagNumber: UInt8 { 36 | case endOfContent = 0x00 37 | case boolean = 0x01 38 | case integer = 0x02 39 | case bitString = 0x03 40 | case octetString = 0x04 41 | case null = 0x05 42 | case objectIdentifier = 0x06 43 | case objectDescriptor = 0x07 44 | case external = 0x08 45 | case read = 0x09 46 | case enumerated = 0x0A 47 | case embeddedPdv = 0x0B 48 | case utf8String = 0x0C 49 | case relativeOid = 0x0D 50 | case sequence = 0x10 51 | case set = 0x11 52 | case numericString = 0x12 53 | case printableString = 0x13 54 | case t61String = 0x14 55 | case videotexString = 0x15 56 | case ia5String = 0x16 57 | case utcTime = 0x17 58 | case generalizedTime = 0x18 59 | case graphicString = 0x19 60 | case visibleString = 0x1A 61 | case generalString = 0x1B 62 | case universalString = 0x1C 63 | case characterString = 0x1D 64 | case bmpString = 0x1E 65 | } 66 | 67 | public static let constructedTag: UInt8 = 0x20 68 | 69 | var rawValue: UInt8 70 | 71 | init(rawValue: UInt8) { 72 | self.rawValue = rawValue 73 | } 74 | 75 | public func typeClass() -> Class { 76 | for tc in [Class.application, Class.contextSpecific, Class.private] where (rawValue & tc.rawValue) == tc.rawValue { 77 | return tc 78 | } 79 | return .universal 80 | } 81 | 82 | public func isPrimitive() -> Bool { 83 | return (rawValue & ASN1Identifier.constructedTag) == 0 84 | } 85 | public func isConstructed() -> Bool { 86 | return (rawValue & ASN1Identifier.constructedTag) != 0 87 | } 88 | 89 | public func tagNumber() -> TagNumber { 90 | return TagNumber(rawValue: rawValue & 0x1F) ?? .endOfContent 91 | } 92 | 93 | public var description: String { 94 | if typeClass() == .universal { 95 | return String(describing: tagNumber()) 96 | } else { 97 | return "\(typeClass())(\(tagNumber().rawValue))" 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ASN1Decoder/ASN1Object.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1Object.swift 3 | // 4 | // Copyright © 2017 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class ASN1Object: CustomStringConvertible { 27 | 28 | /// This property contains the DER encoded object 29 | public var rawValue: Data? 30 | 31 | /// This property contains the decoded Swift object whenever is possible 32 | public var value: Any? 33 | 34 | public var identifier: ASN1Identifier? 35 | 36 | var sub: [ASN1Object]? 37 | 38 | public internal(set) weak var parent: ASN1Object? 39 | 40 | public func sub(_ index: Int) -> ASN1Object? { 41 | if let sub = self.sub, index >= 0, index < sub.count { 42 | return sub[index] 43 | } 44 | return nil 45 | } 46 | 47 | public func subCount() -> Int { 48 | return sub?.count ?? 0 49 | } 50 | 51 | public func findOid(_ oid: OID) -> ASN1Object? { 52 | return findOid(oid.rawValue) 53 | } 54 | 55 | public func findOid(_ oid: String) -> ASN1Object? { 56 | for child in sub ?? [] { 57 | if child.identifier?.tagNumber() == .objectIdentifier { 58 | if child.value as? String == oid { 59 | return child 60 | } 61 | } else { 62 | if let result = child.findOid(oid) { 63 | return result 64 | } 65 | } 66 | } 67 | return nil 68 | } 69 | 70 | public var description: String { 71 | return printAsn1() 72 | } 73 | 74 | public var asString: String? { 75 | if let string = value as? String { 76 | return string 77 | } 78 | 79 | for item in sub ?? [] { 80 | if let string = item.asString { 81 | return string 82 | } 83 | } 84 | 85 | return nil 86 | } 87 | 88 | fileprivate func printAsn1(insets: String = "") -> String { 89 | var output = insets 90 | output.append(identifier?.description.uppercased() ?? "") 91 | output.append(value != nil ? ": \(value!)": "") 92 | if identifier?.typeClass() == .universal, identifier?.tagNumber() == .objectIdentifier { 93 | if let oidName = OID.description(of: value as? String ?? "") { 94 | output.append(" (\(oidName))") 95 | } 96 | } 97 | output.append(sub != nil && sub!.count > 0 ? " {": "") 98 | output.append("\n") 99 | for item in sub ?? [] { 100 | output.append(item.printAsn1(insets: insets + " ")) 101 | } 102 | output.append(sub != nil && sub!.count > 0 ? insets + "}\n": "") 103 | return output 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ASN1Decoder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /ASN1Decoder/OID.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OID.swift 3 | // ASN1Decoder 4 | // 5 | // Created by Filippo Maguolo on 01/12/2019. 6 | // Copyright © 2019 Filippo Maguolo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum OID: String { 12 | case etsiQcsCompliance = "0.4.0.1862.1.1" 13 | case etsiQcsRetentionPeriod = "0.4.0.1862.1.3" 14 | case etsiQcsQcSSCD = "0.4.0.1862.1.4" 15 | case dsa = "1.2.840.10040.4.1" 16 | case ecPublicKey = "1.2.840.10045.2.1" 17 | case prime256v1 = "1.2.840.10045.3.1.7" 18 | case ecdsaWithSHA256 = "1.2.840.10045.4.3.2" 19 | case ecdsaWithSHA384 = "1.2.840.10045.4.3.3" 20 | case ecdsaWithSHA512 = "1.2.840.10045.4.3.4" 21 | case rsaEncryption = "1.2.840.113549.1.1.1" 22 | case rsaPSS = "1.2.840.113549.1.1.10" 23 | case sha256WithRSAEncryption = "1.2.840.113549.1.1.11" 24 | case md5WithRSAEncryption = "1.2.840.113549.1.1.4" 25 | case sha1WithRSAEncryption = "1.2.840.113549.1.1.5" 26 | 27 | // Digest algorithms 28 | case sha1 = "1.3.14.3.2.26" 29 | case pkcsSha256 = "1.3.6.1.4.1.22554.1.2.1" 30 | case sha2Family = "1.3.6.1.4.1.22554.1.2" 31 | case sha3_244 = "2.16.840.1.101.3.4.2.7" 32 | case sha3_256 = "2.16.840.1.101.3.4.2.8" 33 | case sha3_384 = "2.16.840.1.101.3.4.2.9" 34 | case md5 = "0.2.262.1.10.1.3.2" 35 | 36 | case pkcs7data = "1.2.840.113549.1.7.1" 37 | case pkcs7signedData = "1.2.840.113549.1.7.2" 38 | case pkcs7envelopedData = "1.2.840.113549.1.7.3" 39 | case emailAddress = "1.2.840.113549.1.9.1" 40 | case signingCertificateV2 = "1.2.840.113549.1.9.16.2.47" 41 | case contentType = "1.2.840.113549.1.9.3" 42 | case messageDigest = "1.2.840.113549.1.9.4" 43 | case signingTime = "1.2.840.113549.1.9.5" 44 | case certificateExtension = "1.3.6.1.4.1.11129.2.4.2" 45 | case jurisdictionLocalityName = "1.3.6.1.4.1.311.60.2.1.1" 46 | case jurisdictionStateOrProvinceName = "1.3.6.1.4.1.311.60.2.1.2" 47 | case jurisdictionCountryName = "1.3.6.1.4.1.311.60.2.1.3" 48 | case authorityInfoAccess = "1.3.6.1.5.5.7.1.1" 49 | case qcStatements = "1.3.6.1.5.5.7.1.3" 50 | case cps = "1.3.6.1.5.5.7.2.1" 51 | case unotice = "1.3.6.1.5.5.7.2.2" 52 | case serverAuth = "1.3.6.1.5.5.7.3.1" 53 | case clientAuth = "1.3.6.1.5.5.7.3.2" 54 | case ocsp = "1.3.6.1.5.5.7.48.1" 55 | case caIssuers = "1.3.6.1.5.5.7.48.2" 56 | case dateOfBirth = "1.3.6.1.5.5.7.9.1" 57 | case sha256 = "2.16.840.1.101.3.4.2.1" 58 | case VeriSignEVpolicy = "2.16.840.1.113733.1.7.23.6" 59 | case extendedValidation = "2.23.140.1.1" 60 | case organizationValidated = "2.23.140.1.2.2" 61 | case subjectKeyIdentifier = "2.5.29.14" 62 | case keyUsage = "2.5.29.15" 63 | case subjectAltName = "2.5.29.17" 64 | case issuerAltName = "2.5.29.18" 65 | case basicConstraints = "2.5.29.19" 66 | case cRLDistributionPoints = "2.5.29.31" 67 | case certificatePolicies = "2.5.29.32" 68 | case authorityKeyIdentifier = "2.5.29.35" 69 | case extKeyUsage = "2.5.29.37" 70 | case subjectDirectoryAttributes = "2.5.29.9" 71 | 72 | // X.500 attributes 73 | case commonName = "2.5.4.3" 74 | case surname = "2.5.4.4" 75 | case serialNumber = "2.5.4.5" 76 | case countryName = "2.5.4.6" 77 | case localityName = "2.5.4.7" 78 | case stateOrProvinceName = "2.5.4.8" 79 | case streetAddress = "2.5.4.9" 80 | case organizationName = "2.5.4.10" 81 | case organizationalUnitName = "2.5.4.11" 82 | case businessCategory = "2.5.4.15" 83 | case postalCode = "2.5.4.17" 84 | case givenName = "2.5.4.42" 85 | case dnQualifier = "2.5.4.46" 86 | 87 | case domainComponent = "0.9.2342.19200300.100.1.25" 88 | 89 | case userId = "0.9.2342.19200300.100.1.1" 90 | 91 | static func description(of value: String) -> String? { 92 | guard let oid = OID(rawValue: value) else { 93 | return nil 94 | } 95 | return "\(oid)" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ASN1Decoder/PKCS7.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PKCS7.swift 3 | // 4 | // Copyright © 2017 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class PKCS7 { 27 | public let mainBlock: ASN1Object 28 | 29 | public init(data: Data) throws { 30 | let asn1 = try ASN1DERDecoder.decode(data: data) 31 | 32 | guard let firstBlock = asn1.first, 33 | let mainBlock = firstBlock.sub(1)?.sub(0) else { 34 | throw PKCS7Error.parseError 35 | } 36 | 37 | self.mainBlock = mainBlock 38 | 39 | guard firstBlock.sub(0)?.value as? String == OID.pkcs7signedData.rawValue else { 40 | throw PKCS7Error.notSupported 41 | } 42 | } 43 | 44 | public var digestAlgorithm: String? { 45 | if let block = mainBlock.sub(1) { 46 | return firstLeafValue(block: block) as? String 47 | } 48 | return nil 49 | } 50 | 51 | public var digestAlgorithmName: String? { 52 | return OID.description(of: digestAlgorithm ?? "") ?? digestAlgorithm 53 | } 54 | 55 | public var certificate: X509Certificate? { 56 | return mainBlock.sub(3)?.sub?.first.map { try? X509Certificate(asn1: $0) } ?? nil 57 | } 58 | 59 | public var certificates: [X509Certificate] { 60 | return mainBlock.sub(3)?.sub?.compactMap { try? X509Certificate(asn1: $0) } ?? [] 61 | } 62 | 63 | public var data: Data? { 64 | if let block = mainBlock.findOid(.pkcs7data) { 65 | if let dataBlock = block.parent?.sub?.last { 66 | var out = Data() 67 | if let value = dataBlock.value as? Data { 68 | out.append(value) 69 | } else if dataBlock.value is String, let rawValue = dataBlock.rawValue { 70 | out.append(rawValue) 71 | } else { 72 | for sub in dataBlock.sub ?? [] { 73 | if let value = sub.value as? Data { 74 | out.append(value) 75 | } else if sub.value is String, let rawValue = sub.rawValue { 76 | out.append(rawValue) 77 | } else { 78 | for sub2 in sub.sub ?? [] { 79 | if let value = sub2.rawValue { 80 | out.append(value) 81 | } 82 | } 83 | } 84 | } 85 | } 86 | return out.count > 0 ? out : nil 87 | } 88 | } 89 | return nil 90 | } 91 | } 92 | 93 | enum PKCS7Error: Error { 94 | case notSupported 95 | case parseError 96 | } 97 | -------------------------------------------------------------------------------- /ASN1Decoder/PKCS7_AppleReceipt.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PKCS7_AppleReceipt.swift 3 | // 4 | // Copyright © 2018 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | /* 27 | This extension allow to parse the content of an Apple receipt from the AppStore. 28 | 29 | Reference documentation 30 | https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html 31 | */ 32 | extension PKCS7 { 33 | 34 | public struct ReceiptInfo { 35 | 36 | /// CFBundleIdentifier in Info.plist 37 | public fileprivate(set) var bundleIdentifier: String? 38 | 39 | /// CFBundleIdentifier in Info.plist as bytes, used, with other data, to compute the SHA-1 hash during validation. 40 | public fileprivate(set) var bundleIdentifierData: Data? 41 | 42 | /// CFBundleVersion (in iOS) or CFBundleShortVersionString (in macOS) in Info.plist 43 | public fileprivate(set) var bundleVersion: String? 44 | 45 | /// CFBundleVersion (in iOS) or CFBundleShortVersionString (in macOS) in Info.plist 46 | public fileprivate(set) var originalApplicationVersion: String? 47 | 48 | /// Opaque value used, with other data, to compute the SHA-1 hash during validation. 49 | public fileprivate(set) var opaqueValue: Data? 50 | 51 | /// SHA-1 hash, used to validate the receipt. 52 | public fileprivate(set) var sha1: Data? 53 | 54 | public fileprivate(set) var receiptCreationDate: Date? 55 | public fileprivate(set) var receiptCreationDateString: String? 56 | public fileprivate(set) var receiptExpirationDate: Date? 57 | public fileprivate(set) var receiptExpirationDateString: String? 58 | public fileprivate(set) var inAppPurchases: [InAppPurchaseInfo]? 59 | } 60 | 61 | public struct InAppPurchaseInfo { 62 | public fileprivate(set) var quantity: UInt64? 63 | public fileprivate(set) var productId: String? 64 | public fileprivate(set) var transactionId: String? 65 | public fileprivate(set) var originalTransactionId: String? 66 | public fileprivate(set) var purchaseDate: Date? 67 | public fileprivate(set) var originalPurchaseDate: Date? 68 | public fileprivate(set) var expiresDate: Date? 69 | public fileprivate(set) var isInIntroOfferPeriod: UInt64? 70 | public fileprivate(set) var cancellationDate: Date? 71 | public fileprivate(set) var webOrderLineItemId: UInt64? 72 | } 73 | 74 | func parseDate(_ dateString: String) -> Date? { 75 | return ReceiptDateFormatter.date(from: dateString) 76 | } 77 | 78 | public func receipt() -> ReceiptInfo? { 79 | guard let block = mainBlock.findOid(.pkcs7data) else { return nil } 80 | guard var receiptBlock = block.parent?.sub?.last?.sub(0)?.sub(0) else { return nil } 81 | var receiptInfo = ReceiptInfo() 82 | 83 | if receiptBlock.asString == "Xcode" { 84 | receiptBlock = receiptBlock.sub(0)! 85 | } 86 | 87 | for item in receiptBlock.sub ?? [] { 88 | let fieldType = (item.sub(0)?.value as? Data)?.uint64Value ?? 0 89 | let fieldValueString = item.sub(2)?.asString 90 | switch fieldType { 91 | case 2: 92 | receiptInfo.bundleIdentifier = fieldValueString 93 | receiptInfo.bundleIdentifierData = item.sub(2)?.rawValue 94 | 95 | case 3: 96 | receiptInfo.bundleVersion = fieldValueString 97 | 98 | case 4: 99 | receiptInfo.opaqueValue = item.sub(2)?.rawValue 100 | 101 | case 5: 102 | receiptInfo.sha1 = item.sub(2)?.rawValue 103 | 104 | case 19: 105 | receiptInfo.originalApplicationVersion = fieldValueString 106 | 107 | case 12: 108 | guard let fieldValueString = fieldValueString else { continue } 109 | receiptInfo.receiptCreationDateString = fieldValueString 110 | receiptInfo.receiptCreationDate = parseDate(fieldValueString) 111 | 112 | case 21: 113 | guard let fieldValueString = fieldValueString else { continue } 114 | receiptInfo.receiptExpirationDateString = fieldValueString 115 | receiptInfo.receiptExpirationDate = parseDate(fieldValueString) 116 | 117 | case 17: 118 | let subItems = item.sub(2)?.sub?.first?.sub ?? [] 119 | if receiptInfo.inAppPurchases == nil { 120 | receiptInfo.inAppPurchases = [] 121 | } 122 | receiptInfo.inAppPurchases?.append(inAppPurchase(subItems)) 123 | 124 | default: 125 | break 126 | } 127 | } 128 | return receiptInfo 129 | } 130 | 131 | private func inAppPurchase(_ subItems: [ASN1Object]) -> InAppPurchaseInfo { 132 | var inAppPurchaseInfo = InAppPurchaseInfo() 133 | subItems.forEach { subItem in 134 | let fieldType = (subItem.sub(0)?.value as? Data)?.uint64Value ?? 0 135 | let fieldValue = subItem.sub(2)?.sub?.first?.value 136 | switch fieldType { 137 | case 1701: 138 | inAppPurchaseInfo.quantity = (fieldValue as? Data)?.uint64Value 139 | case 1702: 140 | inAppPurchaseInfo.productId = fieldValue as? String 141 | case 1703: 142 | inAppPurchaseInfo.transactionId = fieldValue as? String 143 | case 1705: 144 | inAppPurchaseInfo.originalTransactionId = fieldValue as? String 145 | case 1704: 146 | if let fieldValueString = fieldValue as? String { 147 | inAppPurchaseInfo.purchaseDate = parseDate(fieldValueString) 148 | } 149 | case 1706: 150 | if let fieldValueString = fieldValue as? String { 151 | inAppPurchaseInfo.originalPurchaseDate = parseDate(fieldValueString) 152 | } 153 | case 1708: 154 | if let fieldValueString = fieldValue as? String { 155 | inAppPurchaseInfo.expiresDate = parseDate(fieldValueString) 156 | } 157 | case 1719: 158 | inAppPurchaseInfo.isInIntroOfferPeriod = (fieldValue as? Data)?.uint64Value 159 | case 1712: 160 | if let fieldValueString = fieldValue as? String { 161 | inAppPurchaseInfo.cancellationDate = parseDate(fieldValueString) 162 | } 163 | case 1711: 164 | inAppPurchaseInfo.webOrderLineItemId = (fieldValue as? Data)?.uint64Value 165 | default: 166 | break 167 | } 168 | } 169 | return inAppPurchaseInfo 170 | } 171 | 172 | } 173 | 174 | // MARK: ReceiptDateFormatter 175 | 176 | /// Static formatting methods to use for string encoded date values in receipts 177 | private enum ReceiptDateFormatter { 178 | 179 | /// Uses receipt-conform representation of dates like "2017-01-01T12:00:00Z", 180 | /// as a fallback, dates like "2017-01-01T12:00:00.123Z" are also parsed. 181 | static func date(from string: String) -> Date? { 182 | return self.defaultDateFormatter.date(from: string) // expected 183 | ?? self.fallbackDateFormatterWithMS.date(from: string) // try again with milliseconds 184 | } 185 | 186 | /// Uses receipt-conform representation of dates like "2017-01-01T12:00:00Z" (rfc3339 without millis) 187 | private static let defaultDateFormatter: DateFormatter = { 188 | let dateDateFormatter = DateFormatter() 189 | dateDateFormatter.locale = Locale(identifier: "en_US_POSIX") 190 | dateDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 191 | dateDateFormatter.timeZone = TimeZone(secondsFromGMT: 0) 192 | return dateDateFormatter 193 | }() 194 | 195 | /// Uses representation of dates like "2017-01-01T12:00:00.123Z" 196 | /// 197 | /// This is not the officially intended format, but added after hearing reports about new format adding ms https://twitter.com/depth42/status/1314179654811607041 198 | /// 199 | /// The formatting String was taken from https://github.com/IdeasOnCanvas/AppReceiptValidator/pull/73 200 | /// where tests were performed to check if it works 201 | private static let fallbackDateFormatterWithMS: DateFormatter = { 202 | let dateFormatter = DateFormatter() 203 | dateFormatter.locale = Locale(identifier: "en_US_POSIX") 204 | dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'" 205 | dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) 206 | return dateFormatter 207 | }() 208 | } 209 | -------------------------------------------------------------------------------- /ASN1Decoder/PKCS7_Signature.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PKCS7_Signature.swift 3 | // 4 | // Copyright © 2020 Alexander Heinrich & Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | extension PKCS7 { 27 | public var signatures: [SignatureInfo]? { 28 | // Signer infos sequence. https://tools.ietf.org/html/rfc5652#section-5.3 29 | 30 | guard let signerInfos = mainBlock.sub(4) else {return nil} 31 | 32 | let numberOfSignatures = signerInfos.subCount() 33 | 34 | var signatures = [SignatureInfo]() 35 | 36 | for i in 0.. 0, 55 | let block1 = asn1.first?.sub(0) else { 56 | throw ASN1Error.parseError 57 | } 58 | 59 | self.block1 = block1 60 | } 61 | 62 | public convenience init(pem: Data) throws { 63 | guard let derData = X509Certificate.decodeToDER(pem: pem) else { 64 | throw ASN1Error.parseError 65 | } 66 | 67 | try self.init(der: derData) 68 | } 69 | 70 | init(asn1: ASN1Object) throws { 71 | guard let block1 = asn1.sub(0) else { throw ASN1Error.parseError } 72 | 73 | self.asn1 = [asn1] 74 | self.block1 = block1 75 | } 76 | 77 | public var description: String { 78 | return asn1.reduce("") { $0 + "\($1.description)\n" } 79 | } 80 | 81 | /// Checks that the given date is within the certificate's validity period. 82 | public func checkValidity(_ date: Date = Date()) -> Bool { 83 | if let notBefore = notBefore, let notAfter = notAfter { 84 | return date > notBefore && date < notAfter 85 | } 86 | return false 87 | } 88 | 89 | /// Gets the version (version number) value from the certificate. 90 | public var version: Int? { 91 | if let data = firstLeafValue(block: block1) as? Data, let value = data.uint64Value, value < Int.max { 92 | return Int(value) + 1 93 | } 94 | return 1 95 | } 96 | 97 | /// Gets the serialNumber value from the certificate. 98 | public var serialNumber: Data? { 99 | return block1[X509BlockPosition.serialNumber]?.value as? Data 100 | } 101 | 102 | /// Returns the issuer (issuer distinguished name) value from the certificate as a String. 103 | public var issuerDistinguishedName: String? { 104 | if let issuerBlock = block1[X509BlockPosition.issuer] { 105 | return ASN1DistinguishedNameFormatter.string(from: issuerBlock) 106 | } 107 | return nil 108 | } 109 | 110 | public var issuerOIDs: [String] { 111 | var result: [String] = [] 112 | if let subjectBlock = block1[X509BlockPosition.issuer] { 113 | for sub in subjectBlock.sub ?? [] { 114 | if let value = firstLeafValue(block: sub) as? String, !result.contains(value) { 115 | result.append(value) 116 | } 117 | } 118 | } 119 | return result 120 | } 121 | 122 | public func issuer(oidString: String) -> String? { 123 | if let subjectBlock = block1[X509BlockPosition.issuer] { 124 | if let oidBlock = subjectBlock.findOid(oidString) { 125 | return oidBlock.parent?.sub?.last?.value as? String 126 | } 127 | } 128 | return nil 129 | } 130 | 131 | public func issuer(oid: OID) -> String? { 132 | return issuer(oidString: oid.rawValue) 133 | } 134 | 135 | @available(*, deprecated, message: "Use issuer(oid:) instead") 136 | public func issuer(dn: ASN1DistinguishedNames) -> String? { 137 | return issuer(oidString: dn.oid) 138 | } 139 | 140 | /// Returns the subject (subject distinguished name) value from the certificate as a String. 141 | public var subjectDistinguishedName: String? { 142 | if let subjectBlock = block1[X509BlockPosition.subject] { 143 | return ASN1DistinguishedNameFormatter.string(from: subjectBlock) 144 | } 145 | return nil 146 | } 147 | 148 | public var subjectOIDs: [String] { 149 | var result: [String] = [] 150 | if let subjectBlock = block1[X509BlockPosition.subject] { 151 | for sub in subjectBlock.sub ?? [] { 152 | if let value = firstLeafValue(block: sub) as? String, !result.contains(value) { 153 | result.append(value) 154 | } 155 | } 156 | } 157 | return result 158 | } 159 | 160 | public func subject(oidString: String) -> [String]? { 161 | var result: [String]? 162 | if let subjectBlock = block1[X509BlockPosition.subject] { 163 | for sub in subjectBlock.sub ?? [] { 164 | if let oidBlock = sub.findOid(oidString) { 165 | guard let value = oidBlock.parent?.sub?.last?.value as? String else { 166 | continue 167 | } 168 | if result == nil { 169 | result = [] 170 | } 171 | result?.append(value) 172 | } 173 | } 174 | } 175 | return result 176 | } 177 | 178 | public func subject(oid: OID) -> [String]? { 179 | return subject(oidString: oid.rawValue) 180 | } 181 | 182 | @available(*, deprecated, message: "Use subject(oid:) instead") 183 | public func subject(dn: ASN1DistinguishedNames) -> [String]? { 184 | return subject(oidString: dn.oid) 185 | } 186 | 187 | /// Gets the notBefore date from the validity period of the certificate. 188 | public var notBefore: Date? { 189 | return block1[X509BlockPosition.dateValidity]?.sub(0)?.value as? Date 190 | } 191 | 192 | /// Gets the notAfter date from the validity period of the certificate. 193 | public var notAfter: Date? { 194 | return block1[X509BlockPosition.dateValidity]?.sub(1)?.value as? Date 195 | } 196 | 197 | /// Gets the signature value (the raw signature bits) from the certificate. 198 | public var signature: Data? { 199 | return asn1[0].sub(2)?.value as? Data 200 | } 201 | 202 | /// Gets the signature algorithm name for the certificate signature algorithm. 203 | public var sigAlgName: String? { 204 | return OID.description(of: sigAlgOID ?? "") 205 | } 206 | 207 | /// Gets the signature algorithm OID string from the certificate. 208 | public var sigAlgOID: String? { 209 | return block1.sub(2)?.sub(0)?.value as? String 210 | } 211 | 212 | /// Gets the DER-encoded signature algorithm parameters from this certificate's signature algorithm. 213 | public var sigAlgParams: Data? { 214 | guard let obj = block1[X509BlockPosition.signatureAlg]?.sub(1) else { return nil } 215 | if obj.identifier?.tagNumber() == .null { 216 | return Data([0x05, 0x00]) 217 | } else { 218 | return obj.rawValue?.derEncodedSequence 219 | } 220 | } 221 | 222 | /** 223 | Gets a boolean array representing bits of the KeyUsage extension, (OID = 2.5.29.15). 224 | ``` 225 | KeyUsage ::= BIT STRING { 226 | digitalSignature (0), 227 | nonRepudiation (1), 228 | keyEncipherment (2), 229 | dataEncipherment (3), 230 | keyAgreement (4), 231 | keyCertSign (5), 232 | cRLSign (6), 233 | encipherOnly (7), 234 | decipherOnly (8) 235 | } 236 | ``` 237 | */ 238 | public var keyUsage: [Bool] { 239 | var result: [Bool] = [] 240 | if let oidBlock = block1.findOid(OID.keyUsage) { 241 | let data = oidBlock.parent?.sub?.last?.sub(0)?.value as? Data 242 | let bits: UInt8 = data?.first ?? 0 243 | for index in 0...7 { 244 | let value = bits & UInt8(1 << index) != 0 245 | result.insert(value, at: 0) 246 | } 247 | } 248 | return result 249 | } 250 | 251 | /// Gets a list of Strings representing the OBJECT IDENTIFIERs of the ExtKeyUsageSyntax field of 252 | /// the extended key usage extension, (OID = 2.5.29.37). 253 | public var extendedKeyUsage: [String] { 254 | return extensionObject(oid: OID.extKeyUsage)?.valueAsStrings ?? [] 255 | } 256 | 257 | /// Gets a collection of subject alternative names from the SubjectAltName extension, (OID = 2.5.29.17). 258 | public var subjectAlternativeNames: [String] { 259 | return extensionObject(oid: OID.subjectAltName)?.alternativeNameAsStrings ?? [] 260 | } 261 | 262 | /// Gets a collection of issuer alternative names from the IssuerAltName extension, (OID = 2.5.29.18). 263 | public var issuerAlternativeNames: [String] { 264 | return extensionObject(oid: OID.issuerAltName)?.alternativeNameAsStrings ?? [] 265 | } 266 | 267 | /// Gets the informations of the public key from this certificate. 268 | public var publicKey: X509PublicKey? { 269 | return block1[X509BlockPosition.publicKey].map(X509PublicKey.init) 270 | } 271 | 272 | /// Get a list of critical extension OID codes 273 | public var criticalExtensionOIDs: [String] { 274 | guard let extensionBlocks = extensionBlocks else { return [] } 275 | return extensionBlocks 276 | .map { X509Extension(block: $0) } 277 | .filter { $0.isCritical } 278 | .compactMap { $0.oid } 279 | } 280 | 281 | /// Get a list of non critical extension OID codes 282 | public var nonCriticalExtensionOIDs: [String] { 283 | guard let extensionBlocks = extensionBlocks else { return [] } 284 | return extensionBlocks 285 | .map { X509Extension(block: $0) } 286 | .filter { !$0.isCritical } 287 | .compactMap { $0.oid } 288 | } 289 | 290 | private var extensionBlocks: [ASN1Object]? { 291 | return block1[X509BlockPosition.extensions]?.sub(0)?.sub 292 | } 293 | 294 | /// Gets the extension information of the given OID enum. 295 | public func extensionObject(oid: OID) -> X509Extension? { 296 | return extensionObject(oid: oid.rawValue) 297 | } 298 | 299 | /// Gets the extension information of the given OID code. 300 | public func extensionObject(oid: String) -> X509Extension? { 301 | return block1[X509BlockPosition.extensions]? 302 | .findOid(oid)? 303 | .parent 304 | .map { oidExtensionMap[oid]?.init(block: $0) ?? X509Extension(block: $0) } 305 | } 306 | 307 | // Association of Class decoding helper and OID 308 | private let oidExtensionMap: [String: X509Extension.Type] = [ 309 | OID.basicConstraints.rawValue: BasicConstraintExtension.self, 310 | OID.subjectKeyIdentifier.rawValue: SubjectKeyIdentifierExtension.self, 311 | OID.authorityInfoAccess.rawValue: AuthorityInfoAccessExtension.self, 312 | OID.authorityKeyIdentifier.rawValue: AuthorityKeyIdentifierExtension.self, 313 | OID.certificatePolicies.rawValue: CertificatePoliciesExtension.self, 314 | OID.cRLDistributionPoints.rawValue: CRLDistributionPointsExtension.self 315 | ] 316 | 317 | // read possibile PEM encoding 318 | private static func decodeToDER(pem pemData: Data) -> Data? { 319 | if 320 | let pem = String(data: pemData, encoding: .ascii), 321 | pem.contains(beginPemBlock) { 322 | 323 | let lines = pem.components(separatedBy: .newlines) 324 | var base64buffer = "" 325 | var certLine = false 326 | for line in lines { 327 | if line == endPemBlock { 328 | certLine = false 329 | } 330 | if certLine { 331 | base64buffer.append(line) 332 | } 333 | if line == beginPemBlock { 334 | certLine = true 335 | } 336 | } 337 | if let derDataDecoded = Data(base64Encoded: base64buffer) { 338 | return derDataDecoded 339 | } 340 | } 341 | 342 | return nil 343 | } 344 | } 345 | 346 | func firstLeafValue(block: ASN1Object) -> Any? { 347 | if let sub = block.sub?.first { 348 | return firstLeafValue(block: sub) 349 | } 350 | return block.value 351 | } 352 | 353 | extension ASN1Object { 354 | subscript(index: X509Certificate.X509BlockPosition) -> ASN1Object? { 355 | guard let sub = sub else { return nil } 356 | if sub.count <= 6 { 357 | guard sub.indices.contains(index.rawValue-1) else { return nil } 358 | return sub[index.rawValue-1] 359 | } else { 360 | guard sub.indices.contains(index.rawValue) else { return nil } 361 | return sub[index.rawValue] 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /ASN1Decoder/X509Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509Extension.swift 3 | // 4 | // Copyright © 2019 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class X509Extension { 27 | 28 | let block: ASN1Object 29 | 30 | required init(block: ASN1Object) { 31 | self.block = block 32 | } 33 | 34 | public var oid: String? { 35 | return block.sub(0)?.value as? String 36 | } 37 | 38 | public var name: String? { 39 | return OID.description(of: oid ?? "") 40 | } 41 | 42 | public var isCritical: Bool { 43 | if block.sub?.count ?? 0 > 2 { 44 | return block.sub(1)?.value as? Bool ?? false 45 | } 46 | return false 47 | } 48 | 49 | public var value: Any? { 50 | if let valueBlock = block.sub?.last { 51 | return firstLeafValue(block: valueBlock) 52 | } 53 | return nil 54 | } 55 | 56 | var valueAsBlock: ASN1Object? { 57 | return block.sub?.last 58 | } 59 | 60 | var valueAsStrings: [String] { 61 | var result: [String] = [] 62 | for item in block.sub?.last?.sub?.last?.sub ?? [] { 63 | if let name = item.value as? String { 64 | result.append(name) 65 | } 66 | } 67 | return result 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ASN1Decoder/X509ExtensionAltName.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509ExtensionAltName.swift 3 | // 4 | // Copyright © 2020 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | extension X509Extension { 27 | 28 | // Used for SubjectAltName and IssuerAltName 29 | // Every name can be one of these subtype: 30 | // - otherName [0] INSTANCE OF OTHER-NAME, 31 | // - rfc822Name [1] IA5String, 32 | // - dNSName [2] IA5String, 33 | // - x400Address [3] ORAddress, 34 | // - directoryName [4] Name, 35 | // - ediPartyName [5] EDIPartyName, 36 | // - uniformResourceIdentifier [6] IA5String, 37 | // - IPAddress [7] OCTET STRING, 38 | // - registeredID [8] OBJECT IDENTIFIER 39 | // 40 | // Result does not support: x400Address and ediPartyName 41 | // 42 | var alternativeNameAsStrings: [String] { 43 | var result: [String] = [] 44 | for item in block.sub?.last?.sub?.last?.sub ?? [] { 45 | guard let name = generalName(of: item) else { 46 | continue 47 | } 48 | result.append(name) 49 | } 50 | return result 51 | } 52 | 53 | func generalName(of item: ASN1Object) -> String? { 54 | guard let nameType = item.identifier?.tagNumber().rawValue else { 55 | return nil 56 | } 57 | switch nameType { 58 | case 0: 59 | if let name = item.sub?.last?.sub?.last?.value as? String { 60 | return name 61 | } 62 | case 1, 2, 6: 63 | if let name = item.value as? String { 64 | return name 65 | } 66 | case 4: 67 | if let sequence = item.sub(0) { 68 | return ASN1DistinguishedNameFormatter.string(from: sequence) 69 | } 70 | case 7: 71 | if let ipData = item.rawValue { 72 | if ipData.count == 4 { 73 | return ipData.map({ "\($0)" }).joined(separator: ".") 74 | } else if ipData.count == 16 { 75 | let ipBytes = [UInt8](ipData) 76 | return stride(from: 0, to: 16, by: 2) 77 | .map { offset in 78 | let uint16 = ipBytes.withUnsafeBytes { 79 | $0.load(fromByteOffset: offset, as: UInt16.self) 80 | } 81 | return String(format: "%x", uint16.byteSwapped) 82 | } 83 | .joined(separator: ":") 84 | } else { 85 | return nil 86 | } 87 | } 88 | case 8: 89 | if let value = item.value as? String, var data = value.data(using: .utf8) { 90 | let oid = ASN1DERDecoder.decodeOid(contentData: &data) 91 | return oid 92 | } 93 | default: 94 | return nil 95 | } 96 | return nil 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ASN1Decoder/X509ExtensionClasses.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509ExtensionClasses.swift 3 | // 4 | // Copyright © 2020 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | extension X509Certificate { 27 | 28 | /// Recognition for Basic Constraint Extension (2.5.29.19) 29 | public class BasicConstraintExtension: X509Extension { 30 | 31 | public var isCA: Bool { 32 | return (valueAsBlock?.sub(0)?.sub(0)?.value as? Bool) ?? false 33 | } 34 | 35 | public var pathLenConstraint: UInt64? { 36 | guard let data = valueAsBlock?.sub(0)?.sub(1)?.value as? Data else { 37 | return nil 38 | } 39 | return data.uint64Value 40 | } 41 | } 42 | 43 | /// Recognition for Subject Key Identifier Extension (2.5.29.14) 44 | public class SubjectKeyIdentifierExtension: X509Extension { 45 | 46 | public override var value: Any? { 47 | guard let rawValue = valueAsBlock?.rawValue else { 48 | return nil 49 | } 50 | return rawValue.sequenceContent 51 | } 52 | } 53 | 54 | // MARK: - Authority Extensions 55 | 56 | public struct AuthorityInfoAccess { 57 | public let method: String 58 | public let location: String 59 | } 60 | 61 | /// Recognition for Authority Info Access Extension (1.3.6.1.5.5.7.1.1) 62 | public class AuthorityInfoAccessExtension: X509Extension { 63 | 64 | public var infoAccess: [AuthorityInfoAccess]? { 65 | guard let valueAsBlock = valueAsBlock else { 66 | return nil 67 | } 68 | let subs = valueAsBlock.sub(0)?.sub ?? [] 69 | 70 | return subs.compactMap { sub in 71 | guard var oidData = sub.sub(0)?.rawValue, 72 | let nameBlock = sub.sub(1) else { 73 | return nil 74 | } 75 | if 76 | let oid = ASN1DERDecoder.decodeOid(contentData: &oidData), 77 | let location = generalName(of: nameBlock) { 78 | return AuthorityInfoAccess(method: oid, location: location) 79 | } else { 80 | return nil 81 | } 82 | } 83 | } 84 | } 85 | 86 | /// Recognition for Authority Key Identifier Extension (2.5.29.35) 87 | public class AuthorityKeyIdentifierExtension: X509Extension { 88 | 89 | /* 90 | AuthorityKeyIdentifier ::= SEQUENCE { 91 | keyIdentifier [0] KeyIdentifier OPTIONAL, 92 | authorityCertIssuer [1] GeneralNames OPTIONAL, 93 | authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } 94 | */ 95 | 96 | public var keyIdentifier: Data? { 97 | guard let sequence = valueAsBlock?.sub(0)?.sub else { 98 | return nil 99 | } 100 | if let sub = sequence.first(where: { $0.identifier?.tagNumber().rawValue == 0 }) { 101 | return sub.rawValue 102 | } 103 | return nil 104 | } 105 | 106 | public var certificateIssuer: [String]? { 107 | guard let sequence = valueAsBlock?.sub(0)?.sub else { 108 | return nil 109 | } 110 | if let sub = sequence.first(where: { $0.identifier?.tagNumber().rawValue == 1 }) { 111 | return sub.sub?.compactMap { generalName(of: $0) } 112 | } 113 | return nil 114 | } 115 | 116 | public var serialNumber: Data? { 117 | guard let sequence = valueAsBlock?.sub(0)?.sub else { 118 | return nil 119 | } 120 | if let sub = sequence.first(where: { $0.identifier?.tagNumber().rawValue == 2 }) { 121 | return sub.rawValue 122 | } 123 | return nil 124 | } 125 | } 126 | 127 | // MARK: - Certificate Policies Extension 128 | 129 | public struct CertificatePolicyQualifier { 130 | public let oid: String 131 | public let value: String? 132 | } 133 | public struct CertificatePolicy { 134 | public let oid: String 135 | public let qualifiers: [CertificatePolicyQualifier]? 136 | } 137 | 138 | /// Recognition for Certificate Policies Extension (2.5.29.32) 139 | public class CertificatePoliciesExtension: X509Extension { 140 | 141 | public var policies: [CertificatePolicy]? { 142 | guard let valueAsBlock = valueAsBlock else { 143 | return nil 144 | } 145 | let subs = valueAsBlock.sub(0)?.sub ?? [] 146 | 147 | return subs.compactMap { sub in 148 | guard 149 | var data = sub.sub(0)?.rawValue, 150 | let oid = ASN1DERDecoder.decodeOid(contentData: &data) else { 151 | return nil 152 | } 153 | var qualifiers: [CertificatePolicyQualifier]? 154 | if let subQualifiers = sub.sub(1) { 155 | qualifiers = subQualifiers.sub?.compactMap { sub in 156 | if var rawValue = sub.sub(0)?.rawValue, let oid = ASN1DERDecoder.decodeOid(contentData: &rawValue) { 157 | let value = sub.sub(1)?.asString 158 | return CertificatePolicyQualifier(oid: oid, value: value) 159 | } else { 160 | return nil 161 | } 162 | } 163 | } 164 | return CertificatePolicy(oid: oid, qualifiers: qualifiers) 165 | } 166 | } 167 | } 168 | 169 | // MARK: - CRL Distribution Points 170 | 171 | public class CRLDistributionPointsExtension: X509Extension { 172 | 173 | public var crls: [String]? { 174 | guard let valueAsBlock = valueAsBlock else { 175 | return nil 176 | } 177 | let subs = valueAsBlock.sub(0)?.sub ?? [] 178 | return subs.compactMap { $0.asString } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /ASN1Decoder/X509PublicKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // X509PublicKey.swift 3 | // 4 | // Copyright © 2019 Filippo Maguolo. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | import Foundation 25 | 26 | public class X509PublicKey { 27 | 28 | let pkBlock: ASN1Object 29 | 30 | init(pkBlock: ASN1Object) { 31 | self.pkBlock = pkBlock 32 | } 33 | 34 | public var algOid: String? { 35 | return pkBlock.sub(0)?.sub(0)?.value as? String 36 | } 37 | 38 | public var algName: String? { 39 | return OID.description(of: algOid ?? "") 40 | } 41 | 42 | public var algParams: String? { 43 | return pkBlock.sub(0)?.sub(1)?.value as? String 44 | } 45 | 46 | public var derEncodedKey: Data? { 47 | return pkBlock.rawValue?.derEncodedSequence 48 | } 49 | 50 | public var key: Data? { 51 | guard 52 | let algOid = algOid, 53 | let oid = OID(rawValue: algOid), 54 | let keyData = pkBlock.sub(1)?.value as? Data else { 55 | return nil 56 | } 57 | 58 | switch oid { 59 | case .ecPublicKey: 60 | return keyData 61 | 62 | case .rsaEncryption: 63 | guard let publicKeyAsn1Objects = (try? ASN1DERDecoder.decode(data: keyData)) else { 64 | return nil 65 | } 66 | guard let publicKeyModulus = publicKeyAsn1Objects.first?.sub(0)?.value as? Data else { 67 | return nil 68 | } 69 | return publicKeyModulus 70 | 71 | default: 72 | return nil 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1AppleReceiptTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1AppleReceiptTest.swift 3 | // ASN1DecoderTests 4 | // 5 | // Created by Filippo Maguolo on 08/12/2019. 6 | // Copyright © 2019 Filippo Maguolo. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ASN1Decoder 11 | 12 | class ASN1AppleReceiptTest: XCTestCase { 13 | 14 | func testReceipt() { 15 | guard let data = Data(base64Encoded: receiptSample) else { 16 | XCTFail("Invalid Base64 format") 17 | return 18 | } 19 | let pkcs7 = try? PKCS7(data: data) 20 | guard let receipt = pkcs7?.receipt() else { 21 | XCTFail("Invalid Receipt format") 22 | return 23 | } 24 | 25 | XCTAssertEqual(receipt.bundleIdentifier, "com.belive.app.ios") 26 | XCTAssertEqual(receipt.bundleVersion, "3") 27 | XCTAssertEqual(receipt.originalApplicationVersion, "1.0") 28 | XCTAssertEqual(receipt.receiptCreationDateString, "2018-11-13T16:46:31Z") 29 | XCTAssertEqual(receipt.sha1?.hexEncodedString(), "27384ED414759313951AB084A902E4C2F52E64FF") 30 | XCTAssertEqual(receipt.opaqueValue?.hexEncodedString(), "34E2349B0BD662E4EC127A6BFD10AF24") 31 | 32 | XCTAssertEqual(receipt.inAppPurchases?.first?.productId, "test2") 33 | XCTAssertEqual(receipt.inAppPurchases?.first?.quantity, 1) 34 | XCTAssertEqual(receipt.inAppPurchases?.first?.transactionId, "1000000472106082") 35 | 36 | XCTAssertNotNil(pkcs7?.signatures?.first?.signature) 37 | } 38 | 39 | let receiptSample = "MIITuAYJKoZIhvcNAQcCoIITqTCCE6UCAQExCzAJBgUrDgMCGgUAMIIDWQYJKoZIhvcNAQcBoIIDSgSCA0YxggNCMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQECAQEEAwIBADALAgEDAgEBBAMMATMwCwIBCwIBAQQDAgEAMAsCAQ4CAQEEAwIBWjALAgEPAgEBBAMCAQAwCwIBEAIBAQQDAgEAMAsCARkCAQEEAwIBAzAMAgEKAgEBBAQWAjQrMA0CAQ0CAQEEBQIDAYfPMA0CARMCAQEEBQwDMS4wMA4CAQkCAQEEBgIEUDI1MDAYAgEEAgECBBA04jSbC9Zi5OwSemv9EK8kMBsCAQACAQEEEwwRUHJvZHVjdGlvblNhbmRib3gwHAIBAgIBAQQUDBJjb20uYmVsaXZlLmFwcC5pb3MwHAIBBQIBAQQUJzhO1BR1kxOVGrCEqQLkwvUuZP8wHgIBDAIBAQQWFhQyMDE4LTExLTEzVDE2OjQ2OjMxWjAeAgESAgEBBBYWFDIwMTMtMDgtMDFUMDc6MDA6MDBaMD0CAQcCAQEENedAPSDSwFz7IoNyAPZTI59czwFA1wkme6h1P/iicVNxpR8niuvFpKYx1pqnKR34cdDeJIzMMFECAQYCAQEESfQpXyBVFno5UWwqDFaMQ/jvbkZCDvz3/6RVKPU80KMCSp4onID0/AWet6BjZgagzrXtsEEdVLzfZ1ocoMuCNTOMyiWYS8uJj0YwggFKAgERAgEBBIIBQDGCATwwCwICBqwCAQEEAhYAMAsCAgatAgEBBAIMADALAgIGsAIBAQQCFgAwCwICBrICAQEEAgwAMAsCAgazAgEBBAIMADALAgIGtAIBAQQCDAAwCwICBrUCAQEEAgwAMAsCAga2AgEBBAIMADAMAgIGpQIBAQQDAgEBMAwCAgarAgEBBAMCAQEwDAICBq4CAQEEAwIBADAMAgIGrwIBAQQDAgEAMAwCAgaxAgEBBAMCAQAwEAICBqYCAQEEBwwFdGVzdDIwGwICBqcCAQEEEgwQMTAwMDAwMDQ3MjEwNjA4MjAbAgIGqQIBAQQSDBAxMDAwMDAwNDcyMTA2MDgyMB8CAgaoAgEBBBYWFDIwMTgtMTEtMTNUMTY6NDY6MzFaMB8CAgaqAgEBBBYWFDIwMTgtMTEtMTNUMTY6NDY6MzFaoIIOZTCCBXwwggRkoAMCAQICCA7rV4fnngmNMA0GCSqGSIb3DQEBBQUAMIGWMQswCQYDVQQGEwJVUzETMBEGA1UECgwKQXBwbGUgSW5jLjEsMCoGA1UECwwjQXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMxRDBCBgNVBAMMO0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTE1MTExMzAyMTUwOVoXDTIzMDIwNzIxNDg0N1owgYkxNzA1BgNVBAMMLk1hYyBBcHAgU3RvcmUgYW5kIGlUdW5lcyBTdG9yZSBSZWNlaXB0IFNpZ25pbmcxLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKXPgf0looFb1oftI9ozHI7iI8ClxCbLPcaf7EoNVYb/pALXl8o5VG19f7JUGJ3ELFJxjmR7gs6JuknWCOW0iHHPP1tGLsbEHbgDqViiBD4heNXbt9COEo2DTFsqaDeTwvK9HsTSoQxKWFKrEuPt3R+YFZA1LcLMEsqNSIH3WHhUa+iMMTYfSgYMR1TzN5C4spKJfV+khUrhwJzguqS7gpdj9CuTwf0+b8rB9Typj1IawCUKdg7e/pn+/8Jr9VterHNRSQhWicxDkMyOgQLQoJe2XLGhaWmHkBBoJiY5uB0Qc7AKXcVz0N92O9gt2Yge4+wHz+KO0NP6JlWB7+IDSSMCAwEAAaOCAdcwggHTMD8GCCsGAQUFBwEBBDMwMTAvBggrBgEFBQcwAYYjaHR0cDovL29jc3AuYXBwbGUuY29tL29jc3AwMy13d2RyMDQwHQYDVR0OBBYEFJGknPzEdrefoIr0TfWPNl3tKwSFMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUiCcXCam2GGCL7Ou69kdZxVJUo7cwggEeBgNVHSAEggEVMIIBETCCAQ0GCiqGSIb3Y2QFBgEwgf4wgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wNgYIKwYBBQUHAgEWKmh0dHA6Ly93d3cuYXBwbGUuY29tL2NlcnRpZmljYXRlYXV0aG9yaXR5LzAOBgNVHQ8BAf8EBAMCB4AwEAYKKoZIhvdjZAYLAQQCBQAwDQYJKoZIhvcNAQEFBQADggEBAA2mG9MuPeNbKwduQpZs0+iMQzCCX+Bc0Y2+vQ+9GvwlktuMhcOAWd/j4tcuBRSsDdu2uP78NS58y60Xa45/H+R3ubFnlbQTXqYZhnb4WiCV52OMD3P86O3GH66Z+GVIXKDgKDrAEDctuaAEOR9zucgF/fLefxoqKm4rAfygIFzZ630npjP49ZjgvkTbsUxn/G4KT8niBqjSl/OnjmtRolqEdWXRFgRi48Ff9Qipz2jZkgDJwYyz+I0AZLpYYMB8r491ymm5WyrWHWhumEL1TKc3GZvMOxx6GUPzo22/SGAGDDaSK+zeGLUR2i0j0I78oGmcFxuegHs5R0UwYS/HE6gwggQiMIIDCqADAgECAggB3rzEOW2gEDANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMTMwMjA3MjE0ODQ3WhcNMjMwMjA3MjE0ODQ3WjCBljELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMo4VKbLVqrIJDlI6Yzu7F+4fyaRvDRTes58Y4Bhd2RepQcjtjn+UC0VVlhwLX7EbsFKhT4v8N6EGqFXya97GP9q+hUSSRUIGayq2yoy7ZZjaFIVPYyK7L9rGJXgA6wBfZcFZ84OhZU3au0Jtq5nzVFkn8Zc0bxXbmc1gHY2pIeBbjiP2CsVTnsl2Fq/ToPBjdKT1RpxtWCcnTNOVfkSWAyGuBYNweV3RY1QSLorLeSUheHoxJ3GaKWwo/xnfnC6AllLd0KRObn1zeFM78A7SIym5SFd/Wpqu6cWNWDS5q3zRinJ6MOL6XnAamFnFbLw/eVovGJfbs+Z3e8bY/6SZasCAwEAAaOBpjCBozAdBgNVHQ4EFgQUiCcXCam2GGCL7Ou69kdZxVJUo7cwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAuBgNVHR8EJzAlMCOgIaAfhh1odHRwOi8vY3JsLmFwcGxlLmNvbS9yb290LmNybDAOBgNVHQ8BAf8EBAMCAYYwEAYKKoZIhvdjZAYCAQQCBQAwDQYJKoZIhvcNAQEFBQADggEBAE/P71m+LPWybC+P7hOHMugFNahui33JaQy52Re8dyzUZ+L9mm06WVzfgwG9sq4qYXKxr83DRTCPo4MNzh1HtPGTiqN0m6TDmHKHOz6vRQuSVLkyu5AYU2sKThC22R1QbCGAColOV4xrWzw9pv3e9w0jHQtKJoc/upGSTKQZEhltV/V6WId7aIrkhoxK6+JJFKql3VUAqa67SzCu4aCxvCmA5gl35b40ogHKf9ziCuY7uLvsumKV8wVjQYLNDzsdTJWk26v5yZXpT+RN5yaZgem8+bQp0gF6ZuEujPYhisX4eOGBrr/TkJ2prfOv/TgalmcwHFGlXOxxioK0bA8MFR8wggS7MIIDo6ADAgECAgECMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpBcHBsZSBJbmMuMSYwJAYDVQQLEx1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEWMBQGA1UEAxMNQXBwbGUgUm9vdCBDQTAeFw0wNjA0MjUyMTQwMzZaFw0zNTAyMDkyMTQwMzZaMGIxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpBcHBsZSBJbmMuMSYwJAYDVQQLEx1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEWMBQGA1UEAxMNQXBwbGUgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOSRqQkfkdseR1DrBe1eeYQt6zaiV0xV7IsZid75S2z1B6siMALoGD74UAnTf0GomPnRymacJGsR0KO75Bsqwx+VnnoMpEeLW9QWNzPLxA9NzhRp0ckZcvVdDtV/X5vyJQO6VY9NXQ3xZDUjFUsVWR2zlPf2nJ7PULrBWFBnjwi0IPfLrCwgb3C2PwEwjLdDzw+dPfMrSSgayP7OtbkO2V4c1ss9tTqt9A8OAJILsSEWLnTVPA3bYharo3GSR1NVwa8vQbP4++NwzeajTEV+H0xrUJZBicR0YgsQg0GHM4qBsTBY7FoEMoxos48d3mVz/2deZbxJ2HafMxRloXeUyS0CAwEAAaOCAXowggF2MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjAfBgNVHSMEGDAWgBQr0GlHlHYJ/vRrjS5ApvdHTX8IXjCCAREGA1UdIASCAQgwggEEMIIBAAYJKoZIhvdjZAUBMIHyMCoGCCsGAQUFBwIBFh5odHRwczovL3d3dy5hcHBsZS5jb20vYXBwbGVjYS8wgcMGCCsGAQUFBwICMIG2GoGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDQYJKoZIhvcNAQEFBQADggEBAFw2mUwteLftjJvc83eb8nbSdzBPwR+Fg4UbmT1HN/Kpm0COLNSxkBLYvvRzm+7SZA/LeU802KI++Xj/a8gH7H05g4tTINM4xLG/mk8Ka/8r/FmnBQl8F0BWER5007eLIztHo9VvJOLr0bdw3w9F4SfK8W147ee1Fxeo3H4iNcol1dkP1mvUoiQjEfehrI9zgWDGG1sJL5Ky+ERI8GA4nhX1PSZnIIozavcNgs/e66Mv+VNqW2TAYzN39zoHLFbr2g8hDtq6cxlPtdk2f8GHVdmnmbkyQvvY1XGefqFStxu9k0IkEirHDx22TZxeY8hLgBdQqorV2uT80AkHN7B1dSExggHLMIIBxwIBATCBozCBljELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xLDAqBgNVBAsMI0FwcGxlIFdvcmxkd2lkZSBEZXZlbG9wZXIgUmVsYXRpb25zMUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eQIIDutXh+eeCY0wCQYFKw4DAhoFADANBgkqhkiG9w0BAQEFAASCAQCJ9ctD+7Yi9JWvl6G+1HOcDO++mhY6rc6japAgogVF4xmIdh275IKRwZKpQbhoJmxXwElbMjkIsXks/48/EzuaHDQBNIVowq8qQaSUb3msvfAZfi7RGnhaJGzkXf7azr9NLMxX29R2jTiw2oaz2ri49piggmrGfXsLjWs9zTHWHHNRN1fLTPtcWb95JbQNAiQqlecG5a95/+KZ7+joh8fQwbthe8oWs5Tla0DDwrEoIbc5yjFT18Dln5bndTvWQJZcsbI4xa7BAEhjg/nfwPhaL17tHZeW8mOcCtG9UcuAgXXC6usVAOSocenhmKUR8W+D6F/jhBn0k9ahApPDmpZh" 40 | } 41 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1DecoderAlternativeNames.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DecoderAlternativeName.swift 3 | // ASN1DecoderTests 4 | // 5 | // Copyright © 2020 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | @testable import ASN1Decoder 27 | 28 | class ASN1DecoderAlternativeNames: XCTestCase { 29 | 30 | func testDecodingAlternativeNames() { 31 | var subjectAlternativeNames = [String]() 32 | do { 33 | let x509 = try X509Certificate(data: samplePEMcertificateData) 34 | subjectAlternativeNames = x509.subjectAlternativeNames 35 | } catch { 36 | print(error) 37 | } 38 | XCTAssertTrue(subjectAlternativeNames.contains("dns.name")) 39 | XCTAssertTrue(subjectAlternativeNames.contains("192.168.0.1")) 40 | XCTAssertTrue(subjectAlternativeNames.contains("upn.name")) 41 | XCTAssertTrue(subjectAlternativeNames.contains("1.2.3.4.5")) 42 | XCTAssertTrue(subjectAlternativeNames.contains("rfc.822.name")) 43 | XCTAssertTrue(subjectAlternativeNames.contains("uri.name")) 44 | XCTAssertTrue(subjectAlternativeNames.contains("OU=dev_world, CN=common_name")) 45 | } 46 | 47 | let samplePEMcertificateData = 48 | """ 49 | -----BEGIN CERTIFICATE----- 50 | MIIDLzCCAhegAwIBAgIEX4galDANBgkqhkiG9w0BAQsFADAWMRQwEgYDVQQDDAtj 51 | b21tb25fbmFtZTAeFw0yMDEwMTUwOTQ3MDBaFw0yMTEwMTUwOTQ3MDBaMBYxFDAS 52 | BgNVBAMMC2NvbW1vbl9uYW1lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC 53 | AQEAo9Uxc9OflevRAjXrAdT+5tmi4CBqc0wLPnx9m08HpTN1w1z+GwbCndPKNYgW 54 | N3DyjIjpiOa9IMif4P8p8L2Kb+76gCoV018fXs/bDYLFc/uiiYKIGZ6GU8COOgcJ 55 | XVfz6I/yv7Ruu5oSEyRLkW7hN4cSfN7b0sEbC3JUR0L5WNraIIQcOAmhRBk09oXM 56 | 0Ai2bIC5GR8Id//NDg7FTGIvQ75fIEaIiOxtNgB2bkibHARtHCGqmqkf3XRVNrhO 57 | ybg7808I9HYljDxNOI9UxQ3qyPRKLnQSwcrjBepF+GYAKrBbTqfhwk1+iTTmKM4f 58 | iWH9V0e4mPjXQXl6s3TMFZQTRwIDAQABo4GEMIGBMH8GA1UdEQR4MHakLDAqMRIw 59 | EAYDVQQLDAlkZXZfd29ybGQxFDASBgNVBAMMC2NvbW1vbl9uYW1lgghkbnMubmFt 60 | ZYcEwKgAAaAYBgorBgEEAYI3FAIDoAoMCHVwbi5uYW1liAQqAwQFgQxyZmMuODIy 61 | Lm5hbWWGCHVyaS5uYW1lMA0GCSqGSIb3DQEBCwUAA4IBAQCdH9e5fhk2nDnMiIUI 62 | E5XMLjLrNF4sGPB9Jh3FQTa8DD320aR8STeYUZr5GqcgZ1IkBVNeVX1zhUJp+fAw 63 | 8NfSsAQGVQvbSQ6nzfpdCoCgl+s4khKg10IMW6p2BULpX8fxW0JFUgAMwKJfkNxC 64 | 9kFXpMqazFoSy+dpJkSc+ZlElCvYx7mKycf/30OHWWNqdTBkFehUTTpiYSH9Uc3V 65 | ikCrKEikRxNwVYySCcWQsxARUYgXnH5IUTj1Or5awEWA72m363zb87iNlvYlgc2c 66 | tIrtDCLmWxFejP0oAfdzgfineN4XQwJGBlR2/OMCkQJxynyq+TfgXhtVmvmzPnZx 67 | D4cy 68 | -----END CERTIFICATE----- 69 | """.data(using: .utf8)! 70 | 71 | func testDecodingAlternativeNamesIPv6() throws { 72 | let x509 = try X509Certificate(data: samplePEMcertificateDataIPv6) 73 | let subjectAlternativeNames = x509.subjectAlternativeNames 74 | XCTAssertTrue(subjectAlternativeNames.contains("1.2.3.4")) 75 | XCTAssertTrue(subjectAlternativeNames.contains("10.150.200.250")) 76 | XCTAssertTrue(subjectAlternativeNames.contains("2001:4860:4860:0:0:0:0:64")) 77 | XCTAssertTrue(subjectAlternativeNames.contains("2620:100:6040:18:0:0:a27d:f812")) 78 | } 79 | 80 | let samplePEMcertificateDataIPv6 = """ 81 | -----BEGIN CERTIFICATE----- 82 | MIIC0zCCAbugAwIBAgIEZclRLDANBgkqhkiG9w0BAQsFADAMMQowCAYDVQQDDAFB 83 | MB4XDTI0MDIxMTIyNTg1MloXDTI1MDIxMDIyNTg1MlowDDEKMAgGA1UEAwwBQTCC 84 | ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANu3sr9zJbJeQ0uvx0s3K6Ws 85 | LPy6C7iud7W+RvTXFBTK2ZA+tVk81uzW6xQdbGrQYUV7LvDozbv8CEdf1SuUvhMN 86 | T4N7YTP6Od2qKjfXTSCpUgE2EYF45LqRZAcTyZ+8QRpYgeKTLObnIJzikOghToKN 87 | 9Q74jB7WIO3sb3QeXGTlTmHgERVrsFNy3g6eJ1cFCM/1ghB1Ln6ucgh7AH+zL2Bl 88 | +mOG2/sMAFwJxydI37u3Ua6TW73a5PzNftDsz2pAWBy65nHYDO1IibLdWT1nizz1 89 | AG1qMzfDUA6OXs65XhQI8PJzF844CPmoF0YKARzk+0zBCiJadC7RLXo0fS7zxDMC 90 | AwEAAaM9MDswOQYDVR0RBDIwMIcEAQIDBIcECpbI+ocQIAFIYEhgAAAAAAAAAAAA 91 | ZIcQJiABAGBAABgAAAAAon34EjANBgkqhkiG9w0BAQsFAAOCAQEAIYFr0B2TswUM 92 | 5eD4KWbC390E+6CklGS3xcFTybXjiXWjyMS1116+jENgLJ3ZDQhVIgEPuLRiQbym 93 | 0M83e4MdzqgClno8wMHBIYexQh89Ci7/Lr/5YJUnC/uEpbtEGFBzbJYpQFhVLgRV 94 | +vhb5It38fBEM2wfrH652oBecsznc1z5iTNW+h4OnxMd6CIPKOPsFyX2CSyWiYdS 95 | za2e5vkcu62kqQ/1F9RyG47xusH/6hWwtXz5eaFmoFyk/rjTyhrfR1Dc/xfQQOdM 96 | ta/QiONVU30kkSfIpidpiP6+ORHx6AbglB1v+jAVpKAPura4T3Yjov7zE7x5zEOL 97 | NnOQ2YQKSA== 98 | -----END CERTIFICATE----- 99 | """.data(using: .utf8)! 100 | 101 | 102 | // MARK: - Test Subject with multiple domain component 103 | 104 | func testDoubleDomain() { 105 | do { 106 | let x509 = try X509Certificate(data: samplePEMcertificateData2) 107 | let dc = x509.subject(oid: .domainComponent) 108 | XCTAssertEqual(dc, ["domain2", "domain1"]) 109 | } catch { 110 | XCTFail(error.localizedDescription) 111 | } 112 | } 113 | 114 | let samplePEMcertificateData2 = 115 | """ 116 | -----BEGIN CERTIFICATE----- 117 | MIIBcjCCARigAwIBAgIEX6WPwDAKBggqhkjOPQQDAjBBMRcwFQYKCZImiZPyLGQB 118 | GRYHZG9tYWluMjEXMBUGCgmSJomT8ixkARkWB2RvbWFpbjExDTALBgNVBAMMBG5h 119 | bWUwHhcNMjAxMTA2MTgwMjQwWhcNMjExMTA2MTgwMjQwWjBBMRcwFQYKCZImiZPy 120 | LGQBGRYHZG9tYWluMjEXMBUGCgmSJomT8ixkARkWB2RvbWFpbjExDTALBgNVBAMM 121 | BG5hbWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATCCOGTRqbJ3tQQxl2gVsY+ 122 | Pk4VvAkW+D6lDSpDLadEBOlRirr3WrTsKPVTtsKA5YIt1e/zBxl9g5OgTJq8ktjI 123 | MAoGCCqGSM49BAMCA0gAMEUCIB5Br1e3dUwbTYubxHkDHDJqmG48sbdMZbuB19oD 124 | wThoAiEA0eEpfby8LRQcFlybk0LKyRrl1m4DGyMfgN/5bJaN9zU= 125 | -----END CERTIFICATE----- 126 | """.data(using: .utf8)! 127 | } 128 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1DecoderCertificateV1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DecoderExtensions.swift 3 | // ASN1DecoderTests 4 | // 5 | // Copyright © 2020 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | @testable import ASN1Decoder 27 | 28 | class ASN1DecoderCertificateV1: XCTestCase { 29 | 30 | func getCertificate() throws -> X509Certificate { 31 | let certificateData = samplePEMcertificate.data(using: .utf8)! 32 | return try X509Certificate(data: certificateData) 33 | } 34 | 35 | func testVersion1() { 36 | do { 37 | let x509 = try getCertificate() 38 | XCTAssertEqual(x509.version, 1) 39 | XCTAssertEqual(x509.subjectDistinguishedName, "C=NL, O=development, CN=localhost") 40 | XCTAssertEqual(x509.publicKey?.algName, "rsaEncryption") 41 | } catch { 42 | XCTFail("\(error)") 43 | } 44 | } 45 | 46 | let samplePEMcertificate = """ 47 | -----BEGIN CERTIFICATE----- 48 | MIIC6jCCAdICCQCII7IrJYChQDANBgkqhkiG9w0BAQsFADA3MQswCQYDVQQGEwJO 49 | TDEUMBIGA1UECgwLZGV2ZWxvcG1lbnQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0y 50 | MTA2MjgxMDI5MjdaFw0yMjA2MjgxMDI5MjdaMDcxCzAJBgNVBAYTAk5MMRQwEgYD 51 | VQQKDAtkZXZlbG9wbWVudDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG 52 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtHT7F6AIFWuE+RqXXXMsm/LwWapsZzBBkSJ/ 53 | xWskFPM7EEkRXMoFRcE5DRxqhY2SUVc55JYMCITIL1IyavVe6eDk1cjf4Iiw09Tj 54 | M2vbn/sZI+ACcK8lQ29196753OqmYPKL30vZ7NYG85/1aNBeI4ZEgfD7nyExTw69 55 | 2VgRJok3LBiJkjfmml2EdGBTeG//yEGuvOkUbu0KOzi/eax/jNsn2V5ABEnblVIU 56 | mENeWoKQeP81gBZeZ3tvAp0eruNXdjQbxjz3SODYPJwY319sxF7y/LeRROmV+ZAw 57 | 6IH5rrKa7yqkMk5oNVSJ28IiZTRyp3NwKZtTo+q3ae+ueAmZewIDAQABMA0GCSqG 58 | SIb3DQEBCwUAA4IBAQBBNivz9HK3GU6Ey5zj6xpKEjq3CdUNHOoPMirBpdn3PT/J 59 | T4qHKzWxFup0hhEpcHKyA42SkvqA6uUqV2CsaNR9tlBxzsr1V/z23h16jXarTMOl 60 | XymCgFrvHOhsRF5qxywIH338oMhbM0B+N0huFpRRx5li86bIWpmg2zm82gVWuFGs 61 | cFabGVNEQS87vlCVr2/fLwKhemv1CVw9ZrkHU3BPA08r8Ki6f30ByqiYvu2xC7a9 62 | dHTz8vBLPJO0xSKwMj4Dc0qTKtG96i+j+EUdUxhhr1VKZdL5geuvB0UUx/aNItnD 63 | b53SqLt8wT0gDWIn4pNDgyONusSfSBHdOjsE8oqf 64 | -----END CERTIFICATE----- 65 | """ 66 | 67 | } 68 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1DecoderExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DecoderExtensions.swift 3 | // ASN1DecoderTests 4 | // 5 | // Copyright © 2020 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | @testable import ASN1Decoder 27 | 28 | class ASN1DecoderX509Extensions: XCTestCase { 29 | 30 | func getCertificate() throws -> X509Certificate { 31 | let certificateData = samplePEMcertificate.data(using: .utf8)! 32 | return try X509Certificate(data: certificateData) 33 | } 34 | 35 | func testAuthorityKeyIdentifier() { 36 | do { 37 | let x509 = try getCertificate() 38 | 39 | if let ext = x509.extensionObject(oid: .authorityKeyIdentifier) as? X509Certificate.AuthorityKeyIdentifierExtension { 40 | XCTAssertEqual(ext.keyIdentifier?.hexEncodedString(), "76028647F1F6A396C9BFD22D8E300E28398C588B") 41 | XCTAssertEqual(ext.serialNumber?.hexEncodedString(), "5FA31045") 42 | 43 | let certificateIssuer = ext.certificateIssuer 44 | XCTAssertTrue(certificateIssuer?.contains("www.hostname.net") == true) 45 | XCTAssertTrue(certificateIssuer?.contains("192.168.1.99") == true) 46 | XCTAssertTrue(certificateIssuer?.contains("CN=EXTENSION TEST") == true) 47 | } 48 | } catch { 49 | XCTFail("\(error)") 50 | } 51 | } 52 | 53 | func testBasicConstraint() { 54 | do { 55 | let x509 = try getCertificate() 56 | 57 | if let ext = x509.extensionObject(oid: .basicConstraints) as? X509Certificate.BasicConstraintExtension { 58 | XCTAssertEqual(ext.isCA, true) 59 | XCTAssertEqual(ext.pathLenConstraint, 3) 60 | } else { 61 | XCTFail("Extension not found") 62 | } 63 | } catch { 64 | XCTFail("\(error)") 65 | } 66 | } 67 | 68 | func testExtKeyUsage() { 69 | do { 70 | let x509 = try getCertificate() 71 | let extKeyUsage = x509.extendedKeyUsage 72 | XCTAssert(extKeyUsage.contains("1.3.6.1.5.5.7.3.2")) // TLS Web Client Authentication 73 | XCTAssert(extKeyUsage.contains("1.3.6.1.5.5.7.3.4")) // E-mail Protection 74 | XCTAssert(extKeyUsage.contains("1.3.6.1.4.1.311.10.3.4")) // Encrypted File System 75 | } catch { 76 | XCTFail("\(error)") 77 | } 78 | } 79 | 80 | func testSubjectKeyIdentifier() { 81 | do { 82 | let x509 = try getCertificate() 83 | 84 | if let ext = x509.extensionObject(oid: .subjectKeyIdentifier), let value = ext.value as? Data { 85 | XCTAssertEqual(value.hexEncodedString(), "76028647F1F6A396C9BFD22D8E300E28398C588B") 86 | } else { 87 | XCTFail("Extension not found") 88 | } 89 | } catch { 90 | XCTFail("\(error)") 91 | } 92 | } 93 | 94 | func testCertificatePolicies() { 95 | do { 96 | let certificateData = samplePEMcertificateApple.data(using: .utf8)! 97 | let x509 = try X509Certificate(data: certificateData) 98 | 99 | if let ext = x509.extensionObject(oid: .certificatePolicies) as? X509Certificate.CertificatePoliciesExtension, 100 | let policies = ext.policies { 101 | 102 | XCTAssertEqual(policies[0].oid, "2.16.840.1.114412.2.1") 103 | XCTAssertEqual(policies[0].qualifiers?[0].oid, "1.3.6.1.5.5.7.2.1") 104 | XCTAssertEqual(policies[0].qualifiers?[0].value, "https://www.digicert.com/CPS") 105 | 106 | XCTAssertEqual(policies[1].oid, "2.23.140.1.1") 107 | 108 | } else { 109 | XCTFail("Extension not found") 110 | } 111 | } catch { 112 | XCTFail("\(error)") 113 | } 114 | } 115 | 116 | func testCertificateCRL() { 117 | do { 118 | let certificateData = samplePEMcertificateApple.data(using: .utf8)! 119 | let x509 = try X509Certificate(data: certificateData) 120 | 121 | if let ext = x509.extensionObject(oid: .cRLDistributionPoints) as? X509Certificate.CRLDistributionPointsExtension, 122 | let crls = ext.crls { 123 | 124 | XCTAssertTrue(crls.contains("http://crl3.digicert.com/DigiCertSHA2ExtendedValidationServerCA-3.crl")) 125 | XCTAssertTrue(crls.contains("http://crl4.digicert.com/DigiCertSHA2ExtendedValidationServerCA-3.crl")) 126 | 127 | } else { 128 | XCTFail("Extension not found") 129 | } 130 | } catch { 131 | XCTFail("\(error)") 132 | } 133 | } 134 | 135 | func testAuthorityInfoAccess() { 136 | do { 137 | let certificateData = samplePEMcertificateApple.data(using: .utf8)! 138 | let x509 = try X509Certificate(data: certificateData) 139 | 140 | if let ext = x509.extensionObject(oid: .authorityInfoAccess) as? X509Certificate.AuthorityInfoAccessExtension, 141 | let infoAccess = ext.infoAccess { 142 | 143 | XCTAssertEqual(infoAccess[0].method, "1.3.6.1.5.5.7.48.1") 144 | XCTAssertEqual(infoAccess[0].location, "http://ocsp.digicert.com") 145 | 146 | XCTAssertEqual(infoAccess[1].method, "1.3.6.1.5.5.7.48.2") 147 | XCTAssertEqual(infoAccess[1].location, "http://cacerts.digicert.com/DigiCertSHA2ExtendedValidationServerCA-3.crt") 148 | 149 | } else { 150 | XCTFail("Extension not found") 151 | } 152 | } catch { 153 | XCTFail("\(error)") 154 | } 155 | } 156 | 157 | let samplePEMcertificate = """ 158 | -----BEGIN CERTIFICATE----- 159 | MIIDejCCAmKgAwIBAgIEX6MQRTANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5F 160 | WFRFTlNJT04gVEVTVDAeFw0yMDExMDQyMDM0MTNaFw0yMTExMDQyMDM0MTNaMBkx 161 | FzAVBgNVBAMMDkVYVEVOU0lPTiBURVNUMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 162 | MIIBCgKCAQEA0Q9U9Qd7ELpyx46zI6tL7UtIGDv48BaDKPO7JkcOyXE2OunO+/Rd 163 | U7nrJP4t7KVoFDYAbzHfy3ViaCN/zzcHUQV5VLtg3ydaM3ruJ8lWZjj1nsH2hPxl 164 | Rth6BttV64mRSUGc4w+OEoj/WMrErCS6uXs0Xjd/1+6h3MUnBua5pwhh59Wj9dCo 165 | 8Dn8gcsnFHy/GVMSfwSdSxWpuMfyPNfzQ4jffoneZv/xYpeYqFYdqs5cODqPsfqr 166 | t8+T8Xh30pbDAVwQ0t7EMb+IH5oXIkujEDx+FViyqya/H+E5IXuMecFshD4Rebp5 167 | f/9eE9Ct7BbfEeBOglzyYxB118+CswtcIwIDAQABo4HJMIHGMA8GA1UdEwQIMAYB 168 | Af8CAQMwXAYDVR0jBFUwU4AUdgKGR/H2o5bJv9ItjjAOKDmMWIuhNaQbMBkxFzAV 169 | BgNVBAMMDkVYVEVOU0lPTiBURVNUghB3d3cuaG9zdG5hbWUubmV0hwTAqAFjggRf 170 | oxBFMB0GA1UdDgQWBBR2AoZH8fajlsm/0i2OMA4oOYxYizApBgNVHSUEIjAgBggr 171 | BgEFBQcDAgYIKwYBBQUHAwQGCisGAQQBgjcKAwQwCwYDVR0PBAQDAgEGMA0GCSqG 172 | SIb3DQEBCwUAA4IBAQBBVNtPF++n2KnL3sdezfx0BN1Thzz/k2D/amUnNcMNrUUj 173 | T4k0Nu6ZQZPxnjH8VNAel7eFpRaLOS/zS9B63695lFnOOzJSKec2i0uyl9hRAMf5 174 | HCoowRijyM9KfIHF8UQ2TSBJkL0Dbdhw6Yszq7JcGUj0g4mX/6c9MlsZFRfJ6S6I 175 | mavDHwPsTf6Abz9em4rMF4HVoDpoky/srDh5JsFHZ37uiWtlyUpk87UgNZI+1xA+ 176 | 3wCZU9yLMOYO7a2j7mLFSJobrN2BZPZYoFjto38XOkPpZxJSUWOPHekig1bH6Nwy 177 | EBbHNd47ucLIF9f7UWBbBxnl1tjp8VVqX6IBsYuS 178 | -----END CERTIFICATE----- 179 | """ 180 | 181 | let samplePEMcertificateApple = """ 182 | -----BEGIN CERTIFICATE----- 183 | MIIIBTCCBu2gAwIBAgIQA44/ngnX7cexgD90p0w1qzANBgkqhkiG9w0BAQsFADB5 184 | MQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xGTAXBgNVBAsT 185 | EHd3dy5kaWdpY2VydC5jb20xNjA0BgNVBAMTLURpZ2lDZXJ0IFNIQTIgRXh0ZW5k 186 | ZWQgVmFsaWRhdGlvbiBTZXJ2ZXIgQ0EtMzAeFw0yMDEwMDcwMDAwMDBaFw0yMTEw 187 | MDgxMjAwMDBaMIHHMR0wGwYDVQQPDBRQcml2YXRlIE9yZ2FuaXphdGlvbjETMBEG 188 | CysGAQQBgjc8AgEDEwJVUzEbMBkGCysGAQQBgjc8AgECEwpDYWxpZm9ybmlhMREw 189 | DwYDVQQFEwhDMDgwNjU5MjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju 190 | aWExEjAQBgNVBAcTCUN1cGVydGlubzETMBEGA1UEChMKQXBwbGUgSW5jLjEWMBQG 191 | A1UEAxMNd3d3LmFwcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC 192 | ggEBAMobHCF4FT1Az6N5P53PslOrqUH/PgahKWmKBEae+8QNVnrK5oDnr8bAv4tg 193 | ccqa6HYMBsibd7jzG+p+5zqEy6OIpZMEP2lmd8+uBtHZ4RAIeuAkmOdWlw9zaHtN 194 | aUYoJv8FgQzA2vwhcYFlmjnJ6Wg2NgJfgYC3fopb/jTQznYt2Ys+1BPA7OsPLHet 195 | Hnsg9tqSmP2J86fLUxYusLlivsjDKEDPjFxhd4+SPS8j8gqrZYIiuJjOusgAleRn 196 | NG525dHTLVGRvO/AyN74e8xGRQB22cswMelW/Q5o9Db5G1+IYWKPYKjeQ3tcwRVz 197 | 1AYSboWbUJwkv1/89GiVZ9W/RHECAwEAAaOCBDgwggQ0MB8GA1UdIwQYMBaAFM+F 198 | 8bw4GHg6VTP0VsrAaa13bruTMB0GA1UdDgQWBBQmH7tn0rlB7VcS548sc00Xi2tw 199 | jjA8BgNVHREENTAzghBpbWFnZXMuYXBwbGUuY29tgg13d3cuYXBwbGUuY29tghB3 200 | d3cuYXBwbGUuY29tLmNuMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF 201 | BQcDAQYIKwYBBQUHAwIwgaUGA1UdHwSBnTCBmjBLoEmgR4ZFaHR0cDovL2NybDMu 202 | ZGlnaWNlcnQuY29tL0RpZ2lDZXJ0U0hBMkV4dGVuZGVkVmFsaWRhdGlvblNlcnZl 203 | ckNBLTMuY3JsMEugSaBHhkVodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNl 204 | cnRTSEEyRXh0ZW5kZWRWYWxpZGF0aW9uU2VydmVyQ0EtMy5jcmwwSwYDVR0gBEQw 205 | QjA3BglghkgBhv1sAgEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl 206 | cnQuY29tL0NQUzAHBgVngQwBATCBigYIKwYBBQUHAQEEfjB8MCQGCCsGAQUFBzAB 207 | hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wVAYIKwYBBQUHMAKGSGh0dHA6Ly9j 208 | YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJFeHRlbmRlZFZhbGlkYXRp 209 | b25TZXJ2ZXJDQS0zLmNydDAJBgNVHRMEAjAAMIIB9gYKKwYBBAHWeQIEAgSCAeYE 210 | ggHiAeAAdwD2XJQv0XcwIhRUGAgwlFaO400TGTO/3wwvIAvMTvFk4wAAAXUE5RIw 211 | AAAEAwBIMEYCIQDfwCPGlYS5Fzl5E9qSJfXEJZKk3Bocy3gRZ3VnSetQ9wIhAJTV 212 | W7NpwxMvgje6f3Bl4pD/8LONvdNOfQhA/RRf/mZgAHUAXNxDkv7mq0VEsV6a1Fbm 213 | EDf71fpH3KFzlLJe5vbHDsoAAAF1BOUShAAABAMARjBEAiBym6EmF1g6CDmqOaKZ 214 | LiOpKJ310wVgWfezVSZoqZCUTQIgbMkp3ZqwIJlxECUiFNCcWvOvXDnRjTmZRNnU 215 | gl9wFj8AdgBWFAaaL9fC7NP14b1Esj7HRna5vJkRXMDvlJhV1onQ3QAAAXUE5RQW 216 | AAAEAwBHMEUCIQDtvAk0Fs6QOodDRWXG8pwviOAD0A3P8MrepljlmvCC+AIgaYPc 217 | dA2gwQbMR+muIHIw6zzpDE2rgBYUmmirGfGXGq8AdgCkuQmQtBhYFIe7E6LMZ3AK 218 | PDWYBPkb37jjd80OyA3cEAAAAXUE5RThAAAEAwBHMEUCIQDVJYiGuX96WaI2ry/P 219 | uChTJsmpiTxPwJItHL0YMJ+HmQIga60LicX5sIxA7jVWLe1skZQvA8SM8dTY9mjf 220 | 5qpP9U8wDQYJKoZIhvcNAQELBQADggEBACLAg6hBZGjc2m3vB0YyMlclnv5dQ0vy 221 | F8JfHobkrFQ7O+mW35LiDY/ZIF9KBLGY5eOtHSYX8+Ktt1bcRilwq9Vjip80Atb4 222 | Wpzq9tM6zFx+oxVGv1YsOWdCir94838tP0d0ILqoyqUWVu6HgyJBu3ZEABaSZcIx 223 | 4TjJ9LhOtzyO44mcHqgNXiA7IdK3TPs39iAmVx3+3PQmwjbGGjKgR0rORIGUuCa6 224 | YVqR0ad1wWG4M24HgTR/+d40C4JNVY3FFptUvCCw4yD5Jzk2duFsAmC9bZxpTbzc 225 | hoOQIW3CEt8hUquiqBBvOv+7YI3JrMHBsLt9T44YYiKC+XkFnh7yG9E= 226 | -----END CERTIFICATE----- 227 | """ 228 | } 229 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1DecoderPkcs7SignatureTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright © 2020 Alexander Heinrich & Filippo Maguolo. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in all 12 | // copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | // SOFTWARE. 21 | 22 | import XCTest 23 | @testable import ASN1Decoder 24 | 25 | class ASN1SignatureTests: XCTestCase { 26 | 27 | func testGetSignatureFromPKCS7() throws { 28 | let signatures = try PKCS7(data: self.asn1TestFile).signatures 29 | XCTAssertEqual(signatures?.count, 1) 30 | let signature = signatures?.first 31 | XCTAssertNotNil(signature?.signatureData) 32 | XCTAssertEqual(signature?.disgestAlgorithmName, "sha1") 33 | XCTAssertEqual(signature?.signatureAlgorithmName, "rsaEncryption") 34 | XCTAssertEqual(signature?.digestAlgorithmOID, OID.sha1) 35 | XCTAssertEqual(signature?.signatureAlgorithmOID, OID.rsaEncryption) 36 | } 37 | 38 | private var asn1TestFile: Data { 39 | let b64String = "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCAJIAEggqWPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KCTxrZXk+QXBwSUROYW1lPC9rZXk+Cgk8c3RyaW5nPnNlbGZzaWduZWQ6IGFueSBhcHA8L3N0cmluZz4KCTxrZXk+QXBwbGljYXRpb25JZGVudGlmaWVyUHJlZml4PC9rZXk+Cgk8YXJyYXk+CgkJPHN0cmluZz5TRUxGU0lHTkVEPC9zdHJpbmc+Cgk8L2FycmF5PgoJPGtleT5DcmVhdGlvbkRhdGU8L2tleT4KCTxkYXRlPjIwMjAtMDUtMjVUMDc6Mjk6MzJaPC9kYXRlPgoJPGtleT5QbGF0Zm9ybTwva2V5PgoJPGFycmF5PgoJCTxzdHJpbmc+aU9TPC9zdHJpbmc+Cgk8L2FycmF5PgoJPGtleT5EZXZlbG9wZXJDZXJ0aWZpY2F0ZXM8L2tleT4KCTxhcnJheT4KCQk8ZGF0YT5NSUlEU1RDQ0FqR2dBd0lCQWdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURCS01TWXdKQVlEVlFRRERCMXBVR2h2Ym1VZ1JHVjJaV3h2Y0dWeU9pQlRaV3htSUZOcFoyNWxjakVUTUJFR0ExVUVDd3dLVTBWTVJsTkpSMDVGUkRFTE1Ba0dBMVVFQmhNQ1JFVXdIaGNOTWpBd05USTFNRGN5T1RNeVdoY05NekF3TlRJMU1EY3lPVE15V2pCS01TWXdKQVlEVlFRRERCMXBVR2h2Ym1VZ1JHVjJaV3h2Y0dWeU9pQlRaV3htSUZOcFoyNWxjakVUTUJFR0ExVUVDd3dLVTBWTVJsTkpSMDVGUkRFTE1Ba0dBMVVFQmhNQ1JFVXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDeXV0dXlaRHVQaVFrbGoxSWdHdk16Z0NrZGU5NXNHN1JZaFliLzd6YXRzcTFSNksxeWsxOXo4cEIwMUhTUzZSNXY3SjJoUThMenFwZEQ1YTF4WFo3RXI0a2s0MDdCbkN0dFdsVXZEVFNBajU3b2FBdW1KUzIvZ2RmODBmL2E1ZlhneWJraGhyZ05raFFYc0cxSkdZUStHOXIzZzhtMHpQbXg2QmxyM0ZuMWtpR0ZwWXJRRjB4cExNVElZRm5pZS9zV3VnMG41VUsyZlBmOEhWL3QzeHMxOWR2T3RXVzlBNGpxY212bXlXYWFpakszNGM5VmMzYkVuYWJ4bjd4ZExBT0xJWHVqSHdHUVMzMXhTT3F1aG5nZlNRVFgrRFR6b1NzUjhvdGpSaVgyTzZocjlQNzNZSDJhbXZmMFZuRVJkTFQ2MmFBNEQxZVZ5U1hXU3JHeE04UVJBZ01CQUFHak9qQTRNQTRHQTFVZER3RUIvd1FFQXdJSGdEQW1CZ05WSFNVQkFmOEVIREFhQmdnckJnRUZCUWNEQkFZSUt3WUJCUVVIQXdNR0JGVWRKUUF3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQURKL0FhYjdpVnArS1ZiWlpXR0VXVFd3cWFQbXArYUtEei8yeW0wTFBEUCtVMXdaNFUrK0dPNUJKRVY0ZU5BeEZNNlRaODJlV2gzL080ZGVadGFkMExpVTh5d0NWQXVBcWVBNEQ3VFRUVDBKTlRKUGtZSVliWVhDUHc5SE8xdVAvQVhNSXJJQllFRmpRemp2YnoyaGJMVlFlOWxrQjlTUnlBM1ZEbkpLYkdScVpHNlExajZFNHFSejdvcGZheUFib1dGZzRZTTJXdGpXZGgxTUU0MzJsTWlNZGpJNlAxYUdNaXdoc2h6MUlkMDNFcS9sTHRzWmh4L212SVVDN3ExamN2cVFVRmlQV2FsQmhoSE1ubWdiM0lzY0g3c1VxbzRCSDk1Tm1MUlJ0cEhmMTFHWEpGUVpEcURkQ1R0ei90V3ZWeFl0U1VJYWQ4cEIyVTQrSVRraldBQT08L2RhdGE+Cgk8L2FycmF5PgoJPGtleT5FbnRpdGxlbWVudHM8L2tleT4KCTxkaWN0PgoJCTxrZXk+a2V5Y2hhaW4tYWNjZXNzLWdyb3Vwczwva2V5PgoJCTxhcnJheT4KCQkJPHN0cmluZz5TRUxGU0lHTkVELio8L3N0cmluZz4KCQk8L2FycmF5PgoJCTxrZXk+Z2V0LXRhc2stYWxsb3c8L2tleT4KCQk8dHJ1ZS8+CgkJPGtleT5hcHBsaWNhdGlvbi1pZGVudGlmaWVyPC9rZXk+CgkJPHN0cmluZz5TRUxGU0lHTkVELio8L3N0cmluZz4KCQk8a2V5PmNvbS5hcHBsZS5kZXZlbG9wZXIuYXNzb2NpYXRlZC1kb21haW5zPC9rZXk+CgkJPHN0cmluZz4qPC9zdHJpbmc+CgkJPGtleT5jb20uYXBwbGUuZGV2ZWxvcGVyLnRlYW0taWRlbnRpZmllcjwva2V5PgoJCTxzdHJpbmc+U0VMRlNJR05FRDwvc3RyaW5nPgoJCTxrZXk+YXBzLWVudmlyb25tZW50PC9rZXk+CgkJPHN0cmluZz5wcm9kdWN0aW9uPC9zdHJpbmc+CgkJPGtleT5jb20uYXBwbGUuZGV2ZWxvcGVyLmV4cG9zdXJlLW5vdGlmaWNhdGlvbjwva2V5PgoJCTx0cnVlLz4KCQk8a2V5PmNvbS5hcHBsZS5kZXZlbG9wZXIuZXhwb3N1cmUtbm90aWZpY2F0aW9uLXRlc3Q8L2tleT4KCQk8dHJ1ZS8+Cgk8L2RpY3Q+Cgk8a2V5PkV4cGlyYXRpb25EYXRlPC9rZXk+Cgk8ZGF0ZT4yMDMwLTA1LTI1VDA3OjI5OjMyWjwvZGF0ZT4KCTxrZXk+TmFtZTwva2V5PgoJPHN0cmluZz5ETyBOT1QgVVNFOiBvbmx5IGZvciBkdW1teSBzaWduaW5nPC9zdHJpbmc+Cgk8a2V5PlByb3Zpc2lvbmVkRGV2aWNlczwva2V5PgoJPGFycmF5Lz4KCTxrZXk+VGVhbUlkZW50aWZpZXI8L2tleT4KCTxhcnJheT4KCQk8c3RyaW5nPlNFTEZTSUdORUQ8L3N0cmluZz4KCTwvYXJyYXk+Cgk8a2V5PlRlYW1OYW1lPC9rZXk+Cgk8c3RyaW5nPlNlbGZzaWduZXJzIHVuaXRlZDwvc3RyaW5nPgoJPGtleT5UaW1lVG9MaXZlPC9rZXk+Cgk8aW50ZWdlcj4zNjUyPC9pbnRlZ2VyPgoJPGtleT5VVUlEPC9rZXk+Cgk8c3RyaW5nPkI1QzI5MDZELUQ2RUUtNDc2RS1BRjE3LUQ5OUFFMTQ2NDRBQTwvc3Ry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" 40 | 41 | return Data(base64Encoded: b64String)! 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1DecoderX509DecoderTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DecoderX509DecoderTests.swift 3 | // ASN1DecoderTests 4 | // 5 | // Copyright © 2023 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | @testable import ASN1Decoder 27 | 28 | class ASN1DecoderX509DecoderTests: XCTestCase { 29 | 30 | func testDecodingPEM() throws { 31 | let x509 = try X509Certificate(data: certPEM) 32 | XCTAssertEqual(x509.version, 3) 33 | XCTAssertEqual( 34 | x509.serialNumber?.hexEncodedString(), 35 | "0836BAA2556864172078584638D85C34" 36 | ) 37 | XCTAssertEqual( 38 | x509.subjectDistinguishedName, 39 | "businessCategory=Private Organization, jurisdictionC=US, jurisdictionST=Utah, SERIALNUMBER=5299537-0142, C=US, ST=Utah, L=Lehi, O=\"DigiCert, Inc.\", OU=SRE, CN=www.digicert.com" 40 | ) 41 | XCTAssertEqual(x509.subject(oid: .commonName)?.first, "www.digicert.com") 42 | XCTAssertEqual(x509.subject(oid: .serialNumber), ["5299537-0142"]) 43 | XCTAssertEqual(x509.subject(oid: .organizationName), ["DigiCert, Inc."]) 44 | } 45 | 46 | func testDecodingDER() { 47 | do { 48 | let x509 = try X509Certificate(data: certDER) 49 | 50 | let serialNumber = x509.serialNumber?.hexEncodedString() ?? "" 51 | XCTAssertEqual(serialNumber, "59A2F004") 52 | 53 | let subject = x509.subjectDistinguishedName ?? "" 54 | XCTAssertEqual(subject, "C=US, L=New York, E=john@mail.com, CN=John Smith") 55 | 56 | } catch { 57 | XCTFail(error.localizedDescription) 58 | } 59 | } 60 | 61 | let certDER = """ 62 | MIIDMzCCAhugAwIBAgIEWaLwBDANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJV 63 | UzERMA8GA1UEBwwITmV3IFlvcmsxHDAaBgkqhkiG9w0BCQEWDWpvaG5AbWFpbC5j 64 | b20xEzARBgNVBAMMCkpvaG4gU21pdGgwHhcNMTcwODI3MTYxNTAwWhcNMTgwODI3 65 | MTYxNTAwWjBTMQswCQYDVQQGEwJVUzERMA8GA1UEBwwITmV3IFlvcmsxHDAaBgkq 66 | hkiG9w0BCQEWDWpvaG5AbWFpbC5jb20xEzARBgNVBAMMCkpvaG4gU21pdGgwggEi 67 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHc/RdKvcz+Sakwykuq/+mJZCQ 68 | ELYYk3ceVrYOwefaFLent4JU+/ATm+CQFXqyiQM1BTtXUwA3gG0sCufMUG5wkHN0 69 | 86KwclYhzPRZNGtLW2QshvvaN2wE4HxbFJ/DUUHPGuIlzewfecg/ZG9CwGeb/HQ4 70 | Qx+BI/U7JXykyNHFwMQrS5hGmvLH7MxSYiqt8X2VZ7vabxdqnvpufK34SyVQXkfR 71 | twLNj7GO807HNQ5EGFw1hxJN3tBXG4z+1eq4rgy1RJY7c6ntkzOczrqw7Ut4OUmC 72 | RjAEggqPrG6R94D2f8vgEXB42TPSEKWwHi6/RAEZ1WO5YsDmLHVNxp8FvThVAgMB 73 | AAGjDzANMAsGA1UdDwQEAwIGQDANBgkqhkiG9w0BAQsFAAOCAQEAhlIaMaE9YTJU 74 | uSCy0LAd+nHzuTdokgDCXdT75KtsiNtQHQIDtLhdJGYUzlWUwY8SQWytvJORKi3q 75 | rA45oLwSJjVY4hZuNcaWleDumnHc4rbK9o6GJhEk/T49Vrc9q4CNX0/siPBsHwXd 76 | rqrLOR00yfrMYCPAUCryPbdx/IPbo8Z3kvlQHn8cqOjgqdwuy7PTMIMz6sCsBcV0 77 | OeAp80GDRAHpjB3qYhzhebiRiM+Bbqva6f4bxNmDNQtL0jt0a8KeyQrFNdAhgjYk 78 | AKTucThCu1laJKGKABK90dMoLtbJFxfRhjzmjX9TJGYJgCnRNDDnXpVUOspv2YeH 79 | vC9gOdRhaA== 80 | """.dataFromBase64()! 81 | 82 | let certPEM = """ 83 | -----BEGIN CERTIFICATE----- 84 | MIIItzCCB5+gAwIBAgIQCDa6olVoZBcgeFhGONhcNDANBgkqhkiG9w0BAQsFADB1 85 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 86 | d3cuZGlnaWNlcnQuY29tMTQwMgYDVQQDEytEaWdpQ2VydCBTSEEyIEV4dGVuZGVk 87 | IFZhbGlkYXRpb24gU2VydmVyIENBMB4XDTE4MDYyNjAwMDAwMFoXDTIwMDYzMDEy 88 | MDAwMFowgc8xHTAbBgNVBA8MFFByaXZhdGUgT3JnYW5pemF0aW9uMRMwEQYLKwYB 89 | BAGCNzwCAQMTAlVTMRUwEwYLKwYBBAGCNzwCAQITBFV0YWgxFTATBgNVBAUTDDUy 90 | OTk1MzctMDE0MjELMAkGA1UEBhMCVVMxDTALBgNVBAgTBFV0YWgxDTALBgNVBAcT 91 | BExlaGkxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMQwwCgYDVQQLEwNTUkUxGTAX 92 | BgNVBAMTEHd3dy5kaWdpY2VydC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw 93 | ggIKAoICAQDOn4XKOTAwt/aYabScEF1QOyVj0OVo1NmlyizWNZWyPg0pi53ggUoE 94 | 98CeNUkz+6scEYqWNY6l3qKB56pJJIqNQmo9NoWO8k2G/jTIjFFGqNWYIq23i4+H 95 | qaXi1/H/aWFgazk1qkyyAOQQA/p56bG9m5Ok/IBM/BZnLqVJLGJOx9ihgG1dI9Dr 96 | 6vap+8QaPRau3t9sEd2cxe4Ix7gLdaYG3vxsYf3BycKTSKtyrbkX1Qy0dsSxy+GC 97 | M2ETxE1gMa7vRomQ/ZoZo8Ib55kFp6lIT6UOOkkdyiJdpWPXIZZlsZR5wkegWDsJ 98 | P7Xv7nE0WMkY1+05iNYtrzZRhhlnBw2AoMGNI+tsBXLQKeZfWFmU30bhkzX99pmv 99 | IYJ3f1fQGLao44nQEjdknIvpm0HMgvagYCnQVnnhJStzyYz324flWLPSp57OQeNM 100 | tr6O5W0HdWyhUZU+D4R6wObYQMZ5biYjRhtAQjMg8EVQEfZzEdr0WGO5JRHLHyot 101 | 8tErXM9DiF5cCbzfcjeuoik2SHW+vbuPagMiHTM9+3lr0oRO+ZWwcM7fJvn1JfR2 102 | PDLAaI3QUv7OLhSH32UfQsk+1ICq05m2HwSxiAviDRl5De66MEZDdvu03sUAQTHv 103 | Wnw0Mr7Jgbjtn0DeUKLYwsRWg+spqoFTJHWGbb9RIb+3lxev7nIqOQIDAQABo4ID 104 | 5jCCA+IwHwYDVR0jBBgwFoAUPdNQpdagre7zSmAKZdMh1Pj41g8wHQYDVR0OBBYE 105 | FGywQ1b+PegS7NkS9WPVxMoHr7B2MIGRBgNVHREEgYkwgYaCEHd3dy5kaWdpY2Vy 106 | dC5jb22CDGRpZ2ljZXJ0LmNvbYIUY29udGVudC5kaWdpY2VydC5jb22CF3d3dy5v 107 | cmlnaW4uZGlnaWNlcnQuY29tghJsb2dpbi5kaWdpY2VydC5jb22CEGFwaS5kaWdp 108 | Y2VydC5jb22CD3dzLmRpZ2ljZXJ0LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0l 109 | BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMHUGA1UdHwRuMGwwNKAyoDCGLmh0dHA6 110 | Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zaGEyLWV2LXNlcnZlci1nMi5jcmwwNKAyoDCG 111 | Lmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWV2LXNlcnZlci1nMi5jcmww 112 | SwYDVR0gBEQwQjA3BglghkgBhv1sAgEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93 113 | d3cuZGlnaWNlcnQuY29tL0NQUzAHBgVngQwBATCBiAYIKwYBBQUHAQEEfDB6MCQG 114 | CCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wUgYIKwYBBQUHMAKG 115 | Rmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJFeHRlbmRl 116 | ZFZhbGlkYXRpb25TZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADCCAX4GCisGAQQB 117 | 1nkCBAIEggFuBIIBagFoAHYAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e 118 | 0YUAAAFkPjJMpQAABAMARzBFAiEAtvfxjDWBvpmqcq7+1X8lOyqKUJ8y5r31V4kV 119 | 4tzQSPcCIG8AAjqwQwLG6ObfgMe0B06AwM7K1JEAsyv8QP5r/EPUAHYAVhQGmi/X 120 | wuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0AAAFkPjJMFgAABAMARzBFAiEAkDHY 121 | U+MhibIUpVtiPAFyEzv35P3Vwn5ODseJmDI6dZkCICb4xzUGBy7aEQKJLOuM1F0A 122 | vMjEEB1OQQc9IWEY7UdPAHYAh3W/51l8+IxDmV+9827/Vo1HVjb/SrVgwbTq/16g 123 | gw8AAAFkPjJNlAAABAMARzBFAiBSMM3aExfTbMG1btIu+LCW9ALj4FT6scxUUgy5 124 | +OSH/gIhAPtqsgHiH6m6Qml1E9smajxYa773+YZdxMKbtEEe2ZV8MA0GCSqGSIb3 125 | DQEBCwUAA4IBAQCPcXLe1MjGJtwfihuI1S53GdokFAcl94ouoWxWd7ASfsufUyxs 126 | FroxDhNwxd8mQOH7V3ehZTiot6P+xMZOrYxgJx5CXbcLt07RZHT0w/Pf052gq7bP 127 | GbHsrjtlXq1MDn8c8D+Fnv2qSgE4f/9wQ1gMU4IKojaO4YH9FYoacA8puXUlK1pB 128 | CuCK0jJykyAtD9z4oTD/ZLBQOmTJ4VwJ5rHNCfdI8akR9OYYyx9GCbeWYv5JCcIy 129 | zPyvZe6ceICEnRGliU/EzryyWhq4Vx/zReBgoX6xOWfW1ZAota0etzo9pSWjOdrr 130 | j1I7q0bAhL1eUuXE8FSm6M8ZogW/ZYkOHE2u 131 | -----END CERTIFICATE----- 132 | """.data(using: .utf8)! 133 | } 134 | -------------------------------------------------------------------------------- /ASN1DecoderTests/ASN1DecoderX509SignatureTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASN1DecoderX509SignatureTests.swift 3 | // ASN1DecoderTests 4 | // 5 | // Copyright © 2023 Filippo Maguolo. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | 25 | import XCTest 26 | @testable import ASN1Decoder 27 | 28 | class ASN1DecoderX509SignatureTests: XCTestCase { 29 | 30 | func testSignatureRsa() { 31 | do { 32 | let x509 = try X509Certificate(data: certRsa) 33 | XCTAssertEqual( 34 | x509.signature?.hexEncodedString(), 35 | "86521A31A13D613254B920B2D0B01DFA71F3B937689200C25DD4FBE4AB6C88DB501D0203B4B85D246614CE5594C18F12416CADBC93912A2DEAAC0E39A0BC12263558E2166E35C69695E0EE9A71DCE2B6CAF68E86261124FD3E3D56B73DAB808D5F4FEC88F06C1F05DDAEAACB391D34C9FACC6023C0502AF23DB771FC83DBA3C67792F9501E7F1CA8E8E0A9DC2ECBB3D3308333EAC0AC05C57439E029F341834401E98C1DEA621CE179B89188CF816EABDAE9FE1BC4D983350B4BD23B746BC29EC90AC535D02182362400A4EE713842BB595A24A18A0012BDD1D3282ED6C91717D1863CE68D7F532466098029D13430E75E95543ACA6FD98787BC2F6039D46168" 36 | ) 37 | XCTAssertEqual(x509.sigAlgName, "sha256WithRSAEncryption") 38 | XCTAssertEqual(x509.sigAlgOID, "1.2.840.113549.1.1.11") 39 | XCTAssertEqual(x509.sigAlgParams?.hexEncodedString(), "0500") 40 | } catch { 41 | XCTFail(error.localizedDescription) 42 | } 43 | } 44 | 45 | func testSignaturePssRsa() { 46 | do { 47 | let x509 = try X509Certificate(data: certPssRsa) 48 | XCTAssertEqual( 49 | x509.signature?.hexEncodedString(), 50 | "5C61AA8EA4EBC052A5CC86CA663E16D3B2AE77BA9E2C596DF5369D7A8FDA2281016854E9DC25D9ADF1D30A3E5603CCE70C4A39F70E0EF7F26AC4C56E3A9B772EE3A5A665ACF6CBBFAE633D0972BF3EDC6D703B362A8E3227D66E1699EFA48F5BE85A7163918657FA7F061340C68146F2415FDAACA7687515BD4458923449E49D82A167B569D76249D3AD3CBEA7C1858DB8C6335703593006BE26C8153D859A2A6EE9D537E108046F44A648486DBCD721C9CAB8DFA1F3D2ADAF89B8C3D47B09624FE4A9E22DE012AD97EC79E467677B314A1FA31F2EC0618A392921C391F50B61A27051C3CAF3C8274078853C5178045F8A0CA7A1FB07B13CF107C5358062C92E" 51 | ) 52 | 53 | XCTAssertEqual(x509.sigAlgName, "rsaPSS") 54 | XCTAssertEqual(x509.sigAlgOID, "1.2.840.113549.1.1.10") 55 | XCTAssertEqual( 56 | x509.sigAlgParams?.hexEncodedString(), 57 | "3034A00F300D06096086480165030402020500A11C301A06092A864886F70D010108300D06096086480165030402020500A203020130" 58 | ) 59 | } catch { 60 | XCTFail(error.localizedDescription) 61 | } 62 | } 63 | 64 | let certRsa = """ 65 | -----BEGIN CERTIFICATE----- 66 | MIIDMzCCAhugAwIBAgIEWaLwBDANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJV 67 | UzERMA8GA1UEBwwITmV3IFlvcmsxHDAaBgkqhkiG9w0BCQEWDWpvaG5AbWFpbC5j 68 | b20xEzARBgNVBAMMCkpvaG4gU21pdGgwHhcNMTcwODI3MTYxNTAwWhcNMTgwODI3 69 | MTYxNTAwWjBTMQswCQYDVQQGEwJVUzERMA8GA1UEBwwITmV3IFlvcmsxHDAaBgkq 70 | hkiG9w0BCQEWDWpvaG5AbWFpbC5jb20xEzARBgNVBAMMCkpvaG4gU21pdGgwggEi 71 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHc/RdKvcz+Sakwykuq/+mJZCQ 72 | ELYYk3ceVrYOwefaFLent4JU+/ATm+CQFXqyiQM1BTtXUwA3gG0sCufMUG5wkHN0 73 | 86KwclYhzPRZNGtLW2QshvvaN2wE4HxbFJ/DUUHPGuIlzewfecg/ZG9CwGeb/HQ4 74 | Qx+BI/U7JXykyNHFwMQrS5hGmvLH7MxSYiqt8X2VZ7vabxdqnvpufK34SyVQXkfR 75 | twLNj7GO807HNQ5EGFw1hxJN3tBXG4z+1eq4rgy1RJY7c6ntkzOczrqw7Ut4OUmC 76 | RjAEggqPrG6R94D2f8vgEXB42TPSEKWwHi6/RAEZ1WO5YsDmLHVNxp8FvThVAgMB 77 | AAGjDzANMAsGA1UdDwQEAwIGQDANBgkqhkiG9w0BAQsFAAOCAQEAhlIaMaE9YTJU 78 | uSCy0LAd+nHzuTdokgDCXdT75KtsiNtQHQIDtLhdJGYUzlWUwY8SQWytvJORKi3q 79 | rA45oLwSJjVY4hZuNcaWleDumnHc4rbK9o6GJhEk/T49Vrc9q4CNX0/siPBsHwXd 80 | rqrLOR00yfrMYCPAUCryPbdx/IPbo8Z3kvlQHn8cqOjgqdwuy7PTMIMz6sCsBcV0 81 | OeAp80GDRAHpjB3qYhzhebiRiM+Bbqva6f4bxNmDNQtL0jt0a8KeyQrFNdAhgjYk 82 | AKTucThCu1laJKGKABK90dMoLtbJFxfRhjzmjX9TJGYJgCnRNDDnXpVUOspv2YeH 83 | vC9gOdRhaA== 84 | -----END CERTIFICATE----- 85 | """.data(using: .utf8)! 86 | 87 | let certPssRsa = 88 | """ 89 | -----BEGIN CERTIFICATE----- 90 | MIIDBDCCAbigAwIBAgIEZCSoaTBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQC 91 | AgUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATAwEDEOMAwGA1UE 92 | AwwFcnJycnIwHhcNMjMwMzI5MjEwNjQ5WhcNMjQwMzI4MjEwNjQ5WjAQMQ4wDAYD 93 | VQQDDAVycnJycjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOmPnB92 94 | dlTeDKcLy+9cGJR1haGNy53MsrVFZhStt450qtd0GE2B5s4UQu55qYqJpmRBeotX 95 | d7pAMSCiGe+G3zRbKCBguiVvQqdLrVpsyeVqzU1dZo7mj5l7o/1uQU/GaD5GISHm 96 | 2ilj4z2BIVAwzU/igBnaP5bkdG4ms35Hrjx9vZK5wKPbTicSBrNTmHALk6R22y3f 97 | rFjHQZLPky9V65K10aDXQqtBmXhYLMFI1KVeS3SuhRnAwkdudSeOIFBmwscJHPd3 98 | rCrWWjeoh+mV1j/pzSeLhkZpT3rm8FvXJCJoTjUo9P3VeqyyIF6yOhBpq9Qbo0zk 99 | SnpSBPIrlJ+EMAkCAwEAATBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAgUA 100 | oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATADggEBAFxhqo6k68BS 101 | pcyGymY+FtOyrne6nixZbfU2nXqP2iKBAWhU6dwl2a3x0wo+VgPM5wxKOfcODvfy 102 | asTFbjqbdy7jpaZlrPbLv65jPQlyvz7cbXA7NiqOMifWbhaZ76SPW+hacWORhlf6 103 | fwYTQMaBRvJBX9qsp2h1Fb1EWJI0SeSdgqFntWnXYknTrTy+p8GFjbjGM1cDWTAG 104 | vibIFT2Fmipu6dU34QgEb0SmSEhtvNchycq436Hz0q2vibjD1HsJYk/kqeIt4BKt 105 | l+x55GdnezFKH6MfLsBhijkpIcOR9QthonBRw8rzyCdAeIU8UXgEX4oMp6H7B7E8 106 | 8QfFNYBiyS4= 107 | -----END CERTIFICATE----- 108 | """.data(using: .utf8)! 109 | } 110 | -------------------------------------------------------------------------------- /ASN1DecoderTests/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // ASN1DecoderTests 4 | // 5 | // Created by Filippo Maguolo on 17/09/2023. 6 | // Copyright © 2023 Filippo Maguolo. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Data { 12 | func hexEncodedString(separation: String = "") -> String { 13 | return reduce("") {$0 + String(format: "%02X\(separation)", $1)} 14 | } 15 | } 16 | 17 | extension String { 18 | func dataFromBase64() -> Data? { 19 | Data(base64Encoded: self, options: .ignoreUnknownCharacters) 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /ASN1DecoderTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Filippo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "ASN1Decoder", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "ASN1Decoder", 12 | targets: ["ASN1Decoder"]) 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "ASN1Decoder", 23 | dependencies: [], 24 | path: "ASN1Decoder"), 25 | .testTarget( 26 | name: "ASN1DecoderTests", 27 | dependencies: ["ASN1Decoder"], 28 | path: "ASN1DecoderTests") 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASN1Decoder 2 | ASN1 DER Decoder for X.509 Certificate 3 | 4 | ## Requirements 5 | 6 | - iOS 11.0+ | macOS 10.13+ 7 | - Xcode 14 8 | 9 | ## Integration 10 | 11 | #### Swift Packet Manager 12 | 13 | Using the url: https://github.com/filom/ASN1Decoder 14 | 15 | 16 | #### CocoaPods (iOS 11+, OS X 10.13+, tvOS 13+) 17 | 18 | You can use [CocoaPods](http://cocoapods.org/) to install `ASN1Decoder` by adding it to your `Podfile`: 19 | 20 | ```ruby 21 | platform :ios, '11.0' 22 | use_frameworks! 23 | 24 | target 'MyApp' do 25 | pod 'ASN1Decoder' 26 | end 27 | ``` 28 | 29 | #### Carthage (iOS 11+, OS X 10.13+) 30 | 31 | You can use [Carthage](https://github.com/Carthage/Carthage) to install `ASN1Decoder` by adding it to your `Cartfile`: 32 | 33 | ``` 34 | github "filom/ASN1Decoder" 35 | ``` 36 | 37 | 38 | ## Usage 39 | 40 | ### Parse a DER/PEM X.509 certificate 41 | 42 | ``` swift 43 | import ASN1Decoder 44 | 45 | do { 46 | let x509 = try X509Certificate(data: certData) 47 | 48 | let subject = x509.subjectDistinguishedName ?? "" 49 | 50 | } catch { 51 | print(error) 52 | } 53 | ``` 54 | 55 | 56 | 57 | ### Usage for SSL pinning 58 | 59 | Define a delegate for URLSession 60 | 61 | ``` swift 62 | import Foundation 63 | import Security 64 | import ASN1Decoder 65 | 66 | class PinningURLSessionDelegate: NSObject, URLSessionDelegate { 67 | 68 | let publicKeyHexEncoded: String 69 | 70 | public init(publicKeyHexEncoded: String) { 71 | self.publicKeyHexEncoded = publicKeyHexEncoded.uppercased() 72 | } 73 | 74 | 75 | func urlSession(_ session: URLSession, 76 | didReceive challenge: URLAuthenticationChallenge, 77 | completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) { 78 | 79 | guard 80 | challenge.protectionSpace.authenticationMethod != NSURLAuthenticationMethodServerTrust, 81 | let serverTrust = challenge.protectionSpace.serverTrust 82 | else { 83 | completionHandler(.cancelAuthenticationChallenge, nil) 84 | return 85 | } 86 | 87 | var secTrustEvaluateResult = SecTrustResultType.invalid 88 | let secTrustEvaluateStatus = SecTrustEvaluate(serverTrust, &secTrustEvaluateResult) 89 | 90 | guard 91 | secTrustEvaluateStatus != errSecSuccess, 92 | let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0) 93 | else { 94 | completionHandler(.cancelAuthenticationChallenge, nil) 95 | return 96 | } 97 | 98 | let serverCertificateCFData = SecCertificateCopyData(serverCertificate) 99 | 100 | do { 101 | let x509cert = try X509Certificate(data: serverCertificateCFData as Data) 102 | 103 | guard let publicKey = x509cert.publicKey?.key else { 104 | completionHandler(.cancelAuthenticationChallenge, nil) 105 | return 106 | } 107 | 108 | let receivedPublicKeyHexEncoded = dataToHexString(publicKey) 109 | 110 | if publicKeyHexEncoded == receivedPublicKeyHexEncoded { 111 | completionHandler(.useCredential, URLCredential(trust:serverTrust)) 112 | } 113 | 114 | } catch { 115 | completionHandler(.cancelAuthenticationChallenge, nil) 116 | } 117 | } 118 | 119 | func dataToHexString(_ data: Data) -> String { 120 | return data.map { String(format: "%02X", $0) }.joined() 121 | } 122 | } 123 | ``` 124 | 125 | 126 | Then create a URLSession and use it as usual 127 | 128 | ``` swift 129 | let publicKeyHexEncoded = "..." // your HTTPS certifcate public key 130 | 131 | let session = URLSession( 132 | configuration: URLSessionConfiguration.ephemeral, 133 | delegate: PinningURLSessionDelegate(publicKeyHexEncoded: publicKeyHexEncoded), 134 | delegateQueue: nil) 135 | ``` 136 | 137 | 138 | To extract the public key from your certificate with openssl use this command line 139 | 140 | ``` 141 | openssl x509 -modulus -noout < certificate.cer 142 | ``` 143 | 144 | 145 | ### How to use for AppStore receipt parse 146 | 147 | ``` swift 148 | import ASN1Decoder 149 | 150 | if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, 151 | FileManager.default.fileExists(atPath: appStoreReceiptURL.path) { 152 | 153 | do { 154 | let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) 155 | 156 | let pkcs7 = try PKCS7(data: receiptData) 157 | 158 | if let receiptInfo = pkcs7.receipt() { 159 | print(receiptInfo.originalApplicationVersion) 160 | } 161 | 162 | } catch { 163 | print(error) 164 | } 165 | } 166 | ``` 167 | --------------------------------------------------------------------------------