From 5eefa4257b1ef744d9eeea974ec041093219c451 Mon Sep 17 00:00:00 2001 From: Russell Hancox Date: Thu, 20 Aug 2015 15:08:28 -0400 Subject: [PATCH] Initial commit --- LICENSE | 202 ++++++++++++ MOLCertificate.podspec | 12 + MOLCertificate.xcodeproj/project.pbxproj | 395 +++++++++++++++++++++++ README.md | 2 + Source/MOLCertificate.h | 113 +++++++ Source/MOLCertificate.m | 361 +++++++++++++++++++++ Tests/GIAG2.crt | Bin 0 -> 1032 bytes Tests/GIAG2.pem | 24 ++ Tests/Info.plist | 24 ++ Tests/MOLCertificateTest.m | 211 ++++++++++++ Tests/apple.pem | 34 ++ Tests/tubitak.crt | Bin 0 -> 1307 bytes 12 files changed, 1378 insertions(+) create mode 100644 LICENSE create mode 100644 MOLCertificate.podspec create mode 100644 MOLCertificate.xcodeproj/project.pbxproj create mode 100644 README.md create mode 100644 Source/MOLCertificate.h create mode 100644 Source/MOLCertificate.m create mode 100644 Tests/GIAG2.crt create mode 100644 Tests/GIAG2.pem create mode 100644 Tests/Info.plist create mode 100644 Tests/MOLCertificateTest.m create mode 100644 Tests/apple.pem create mode 100644 Tests/tubitak.crt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MOLCertificate.podspec b/MOLCertificate.podspec new file mode 100644 index 0000000..06e3051 --- /dev/null +++ b/MOLCertificate.podspec @@ -0,0 +1,12 @@ +Pod::Spec.new do |s| + s.name = 'MOLCertificate' + s.version = '1.0' + s.platform = :osx + s.license = 'Apache' + s.homepage = 'https://github.com/google/macops-molcertificate' + s.author = { 'Google Macops' => 'macops-external@google.com' } + s.summary = 'Objective-C wrapper around SecCertificateRef' + s.source = { :git => 'https://github.com/google/macops-molcertificate.git', :tag => 'v1.0' } + s.source_files = 'Source/MOLCertificate.{h,m}' + s.framework = 'Security' +end diff --git a/MOLCertificate.xcodeproj/project.pbxproj b/MOLCertificate.xcodeproj/project.pbxproj new file mode 100644 index 0000000..70ce072 --- /dev/null +++ b/MOLCertificate.xcodeproj/project.pbxproj @@ -0,0 +1,395 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 0D53CACD1B71548E00073187 /* libMOLCertificate.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D53CAC11B71548E00073187 /* libMOLCertificate.dylib */; }; + 0D53CADD1B71549A00073187 /* MOLCertificate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D53CADB1B71549A00073187 /* MOLCertificate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 0D53CADE1B71549A00073187 /* MOLCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D53CADC1B71549A00073187 /* MOLCertificate.m */; }; + 0D53CAE01B7154A000073187 /* MOLCertificateTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D53CADF1B7154A000073187 /* MOLCertificateTest.m */; }; + 0D53CAE51B7154B700073187 /* apple.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D53CAE11B7154B700073187 /* apple.pem */; }; + 0D53CAE61B7154B700073187 /* GIAG2.crt in Resources */ = {isa = PBXBuildFile; fileRef = 0D53CAE21B7154B700073187 /* GIAG2.crt */; }; + 0D53CAE71B7154B700073187 /* GIAG2.pem in Resources */ = {isa = PBXBuildFile; fileRef = 0D53CAE31B7154B700073187 /* GIAG2.pem */; }; + 0D53CAE81B7154B700073187 /* tubitak.crt in Resources */ = {isa = PBXBuildFile; fileRef = 0D53CAE41B7154B700073187 /* tubitak.crt */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0D53CACE1B71548E00073187 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0D53CAB91B71548E00073187 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0D53CAC01B71548E00073187; + remoteInfo = MOLCertificate; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0D53CAC11B71548E00073187 /* libMOLCertificate.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libMOLCertificate.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D53CACC1B71548E00073187 /* MOLCertificateTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MOLCertificateTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 0D53CAD21B71548E00073187 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 0D53CADB1B71549A00073187 /* MOLCertificate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MOLCertificate.h; sourceTree = ""; }; + 0D53CADC1B71549A00073187 /* MOLCertificate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOLCertificate.m; sourceTree = ""; }; + 0D53CADF1B7154A000073187 /* MOLCertificateTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MOLCertificateTest.m; sourceTree = ""; }; + 0D53CAE11B7154B700073187 /* apple.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = apple.pem; sourceTree = ""; }; + 0D53CAE21B7154B700073187 /* GIAG2.crt */ = {isa = PBXFileReference; lastKnownFileType = file; path = GIAG2.crt; sourceTree = ""; }; + 0D53CAE31B7154B700073187 /* GIAG2.pem */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GIAG2.pem; sourceTree = ""; }; + 0D53CAE41B7154B700073187 /* tubitak.crt */ = {isa = PBXFileReference; lastKnownFileType = file; path = tubitak.crt; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0D53CABE1B71548E00073187 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0D53CAC91B71548E00073187 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D53CACD1B71548E00073187 /* libMOLCertificate.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 0D53CAB81B71548E00073187 = { + isa = PBXGroup; + children = ( + 0D53CAC31B71548E00073187 /* Source */, + 0D53CAD01B71548E00073187 /* Tests */, + 0D53CAC21B71548E00073187 /* Products */, + ); + sourceTree = ""; + }; + 0D53CAC21B71548E00073187 /* Products */ = { + isa = PBXGroup; + children = ( + 0D53CAC11B71548E00073187 /* libMOLCertificate.dylib */, + 0D53CACC1B71548E00073187 /* MOLCertificateTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 0D53CAC31B71548E00073187 /* Source */ = { + isa = PBXGroup; + children = ( + 0D53CADB1B71549A00073187 /* MOLCertificate.h */, + 0D53CADC1B71549A00073187 /* MOLCertificate.m */, + ); + path = Source; + sourceTree = ""; + }; + 0D53CAD01B71548E00073187 /* Tests */ = { + isa = PBXGroup; + children = ( + 0D53CAE11B7154B700073187 /* apple.pem */, + 0D53CAE21B7154B700073187 /* GIAG2.crt */, + 0D53CAE31B7154B700073187 /* GIAG2.pem */, + 0D53CAE41B7154B700073187 /* tubitak.crt */, + 0D53CADF1B7154A000073187 /* MOLCertificateTest.m */, + 0D53CAD11B71548E00073187 /* Supporting Files */, + ); + path = Tests; + sourceTree = ""; + }; + 0D53CAD11B71548E00073187 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 0D53CAD21B71548E00073187 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 0D53CABF1B71548E00073187 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D53CADD1B71549A00073187 /* MOLCertificate.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0D53CAC01B71548E00073187 /* MOLCertificate */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0D53CAD51B71548E00073187 /* Build configuration list for PBXNativeTarget "MOLCertificate" */; + buildPhases = ( + 0D53CABD1B71548E00073187 /* Sources */, + 0D53CABE1B71548E00073187 /* Frameworks */, + 0D53CABF1B71548E00073187 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MOLCertificate; + productName = MOLCertificate; + productReference = 0D53CAC11B71548E00073187 /* libMOLCertificate.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + 0D53CACB1B71548E00073187 /* MOLCertificateTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0D53CAD81B71548E00073187 /* Build configuration list for PBXNativeTarget "MOLCertificateTests" */; + buildPhases = ( + 0D53CAC81B71548E00073187 /* Sources */, + 0D53CAC91B71548E00073187 /* Frameworks */, + 0D53CACA1B71548E00073187 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 0D53CACF1B71548E00073187 /* PBXTargetDependency */, + ); + name = MOLCertificateTests; + productName = MOLCertificateTests; + productReference = 0D53CACC1B71548E00073187 /* MOLCertificateTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0D53CAB91B71548E00073187 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = "Google Inc"; + TargetAttributes = { + 0D53CAC01B71548E00073187 = { + CreatedOnToolsVersion = 6.1.1; + }; + 0D53CACB1B71548E00073187 = { + CreatedOnToolsVersion = 6.1.1; + }; + }; + }; + buildConfigurationList = 0D53CABC1B71548E00073187 /* Build configuration list for PBXProject "MOLCertificate" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 0D53CAB81B71548E00073187; + productRefGroup = 0D53CAC21B71548E00073187 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0D53CAC01B71548E00073187 /* MOLCertificate */, + 0D53CACB1B71548E00073187 /* MOLCertificateTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0D53CACA1B71548E00073187 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D53CAE71B7154B700073187 /* GIAG2.pem in Resources */, + 0D53CAE61B7154B700073187 /* GIAG2.crt in Resources */, + 0D53CAE81B7154B700073187 /* tubitak.crt in Resources */, + 0D53CAE51B7154B700073187 /* apple.pem in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 0D53CABD1B71548E00073187 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D53CADE1B71549A00073187 /* MOLCertificate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0D53CAC81B71548E00073187 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0D53CAE01B7154A000073187 /* MOLCertificateTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0D53CACF1B71548E00073187 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0D53CAC01B71548E00073187 /* MOLCertificate */; + targetProxy = 0D53CACE1B71548E00073187 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 0D53CAD31B71548E00073187 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 0D53CAD41B71548E00073187 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 0D53CAD61B71548E00073187 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 0D53CAD71B71548E00073187 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 0D53CAD91B71548E00073187 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 0D53CADA1B71548E00073187 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0D53CABC1B71548E00073187 /* Build configuration list for PBXProject "MOLCertificate" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0D53CAD31B71548E00073187 /* Debug */, + 0D53CAD41B71548E00073187 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0D53CAD51B71548E00073187 /* Build configuration list for PBXNativeTarget "MOLCertificate" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0D53CAD61B71548E00073187 /* Debug */, + 0D53CAD71B71548E00073187 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 0D53CAD81B71548E00073187 /* Build configuration list for PBXNativeTarget "MOLCertificateTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0D53CAD91B71548E00073187 /* Debug */, + 0D53CADA1B71548E00073187 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0D53CAB91B71548E00073187 /* Project object */; +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1305cf --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# MOLCertificate +This is an Objective-C wrapper around SecCertificateRef with caching accessors. diff --git a/Source/MOLCertificate.h b/Source/MOLCertificate.h new file mode 100644 index 0000000..95623d7 --- /dev/null +++ b/Source/MOLCertificate.h @@ -0,0 +1,113 @@ +/// Copyright 2015 Google Inc. All rights reserved. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#import + +/// +/// MOLCertificate wraps a @c SecCertificateRef to provide Objective-C accessors to +/// commonly used certificate data. Accessors cache data for repeated access. +/// +@interface MOLCertificate : NSObject + +/// +/// Initialize a MOLCertificate object with a valid SecCertificateRef. Designated initializer. +/// +/// @param certRef valid SecCertificateRef, which will be retained. +/// +- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef; + +/// +/// Initialize a MOLCertificate object with certificate data in DER format. +/// +/// @param certData DER-encoded certificate data. +/// @return initialized MOLCertificate or nil if certData is not a DER-encoded certificate. +/// +- (instancetype)initWithCertificateDataDER:(NSData *)certData; + +/// +/// Initialize a MOLCertificate object with certificate data in PEM format. +/// If multiple PEM certificates exist within the string, the first is used. +/// +/// @param certData PEM-encoded certificate data. +/// @return initialized GMOCertifcate or nil if certData is not a PEM-encoded certificate. +/// +- (instancetype)initWithCertificateDataPEM:(NSString *)certData; + +/// +/// Returns an array of MOLCertificate's for all of the certificates in @c pemData. +/// +/// @param pemData PEM-encoded certificates. +/// @return array of MOLCertificate objects. +/// ++ (NSArray *)certificatesFromPEM:(NSString *)pemData; + +/// +/// Access the underlying certificate ref. +/// +@property(readonly, nonatomic) SecCertificateRef certRef; + +/// +/// SHA-1 hash of the certificate data. +/// +@property(readonly, nonatomic) NSString *SHA1; + +/// +/// SHA-256 hash of the certificate data. +/// +@property(readonly, nonatomic) NSString *SHA256; + +/// +/// Certificate data. +/// +@property(readonly, nonatomic) NSData *certData; + +/// +/// Common Name e.g: "Software Signing" +/// +@property(readonly, nonatomic) NSString *commonName; + +/// +/// Country Name e.g: "US" +/// +@property(readonly, nonatomic) NSString *countryName; + +/// +/// Organizational Name e.g: "Apple Inc." +/// +@property(readonly, nonatomic) NSString *orgName; + +/// +/// Organizational Unit Name e.g: "Apple Software" +/// +@property(readonly, nonatomic) NSString *orgUnit; + +/// +/// Issuer details, same fields as above. +/// +@property(readonly, nonatomic) NSString *issuerCommonName; +@property(readonly, nonatomic) NSString *issuerCountryName; +@property(readonly, nonatomic) NSString *issuerOrgName; +@property(readonly, nonatomic) NSString *issuerOrgUnit; + +/// +/// Validity Not Before +/// +@property(readonly, nonatomic) NSDate *validFrom; + +/// +/// Validity Not After +/// +@property(readonly, nonatomic) NSDate *validUntil; + +@end diff --git a/Source/MOLCertificate.m b/Source/MOLCertificate.m new file mode 100644 index 0000000..ca8b1b3 --- /dev/null +++ b/Source/MOLCertificate.m @@ -0,0 +1,361 @@ +/// Copyright 2015 Google Inc. All rights reserved. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#import "MOLCertificate.h" + +#import +#import + +@interface MOLCertificate () +/// A container for cached property values +@property NSMutableDictionary *memoizedData; +@end + +@implementation MOLCertificate + +static NSString *const kCertDataKey = @"certData"; + +#pragma mark Init/Dealloc + +- (instancetype)initWithSecCertificateRef:(SecCertificateRef)certRef { + self = [super init]; + if (self) { + _certRef = certRef; + CFRetain(_certRef); + } + return self; +} + +- (instancetype)initWithCertificateDataDER:(NSData *)certData { + SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData); + + if (cert) { + // Despite the header file claiming that SecCertificateCreateWithData will return NULL if + // @c certData doesn't contain a valid DER-encoded X509 cert, this isn't always true. + // radar://problem/16124651 + // To workaround, check that the certificate serial number can be retrieved. According to + // RFC5280, the serial number field is required. + NSData *ser = CFBridgingRelease(SecCertificateCopySerialNumber(cert, NULL)); + if (ser) { + self = [self initWithSecCertificateRef:cert]; + } else { + self = nil; + } + CFRelease(cert); // was retained in initWithSecCertificateRef + } else { + self = nil; + } + + return self; +} + +- (instancetype)initWithCertificateDataPEM:(NSString *)certData { + // Find the PEM and extract the base64-encoded DER data from within + NSScanner *scanner = [NSScanner scannerWithString:certData]; + NSString *base64der; + + // Locate and parse DER data into |base64der| + [scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL]; + if (!([scanner scanString:@"-----BEGIN CERTIFICATE-----" intoString:NULL] && + [scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&base64der] && + [scanner scanString:@"-----END CERTIFICATE-----" intoString:NULL])) { + return nil; + } + + // base64-decode the DER + SecTransformRef transform = SecDecodeTransformCreate(kSecBase64Encoding, NULL); + if (!transform) return nil; + NSData *input = [base64der dataUsingEncoding:NSUTF8StringEncoding]; + NSData *output = nil; + + if (SecTransformSetAttribute(transform, + kSecTransformInputAttributeName, + (__bridge CFDataRef)input, + NULL)) { + output = CFBridgingRelease(SecTransformExecute(transform, NULL)); + } + if (transform) CFRelease(transform); + + return [self initWithCertificateDataDER:output]; +} + ++ (NSArray *)certificatesFromPEM:(NSString *)pemData { + NSScanner *scanner = [NSScanner scannerWithString:pemData]; + NSMutableArray *certs = [[NSMutableArray alloc] init]; + + while (YES) { + NSString *curCert; + + [scanner scanUpToString:@"-----BEGIN CERTIFICATE-----" intoString:NULL]; + [scanner scanUpToString:@"-----END CERTIFICATE-----" intoString:&curCert]; + + // If there was no data, break. + if (!curCert) break; + + curCert = [curCert stringByAppendingString:@"-----END CERTIFICATE-----"]; + MOLCertificate *cert = [[MOLCertificate alloc] initWithCertificateDataPEM:curCert]; + + // If the data couldn't be turned into a valid MOLCertificate, continue. + if (!cert) continue; + + [certs addObject:cert]; + } + + return certs; +} + +- (instancetype)init { + [self doesNotRecognizeSelector:_cmd]; + return nil; +} + +- (void)dealloc { + if (_certRef) CFRelease(_certRef); +} + +#pragma mark Equality & description + +- (BOOL)isEqual:(id)other { + if (self == other) return YES; + if (![other isKindOfClass:[MOLCertificate class]]) return NO; + + MOLCertificate *o = other; + return [self.certData isEqual:o.certData]; +} + +- (NSUInteger)hash { + return [self.certData hash]; +} + +- (NSString *)description { + return + [NSString stringWithFormat:@"/O=%@/OU=%@/CN=%@", self.orgName, self.orgUnit, self.commonName]; +} + +#pragma mark NSSecureCoding + ++ (BOOL)supportsSecureCoding { + return YES; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [coder encodeObject:self.certData forKey:kCertDataKey]; +} + +- (instancetype)initWithCoder:(NSCoder *)decoder { + NSData *certData = [decoder decodeObjectOfClass:[NSData class] forKey:kCertDataKey]; + if ([certData length] == 0) return nil; + SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData); + self = [self initWithSecCertificateRef:cert]; + if (cert) CFRelease(cert); + return self; +} + +#pragma mark Private Accessors + +/// +/// For a given selector, caches the value that selector would return on subsequent invocations, +/// using the provided block to get the value on the first invocation. +/// Assumes the selector's value will never change. +/// +- (id)memoizedSelector:(SEL)selector forBlock:(id (^)(void))block { + NSString *selName = NSStringFromSelector(selector); + + if (!self.memoizedData) { + self.memoizedData = [NSMutableDictionary dictionary]; + } + + if (!self.memoizedData[selName]) { + id val = block(); + if (val) { + self.memoizedData[selName] = val; + } else { + self.memoizedData[selName] = [NSNull null]; + } + } + + // Return the value if there is one, or nil if the value is NSNull + return self.memoizedData[selName] != [NSNull null] ? self.memoizedData[selName] : nil; +} + +- (NSDictionary *)allCertificateValues { + return [self memoizedSelector:_cmd forBlock:^id{ + return CFBridgingRelease(SecCertificateCopyValues(self.certRef, NULL, NULL)); + }]; +} + +- (NSDictionary *)x509SubjectName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1SubjectName]; + }]; +} + +- (NSDictionary *)x509IssuerName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self allCertificateValues][(__bridge NSString *)kSecOIDX509V1IssuerName]; + }]; +} + +/// +/// Retrieve the value with the specified label from the X509 dictionary provided +/// +/// @param desiredLabel The label you want, e.g: kSecOIDOrganizationName. +/// @param dict The dictionary to look in (Subject or Issuer) +/// @return An @c NSString, the value for the specified label. +/// +- (NSString *)x509ValueForLabel:(NSString *)desiredLabel fromDictionary:(NSDictionary *)dict { + @try { + NSArray *valArray = dict[(__bridge NSString *)kSecPropertyKeyValue]; + + for (NSDictionary *curCertVal in valArray) { + NSString *valueLabel = curCertVal[(__bridge NSString *)kSecPropertyKeyLabel]; + if ([valueLabel isEqual:desiredLabel]) { + return curCertVal[(__bridge NSString *)kSecPropertyKeyValue]; + } + } + return nil; + } + @catch (NSException *e) { + return nil; + } +} + +/// +/// Retrieve the specified date from the certificate's values and convert from a reference date +/// to an NSDate object. +/// +/// @param key The identifier for the date: @c kSecOIDX509V1ValiditityNot{Before,After} +/// @return An @c NSDate representing the date and time the certificate is valid from or expires. +/// +- (NSDate *)dateForX509Key:(NSString *)key { + NSDictionary *curCertVal = [self allCertificateValues][key]; + NSNumber *value = curCertVal[(__bridge NSString *)kSecPropertyKeyValue]; + + NSTimeInterval interval = [value doubleValue]; + if (interval) { + return [NSDate dateWithTimeIntervalSinceReferenceDate:interval]; + } + + return nil; +} + +#pragma mark Public Accessors + +- (NSString *)SHA1 { + return [self memoizedSelector:_cmd forBlock:^id{ + NSMutableData *SHA1Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA1_DIGEST_LENGTH]; + + CC_SHA1([self.certData bytes], (CC_LONG)[self.certData length], [SHA1Buffer mutableBytes]); + + const unsigned char *bytes = (const unsigned char *)[SHA1Buffer bytes]; + NSMutableString *hexDigest = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) { + [hexDigest appendFormat:@"%02x", bytes[i]]; + } + + return hexDigest; + }]; +} + +- (NSString *)SHA256 { + return [self memoizedSelector:_cmd forBlock:^id{ + NSMutableData *SHA256Buffer = [[NSMutableData alloc] initWithCapacity:CC_SHA256_DIGEST_LENGTH]; + + CC_SHA256([self.certData bytes], (CC_LONG)[self.certData length], [SHA256Buffer mutableBytes]); + + const unsigned char *bytes = (const unsigned char *)[SHA256Buffer bytes]; + NSMutableString *hexDigest = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2]; + for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) { + [hexDigest appendFormat:@"%02x", bytes[i]]; + } + + return hexDigest; + }]; +} + +- (NSData *)certData { + return CFBridgingRelease(SecCertificateCopyData(self.certRef)); +} + +- (NSString *)commonName { + return [self memoizedSelector:_cmd forBlock:^id{ + CFStringRef commonName = NULL; + SecCertificateCopyCommonName(self.certRef, &commonName); + return CFBridgingRelease(commonName); + }]; +} + +- (NSString *)countryName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName + fromDictionary:[self x509SubjectName]]; + }]; +} + +- (NSString *)orgName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName + fromDictionary:[self x509SubjectName]]; + }]; +} + +- (NSString *)orgUnit { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName + fromDictionary:[self x509SubjectName]]; + }]; +} + +- (NSDate *)validFrom { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotBefore]; + }]; +} + +- (NSDate *)validUntil { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self dateForX509Key:(__bridge NSString *)kSecOIDX509V1ValidityNotAfter]; + }]; +} + +- (NSString *)issuerCommonName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCommonName + fromDictionary:[self x509IssuerName]]; + }]; +} + +- (NSString *)issuerCountryName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDCountryName + fromDictionary:[self x509IssuerName]]; + }]; +} + +- (NSString *)issuerOrgName { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationName + fromDictionary:[self x509IssuerName]]; + }]; +} + +- (NSString *)issuerOrgUnit { + return [self memoizedSelector:_cmd forBlock:^id{ + return [self x509ValueForLabel:(__bridge NSString *)kSecOIDOrganizationalUnitName + fromDictionary:[self x509IssuerName]]; + }]; +} + + +@end diff --git a/Tests/GIAG2.crt b/Tests/GIAG2.crt new file mode 100644 index 0000000000000000000000000000000000000000..d24272bd2774b4ed01360e50483384bb6b32b438 GIT binary patch literal 1032 zcmXqLVqr08VtTWHnTe5!iJ8eN(}0(aQ>)FR?K>|cBP%O|fs-M(0Vf-CC<~h~Q)sZE zn1Kk0!zIk?o|+$0R9al3;F*`KXDDqT2@+=(7J`er=j10P<|sHj8pw(B8X6mz7?>IY zp{Z#UkZTI$nn1Y*o~U*R8wf(|;BwE;PtQpO*U5Tt=JYfJ-89zv8U7iqhx_p zhg;o){-Yl|&ndo(`j^PN-N>bLK?!%Tf9R5PO-WPVy^f4O_K>wf=t$fY`#sSf7GHwD zt33NX@uldI^Iw8_zj*zbA9AmD--7J?_5&9Jb}PfUINFo-t3yLAb8wzrU1)=4~bP%ReSt`N*`@|%W+EN&pD;6&3@tUiErc0VXLn11k_;p2gU}&_I8I?gH&L z&5V+g0xNy}=rd!R{@}w@YxBR-P7(i5QNYs+s~xGT(PYTKPVS*KWfqU*oyKa=0e9C0`!Q26ZM6W=Rs zXPSNpTfFx{pU{77aW=PP!uC!Jtj-VDy1i)Cv^zZ| zho`B#I-Lp&OXa(b?V`A)`~x;ZcVwM!(m$|R(WDU KfPzYhaUlS5KYOtN literal 0 HcmV?d00001 diff --git a/Tests/GIAG2.pem b/Tests/GIAG2.pem new file mode 100644 index 0000000..6de7922 --- /dev/null +++ b/Tests/GIAG2.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG +EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy +bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP +VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv +h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE +ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ +EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC +DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7 +qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD +VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g +K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI +KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n +ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB +BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY +/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/ +zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza +HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto +WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6 +yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx +-----END CERTIFICATE----- diff --git a/Tests/Info.plist b/Tests/Info.plist new file mode 100644 index 0000000..da333b5 --- /dev/null +++ b/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Tests/MOLCertificateTest.m b/Tests/MOLCertificateTest.m new file mode 100644 index 0000000..6e36c6a --- /dev/null +++ b/Tests/MOLCertificateTest.m @@ -0,0 +1,211 @@ +/// Copyright 2015 Google Inc. All rights reserved. +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + +#import + +#import "MOLCertificate.h" + +@interface MOLCertificateTest : XCTestCase +@property NSString *testDataPEM1; +@property NSString *testDataPEM2; +@property NSData *testDataDER1; +@property NSData *testDataDER2; +@property NSString *testDataPrivateKey; +@end + +@implementation MOLCertificateTest + +- (void)setUp { + [super setUp]; + + NSString *file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GIAG2" ofType:@"pem"]; + self.testDataPEM1 = [NSString stringWithContentsOfFile:file + encoding:NSUTF8StringEncoding + error:nil]; + + file = [[NSBundle bundleForClass:[self class]] pathForResource:@"apple" ofType:@"pem"]; + self.testDataPEM2 = [NSString stringWithContentsOfFile:file + encoding:NSUTF8StringEncoding + error:nil]; + + file = [[NSBundle bundleForClass:[self class]] pathForResource:@"GIAG2" ofType:@"crt"]; + self.testDataDER1 = [NSData dataWithContentsOfFile:file]; + + file = [[NSBundle bundleForClass:[self class]] pathForResource:@"tubitak" ofType:@"crt"]; + self.testDataDER2 = [NSData dataWithContentsOfFile:file]; + + self.testDataPrivateKey = @"-----BEGIN RSA PRIVATE KEY-----" + @"MIICXQIBAAKBgQDk2F9JsQQjKSveMwazXzFLbiiOD0RkDiRX1LTmQtVdi514F6l/" + @"RwohMrwxQpsoKwyzEngX58+PrGZ0XZrcVcHn666521IxswHZPaacBlWZ7k9XkB2Y" + @"m8mxULMBG9iIv/k5tRJN3MuJdtbQc8qLBsyFFsytL8hSRvBQNyP7N/OqnQIDAQAB" + @"AoGATpLUNNMonoH2Y/aVKGVY4ZNTLWOkkc4hQF7yNdVguRvE14UYV3Em0zs+TpOV" + @"/na5h4qh3WNkaupAy1eQYnK3fqmGLZw5e8cBCgUkIi8P//zMrKlgJKwfzQHSdJSP" + @"pkCvj2kliFwNzbA026jcwGEYV+uRCNazO5ldtOcP5EDb+qkCQQD/Ihc2mjtf7oq1" + @"VZSzo0xch3NtzTZMyFCRWqMpXHQO1fZTAe96EbI85zsTRmOVuqKnGxBvvtHJr2QY" + @"UoZ72+f7AkEA5Z9qte46t1F1ME3ZzWd6Ob1obCmuAa75eTPAgQKc+1bSVeFMGLTz" + @"n2M9wZ+mIpWvJp8QRdmOi0zpEArHqa68RwJBAO1YoY/CW4obOB8JxpR3TgqmV9PG" + @"HMXBdHJEh5Vq1O0YT1dZbZd57v6JfoOn7+zS+43Jt7i9JB0kdVHLNCD1qxECQQC3" + @"wXGGEhVO6pMbitGHvQ1k85yDIn+rvTjLs4yUMWErCfnc3CUniHeFz8d2EarD9oFq" + @"KNS+8TFPbMb+HYJW2gy1AkAHGBUKmZNPGiKJEUjc5jN1uN+B9OMLDX+3rMUO9Q2x" + @"jsn0m7Mobx+pPqbIAvsklMtA4Qdrt5a9pnwEgTWoJPYA" + @"-----END RSA PRIVATE KEY-----"; +} + +- (void)tearDown { + + [super tearDown]; +} + +- (void)testInitWithDER { + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataDER:self.testDataDER1]; + + XCTAssertNotNil(sut); + XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2"); + XCTAssertEqualObjects(sut.orgUnit, nil); + XCTAssertEqualObjects(sut.orgName, @"Google Inc"); + XCTAssertEqualObjects(sut.countryName, @"US"); + XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA"); + XCTAssertEqualObjects(sut.issuerOrgName, @"GeoTrust Inc."); + XCTAssertEqualObjects(sut.issuerOrgUnit, nil); + XCTAssertEqualObjects(sut.issuerCountryName, @"US"); + XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24"); + XCTAssertEqualObjects(sut.SHA256, + @"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d"); + XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithTimeIntervalSince1970:1365174955]); + XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithTimeIntervalSince1970:1428160555]); + + sut = [[MOLCertificate alloc] initWithCertificateDataDER:self.testDataDER2]; + XCTAssertNotNil(sut); + XCTAssertEqualObjects(sut.commonName, + @"TÜBİTAK UEKAE Kök Sertifika Hizmet Sağlayıcısı - Sürüm 3"); + XCTAssertEqualObjects(sut.orgUnit, + @"Ulusal Elektronik ve Kriptoloji Araştırma Enstitüsü - UEKAE"); + XCTAssertEqualObjects(sut.orgName, + @"Türkiye Bilimsel ve Teknolojik Araştırma Kurumu - TÜBİTAK"); + XCTAssertEqualObjects(sut.countryName, @"TR"); +} + +- (void)testInitWithValidPEM { + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1]; + XCTAssertNotNil(sut); + XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2"); + XCTAssertEqualObjects(sut.orgUnit, nil); + XCTAssertEqualObjects(sut.orgName, @"Google Inc"); + XCTAssertEqualObjects(sut.issuerCommonName, @"GeoTrust Global CA"); + XCTAssertEqualObjects(sut.SHA1, @"d83c1a7f4d0446bb2081b81a1670f8183451ca24"); + XCTAssertEqualObjects(sut.SHA256, + @"a047a37fa2d2e118a4f5095fe074d6cfe0e352425a7632bf8659c03919a6c81d"); + XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithTimeIntervalSince1970:1365174955]); + XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithTimeIntervalSince1970:1428160555]); + + sut = [[MOLCertificate alloc] initWithCertificateDataPEM:self.testDataPEM2]; + XCTAssertNotNil(sut); + XCTAssertEqualObjects(sut.commonName, @"www.apple.com"); + XCTAssertEqualObjects(sut.orgUnit, @"ISG for Akamai"); + XCTAssertEqualObjects(sut.orgName, @"Apple Inc."); + XCTAssertEqualObjects(sut.issuerCommonName, @"VeriSign Class 3 Extended Validation SSL SGC CA"); + XCTAssertEqualObjects(sut.issuerOrgName, @"VeriSign, Inc."); + XCTAssertEqualObjects(sut.issuerOrgUnit, @"VeriSign Trust Network"); + XCTAssertEqualObjects(sut.SHA1, @"96df534f6f4306ca474d9078fc346b20f856f0d4"); + XCTAssertEqualObjects(sut.SHA256, + @"129d39ff4384197dc2bcbe1a83a69b3405b7df33254b1b1ee29a23847a23555a"); + XCTAssertEqualObjects(sut.validFrom, [NSDate dateWithTimeIntervalSince1970:1384387200]); + XCTAssertEqualObjects(sut.validUntil, [NSDate dateWithTimeIntervalSince1970:1447545599]); +} + +- (void)testInitWithValidPEMAfterKey { + NSString *pemWithKey = [self.testDataPrivateKey stringByAppendingString:self.testDataPEM1]; + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:pemWithKey]; + + XCTAssertNotNil(sut); + XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2"); +} + +- (void)testInitWithEmptyPEM { + NSString *badPEM = @"-----BEGIN CERTIFICATE----------END CERTIFICATE-----"; + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:badPEM]; + XCTAssertNil(sut); +} + +- (void)testInitWithTruncatedPEM { + NSString *badPEM = @"-----BEGIN CERTIFICATE-----" + @"MIICXQIBAAKBgQDk2F9JsQQjKSveMwazXzFLbiiOD0RkDiRX1LTmQtVdi514F6l/"; + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:badPEM]; + XCTAssertNil(sut); +} + +- (void)testInitWithInvalidPEM { + NSString *badPEM = @"This is not a valid PEM"; + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:badPEM]; + XCTAssertNil(sut); + + badPEM = @"-----BEGIN CERTIFICATE-----Hello Thar-----END CERTIFICATE-----"; + sut = [[MOLCertificate alloc] initWithCertificateDataPEM:badPEM]; + XCTAssertNil(sut); +} + +- (void)testInitWithMultipleCertsInPEM { + NSString *multiPEM = [self.testDataPEM1 stringByAppendingString:self.testDataPEM2]; + + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:multiPEM]; + XCTAssertNotNil(sut); + XCTAssertEqualObjects(sut.commonName, @"Google Internet Authority G2"); +} + +- (void)testArrayOfCerts { + NSString *multiPEM = [self.testDataPEM1 stringByAppendingString:self.testDataPEM2]; + + NSArray *certs = [MOLCertificate certificatesFromPEM:multiPEM]; + + XCTAssertNotNil(certs); + XCTAssertEqual(certs.count, 2); + XCTAssertEqualObjects([certs[0] commonName], @"Google Internet Authority G2"); + XCTAssertEqualObjects([certs[1] commonName], @"www.apple.com"); +} + +- (void)testPlainInit { + XCTAssertThrows([[MOLCertificate alloc] init]); +} + +- (void)testEquals { + MOLCertificate *sut1 = [[MOLCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1]; + MOLCertificate *sut2 = [[MOLCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1]; + + XCTAssertEqualObjects(sut1, sut2); +} + +- (void)testDescription { + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1]; + + XCTAssertEqualObjects([sut description], @"/O=Google Inc/OU=(null)/CN=Google Internet Authority G2"); +} + +- (void)testSecureCoding { + XCTAssertTrue([MOLCertificate supportsSecureCoding]); + + MOLCertificate *sut = [[MOLCertificate alloc] initWithCertificateDataPEM:self.testDataPEM1]; + + NSMutableData *encodedObject = [[NSMutableData alloc] init]; + NSKeyedArchiver *archive = [[NSKeyedArchiver alloc] initForWritingWithMutableData:encodedObject]; + [archive encodeObject:sut forKey:@"exampleCert"]; + [archive finishEncoding]; + NSKeyedUnarchiver *unarchive = [[NSKeyedUnarchiver alloc] initForReadingWithData:encodedObject]; + MOLCertificate *newCert = [unarchive decodeObjectForKey:@"exampleCert"]; + + XCTAssertNotNil(newCert); + XCTAssertEqualObjects(newCert, sut); + XCTAssertEqualObjects(newCert.SHA1, sut.SHA1); +} + +@end diff --git a/Tests/apple.pem b/Tests/apple.pem new file mode 100644 index 0000000..87119fc --- /dev/null +++ b/Tests/apple.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF+DCCBOCgAwIBAgIQXvpnDpnkq4jg8gszhnt4TTANBgkqhkiG9w0BAQUFADCB +vjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug +YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMv +VmVyaVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0Ew +HhcNMTMxMTE0MDAwMDAwWhcNMTUxMTE0MjM1OTU5WjCCAQoxEzARBgsrBgEEAYI3 +PAIBAxMCVVMxGzAZBgsrBgEEAYI3PAIBAhMKQ2FsaWZvcm5pYTEdMBsGA1UEDxMU +UHJpdmF0ZSBPcmdhbml6YXRpb24xETAPBgNVBAUTCEMwODA2NTkyMQswCQYDVQQG +EwJVUzEOMAwGA1UEERQFOTUwMTQxEzARBgNVBAgTCkNhbGlmb3JuaWExEjAQBgNV +BAcUCUN1cGVydGlubzEYMBYGA1UECRQPMSBJbmZpbml0ZSBMb29wMRMwEQYDVQQK +FApBcHBsZSBJbmMuMRcwFQYDVQQLFA5JU0cgZm9yIEFrYW1haTEWMBQGA1UEAxQN +d3d3LmFwcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMRK +AzgN5IfMI0ioi1hYtFu5x6CGTH3/wpdlACJhfUtcAk7k1GaaMQKy/6f4scGI3tmj +K6qJjvEHf3cp6sliOPBrCLoby8/4r1T1+yBxanpe8arpChtj5KwD+owXQYwN7jqO +IB4nQGQin+D3aJiiqJfBc4XxhAEh+u29/RM+ux3l+QDMTzOm2hecl/z+wqtmRNeq +wCp1MHL+ejGaf60/SJUQM0QlQjqUcQBNbE73Lx+8UpZzsxjbEwOnstmSLkAjWQTE +FBVwPGqLF6ttR6ytACnUi2w6bFJsDv71nuIO56/SpzB/ZivddtZv4iYDg8ALBnFF +3AZI5jhbSid+vDzz5XECAwEAAaOCAaEwggGdMBgGA1UdEQQRMA+CDXd3dy5hcHBs +ZS5jb20wCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCBaAwKAYDVR0lBCEwHwYIKwYB +BQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwRAYDVR0gBD0wOzA5BgtghkgBhvhF +AQcXBjAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vY3Bz +MB0GA1UdDgQWBBT/EdiWLzrKfY9ayp23GIwElp5xiTAfBgNVHSMEGDAWgBROQ8gd +du83U3pP8lhvlPM44tW93zA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vRVZJbnRs +LWNybC52ZXJpc2lnbi5jb20vRVZJbnRsMjAwNi5jcmwwdgYIKwYBBQUHAQEEajBo +MCsGCCsGAQUFBzABhh9odHRwOi8vRVZJbnRsLW9jc3AudmVyaXNpZ24uY29tMDkG +CCsGAQUFBzAChi1odHRwOi8vRVZJbnRsLWFpYS52ZXJpc2lnbi5jb20vRVZJbnRs +MjAwNi5jZXIwDQYJKoZIhvcNAQEFBQADggEBAI4W8R0KFLvKzi20sd3cSaWHLqNb +K6AgFSShRtrrPIu2yNXzI+VjHZTuPhViqSGxfZJYI6c4e60zz5BkfuB0RbrJjQF9 +iFjA/+bfS6+dIU95D9+zwp6u/NhSZAoqUH8LU5+GLGdCy2f9wU1Gyp47jgY9sDdF +tgkrVk1AnKU7Okd+n6ZOeUCTXgJgVm6UWFt5FXr80VMdEWv2U1JYxwNzITR+tJ// +u5BBaPG2p/6E3d9lmer8Ffq97CZ8poAeXsQXtuwNdwMCrbUGZKalaS71G/sSMrAR +xVVZakUhwnXXswczh701AK+TzB3dQNhFWooX/sn4+MiIkg+WPaxkd8AsCIM= +-----END CERTIFICATE----- diff --git a/Tests/tubitak.crt b/Tests/tubitak.crt new file mode 100644 index 0000000000000000000000000000000000000000..49d447b348d8e6b747761715be73f327c9a08c04 GIT binary patch literal 1307 zcmXqLVih-NV*bB?nTe5!iBZsimyJ`a&7D}zB3qqZTp0Vf-CC<~h~Q%I1Z zgn<}{!_LF+o|;sZs-Uajou8bTnv-ehZr}=%;o`9iIlQMRJF_xX!6`E*Gq*T3N1-fL zAtW_BFFz+gD>GZcu_*EA{E{OZi*gecyi1Epb4!87gdCpZbYw$_qqm`lfg8vmZXWy4 zoYLaN90k{$)a;U?{5+smph4b6nFS?alTZwD%_}a+EIGWV`0yT}X`!y(j;@Bv28s}q zq`VV>mIS93m1L%6W+xU`=I1H+rWR$VR%IG`8F+vcG4nXWodD6U;C*;oHbRqvM`l%S zYKcN{;*t3|iIqn-CLh^Yd}JfgvS6Tp5AVrUFgB1A=QS`lurM+)G&D9hFpm=FH3V@D zq1@5nBrQ0b7?qF{BqJ*Wa}y&!C{=PXH8C9XFE_Ueqo zpU10mHl1d?ZM6T=>(=uNc|#9JTZ9+vC_PqrQN-Y_bVPLad(%$~75$E$tC<;}vvY^i zkP6iGJvcM!P z%f}+dBC@xm{bc{QC0yLAULXB8QF-oauP!44K9Dp&BjbM-7GOqaGvEjDg+Y8)17;wF z9Lm6K4-92S2HBclp8X~-x2sJOo5fo4-1|bVWzJ!>Z|8Ox?m2RQkJbUN5loYw}+MIXWFz`$jp0xOTFS^i+N#}jko3bI#;%9x;MIfD}=7CJ#_i> zr3;Rq)y%wRPtDa$ar7)IP58f`|EtBkRmZ_D`~ZENE*y-f~R- zs&}K%CNV`F;e`#G;&{A-=PBGsa#%g#Rf?DX8eT@BO4k+o?UETM94?F}s{gUvR(k+S;D7#lnZDBx+~h%sI%;rNrKv)2iaOa-V&7MN3ni3eViXjpmP4 XSG+azo2qki&V*ZURyx_N-Npm}F{kxH literal 0 HcmV?d00001