├── .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 = "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"
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 |
--------------------------------------------------------------------------------