├── keys
├── certificate.der
├── operations.txt
├── private_key.key
└── certificate.pem
├── CryptoLoadExternalCertificate
├── certificate.der
├── CryptoLoadExternalCertificate.entitlements
├── Data+HexDescription.swift
├── ViewController.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── ImportViewController.swift
├── AppDelegate.swift
├── ExportViewController.swift
└── CryptoExportImportManager.swift
├── CryptoLoadExternalCertificate.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcuserdata
│ └── Nacho.xcuserdatad
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── CryptoLoadExternalCertificate.xcscheme
└── project.pbxproj
├── readkey.php
└── README.md
/keys/certificate.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DigitalLeaves/CryptoExportImportManager/HEAD/keys/certificate.der
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/certificate.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DigitalLeaves/CryptoExportImportManager/HEAD/CryptoLoadExternalCertificate/certificate.der
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/readkey.php:
--------------------------------------------------------------------------------
1 | \n";
4 | die();
5 | }
6 |
7 | if ($pub_key = openssl_pkey_get_public(file_get_contents($argv[1]))) {
8 | $keyData = openssl_pkey_get_details($pub_key);
9 | print "Key data:\n".var_export($keyData, true);
10 | } else {
11 | print "Error reading key from ".$argv[1]."\n";
12 | }
13 | ?>
14 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/CryptoLoadExternalCertificate.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | keychain-access-groups
6 |
7 | $(AppIdentifierPrefix)com.Ignacio-Nieto-Carvajal.CryptoLoadExternalCertificate
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/Data+HexDescription.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data+HexDescription.swift
3 | // CryptoLoadExternalCertificate
4 | //
5 | // Created by Ignacio Nieto Carvajal on 6/10/16.
6 | // Copyright © 2016 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Data {
12 | var hexDescription: String {
13 | return self.map { String(format: "%02hhx", $0) }.joined()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/keys/operations.txt:
--------------------------------------------------------------------------------
1 | 1. Generate the private key and the certificate
2 |
3 | openssl req -newkey rsa:2048 -nodes -keyout private_key.key -x509 -days 3650 -out certificate.pem
4 |
5 | 2. Convert the certificate from PEM to DER
6 |
7 | openssl x509 -outform der -in certificate.pem -out certificate.der
8 |
9 | 3. Check the certificate and private key
10 |
11 | openssl x509 -text -noout -inform DER -in certificate.der
12 | openssl rsa -check -in private_key.key
13 |
14 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate.xcodeproj/xcuserdata/Nacho.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CryptoLoadExternalCertificate.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 668B94D31BC3D19F00913B83
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // CryptoLoadExternalCertificate
4 | //
5 | // Created by Ignacio Nieto Carvajal on 6/10/15.
6 | // Copyright © 2015 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 | // Do any additional setup after loading the view, typically from a nib.
16 | }
17 |
18 | override func didReceiveMemoryWarning() {
19 | super.didReceiveMemoryWarning()
20 | // Dispose of any resources that can be recreated.
21 | }
22 |
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | }
33 | ],
34 | "info" : {
35 | "version" : 1,
36 | "author" : "xcode"
37 | }
38 | }
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/keys/private_key.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEowIBAAKCAQEA3iHnmQzhcOfhZUZ8ihJQ+UTxKlsyCsu4WnG34dayegN1lm2v
3 | DrL16fELeNyt3k46eBXsmUjhOTMZyioK6n7KgkA0eJXhogYbOj1EKSMRUaStMiGf
4 | iYGyD8tVsZ8MY5yb7G1j2kktYbexRLHvzUPGmSrt2ubEAcNsi6nscZkRpaav4xiO
5 | Gtasg7VX33rva1JLKuVDbEwzE5QRR72FguwlYs5uc51iOI2WjcG1vM4FKg9mHhei
6 | AbChBLU7HO2Yt51nzC+YB7Bw1xzqH9X2MnmI2b1xBKBEOAnfsGT7fZCiPIiBt+Ql
7 | 2IMVyQVP/uTUR3avvfY5ELxbz2yBKSbxN2cQMQIDAQABAoIBABibcOiHgMHdLh7l
8 | /A3fkZDLjlu+1oBTqQx75H7kJclWcMRFSks6j/tmdHrjF2+1As0iwJ8Kb8jCXG0E
9 | 00i+2vgq3KUvFVu5iXKeLtrPw69jqrwO9XlqIKuXPtr6X4j5ZFyAkbswuBJHIyAV
10 | BhebFnkJRGgngDchyvh9zm+XIVlUNPPm9TOumYitxcdNMwjI0RYYNS+UnyhQfTp2
11 | yDi+dVfYoRA42avAEAYkrNBqFKR9fpbiAgL6GU78lZH+SidJaW6bNG+W0aUaKlpe
12 | NMGFLO0+YewWGsh6vaYQnPXY7SNLi3UOGLSqsoY9+5sksXZ/l33A27nfN9d5agNt
13 | IS3Hk7ECgYEA9fOETRYP31h8ZTKf9G6U/U3Z0pki838bKVSJ2fGVlu+0tdq9HyoJ
14 | zBn7U8BfB/NB7u0a3HTb0V7VaNJxkFRMMUosAlJu/GB6iYabesI6zI7341NGxi0q
15 | M4m3Ll1wX+K9erreuPC0rgyDfR8ibaVPV5uq8AP6gFfjKOZB6PBvEgsCgYEA5zVC
16 | aGsu/f8ZaGlUevziwbOIAqy7jvTR2ioRlUyNxfDYPkeQySvKmrNcxoOYa2wK/pVA
17 | KRgugcB3JSK22BgnlCmOIGZLtTTpxCinQpsyPLNPzw0rjQsaIcF39KxCkcCXltXB
18 | YPXKfAOTq+fJ2uJvESPVUWqXM2KeBrTkBg8WaDMCgYAw4Yn8zBKxf+ORNUOHD+c6
19 | hJty+I5yuGNMjomrMBFb4x4WOnf11qQF1XAW0BfS+6yx8JuzDuKVNPTTFWgOmEJg
20 | VW5zs7BtJjuUU5VZC3smJo/rtSROxLErdwZaB6YK2IRx/OvA883k/YWwe1ybUAx5
21 | y+1oMFI76buy6DhXNFnOywKBgQCG/sDUaOiaIu052m4aFTdss59ROHuWX3l7sR5t
22 | FrKchSAtVOnDm88C/oDH46QF7KKKvKgmlljpJ3ApfZhWmdvaFV1Hq5K+rbNj9fya
23 | 1jTB3kxV7zGje3z5gpXAhSelJFceGZZ8P2bHe+sn7wEbzSPeVRbDH8JCnWz5K3sL
24 | 0plZowKBgBxzqRWQWfYxJk4WPahVbrUDee89GjT5WjcdP6EvgCG3I1HYIuGsjMRQ
25 | jWEQTo5sUdpHD3KMi/YtDYD+i07K7M+lg9mBLLXXni1d4ZVStqLWYiYzx93uJWYs
26 | yOXmkOv7sQF1BURBXJs1EHBIQdsHZ8SX85PE1lGcI9QZ806mJIgJ
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/keys/certificate.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIEszCCA5ugAwIBAgIJAIBQ5eAnyK2aMA0GCSqGSIb3DQEBBQUAMIGXMQswCQYD
3 | VQQGEwJFUzEPMA0GA1UECBMGTWFkcmlkMQ8wDQYDVQQHEwZNYWRyaWQxHjAcBgNV
4 | BAoTFU15IFNlY3JldCBDb21wYW55IExURDEdMBsGA1UEAxMUd3d3LnNlY3JldGNv
5 | bXBhbnkuZXMxJzAlBgkqhkiG9w0BCQEWGGNvbnRhY3RAc2VjcmV0Y29tcGFueS5l
6 | czAeFw0xNTEwMDYxMjAzNThaFw0yNTEwMDMxMjAzNThaMIGXMQswCQYDVQQGEwJF
7 | UzEPMA0GA1UECBMGTWFkcmlkMQ8wDQYDVQQHEwZNYWRyaWQxHjAcBgNVBAoTFU15
8 | IFNlY3JldCBDb21wYW55IExURDEdMBsGA1UEAxMUd3d3LnNlY3JldGNvbXBhbnku
9 | ZXMxJzAlBgkqhkiG9w0BCQEWGGNvbnRhY3RAc2VjcmV0Y29tcGFueS5lczCCASIw
10 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN4h55kM4XDn4WVGfIoSUPlE8Spb
11 | MgrLuFpxt+HWsnoDdZZtrw6y9enxC3jcrd5OOngV7JlI4TkzGcoqCup+yoJANHiV
12 | 4aIGGzo9RCkjEVGkrTIhn4mBsg/LVbGfDGOcm+xtY9pJLWG3sUSx781Dxpkq7drm
13 | xAHDbIup7HGZEaWmr+MYjhrWrIO1V99672tSSyrlQ2xMMxOUEUe9hYLsJWLObnOd
14 | YjiNlo3BtbzOBSoPZh4XogGwoQS1OxztmLedZ8wvmAewcNcc6h/V9jJ5iNm9cQSg
15 | RDgJ37Bk+32QojyIgbfkJdiDFckFT/7k1Ed2r732ORC8W89sgSkm8TdnEDECAwEA
16 | AaOB/zCB/DAdBgNVHQ4EFgQUzGaDtGeJ58tmt3wEAcUS+8fxYSgwgcwGA1UdIwSB
17 | xDCBwYAUzGaDtGeJ58tmt3wEAcUS+8fxYSihgZ2kgZowgZcxCzAJBgNVBAYTAkVT
18 | MQ8wDQYDVQQIEwZNYWRyaWQxDzANBgNVBAcTBk1hZHJpZDEeMBwGA1UEChMVTXkg
19 | U2VjcmV0IENvbXBhbnkgTFREMR0wGwYDVQQDExR3d3cuc2VjcmV0Y29tcGFueS5l
20 | czEnMCUGCSqGSIb3DQEJARYYY29udGFjdEBzZWNyZXRjb21wYW55LmVzggkAgFDl
21 | 4CfIrZowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA1TocFK0uFCzm
22 | vib9RbYMb2d6O8DL4CgItBk6W1622HntueV1m+WfTGlnBP2mVL6YbJyCUloIZIQt
23 | fFkbgBn7C51JwFO/wyICKE6gl9HJP7wA86AtJQ2kPXe6i3ImBbdlEHq3kv1eTfRL
24 | PYNMA+QQ4wa1FZ5p8B3BiZBqN9LetdW6moa3Ee1ctdRTRo6F+lB/5l9oNGBBlP1k
25 | zKlWRmqVFG9BREHdkPiv7PEGmYgDFxaIiBeIwVn8Tl6KooSdsGjdttRMxxueT5DB
26 | 1NRa+pljdXpJa34UsBv0iMyNOqYfxvgXH4b28o7YPCdkfvnvzZIA4Zm2Szr9NwJ8
27 | WC0yRL9D+g==
28 | -----END CERTIFICATE-----
29 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/ImportViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImportViewController.swift
3 | // CryptoLoadExternalCertificate
4 | //
5 | // Created by Ignacio Nieto Carvajal on 11/10/15.
6 | // Copyright © 2015 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ImportViewController: UIViewController {
12 | // outlets && buttons
13 | @IBOutlet weak var textView: UITextView!
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 |
18 | // Do any additional setup after loading the view.
19 | }
20 |
21 | override func didReceiveMemoryWarning() {
22 | super.didReceiveMemoryWarning()
23 | // Dispose of any resources that can be recreated.
24 | }
25 |
26 |
27 | @IBAction func importKey(_ sender: AnyObject) {
28 | // first try to get the path for certificate.der
29 | guard let certPath = Bundle.main.path(forResource: "certificate", ofType: "der") else {
30 | textView.text = "An error happened while reading the certificate file. Unable to get path for certificate.der"
31 | return
32 | }
33 |
34 | // now get the data from the certificate file
35 | guard let certData = try? Data(contentsOf: URL(fileURLWithPath: certPath)) else {
36 | textView.text = "An error happened while reading the certificate file. Unable to read certificate.der"
37 | return
38 | }
39 |
40 | // if we got the certificate data, let's extract the public key reference.
41 | let importExportManager = CryptoExportImportManager()
42 | if let publicKeyRef = importExportManager.importPublicKeyReferenceFromDERCertificate(certData) {
43 | textView.text = "Successfully extracted public key from certificate:\n\(publicKeyRef)\n"
44 | } else {
45 | textView.text = "Oups! I was unable to retrieve a public key from the certificate."
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // CryptoLoadExternalCertificate
4 | //
5 | // Created by Ignacio Nieto Carvajal on 6/10/15.
6 | // Copyright © 2015 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate.xcodeproj/xcuserdata/Nacho.xcuserdatad/xcschemes/CryptoLoadExternalCertificate.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CryptoExportImportManager
2 |
3 |
4 | The Security framework and cryptographic functions available for iOS developers are IMHO obscure, bad documented, and thought to be used only inside iOS. Unfortunately, there is no easy way to integrate iOS in a general, heterogeneous cryptographic system due to the way the Security framework stores and retrieves the key information.
5 |
6 | This class tries to make it easier for a developer to import public keys from an external source (i.e: a backend or server, generated by OpenSSL) or exporting the generated public keys to a format readable to external sources (i.e: openSSL or PHP (i.e: openssl_get_publickey).
7 |
8 | ## Usage
9 |
10 | Drag and drop the CryptoExportImportManager.swift file to your project, and copy if needed. Now you are ready to use it to import or export public keys:
11 |
12 | ### Import public RSA keys.
13 | Unfortunately, the Security framework provides no way for importing a public RSA key generated by, say, openSSL into iOS. This is due to the format that iOS expects the files to be. In order to import a public key, you need to generate a certificate, not a keypair with OpenSSL, and then pass this certificate to your iOS App or embed it inside iOS:
14 |
15 | - Generate the private key and the certificate
16 | ```
17 | openssl req -newkey rsa:2048 -nodes -keyout private_key.key -x509 -days 3650 -out certificate.pem
18 | ```
19 | - Convert the certificate from PEM to DER
20 | ```
21 | openssl x509 -outform der -in certificate.pem -out certificate.der
22 | ```
23 | - Check the certificate and private key
24 | ```
25 | openssl x509 -text -noout -inform DER -in certificate.der
26 | openssl rsa -check -in private_key.key
27 | ```
28 |
29 | Once you have that certificate.der inside iOS, you just have to read it and get the NSData:
30 |
31 | ```swift
32 | // first try to get the path for certificate.der
33 | guard let certPath = NSBundle.mainBundle().pathForResource("certificate", ofType: "der") else { ... }
34 |
35 | // now get the data from the certificate file
36 | guard let certData = NSData(contentsOfFile: certPath) else { ... }
37 | ```
38 |
39 | Then you just create an instance of CryptoImportExportManager and call its importPublicKeyReferenceFromDERCertificate(data) method:
40 |
41 | ```swift
42 | let importExportManager = CryptoExportImportManager()
43 | if let publicKeyRef = importExportManager.importPublicKeyReferenceFromDERCertificate(certData) {
44 | // use publicKeyRef to sign, decrypt, etc..
45 | } else { ... handle error ... }
46 | ```
47 |
48 | importPublicKeyReferenceFromDERCertificate(data) returns a SecKeyRef without adding it to the KeyChain.
49 |
50 | ### Export Apple keys to a valid, readable format
51 | In order to export a public key in a format that can be read and managed by external sources (PHP, Ruby, OpenSSL...) you just need to retrieve the public key bytes in a NSData by using SecItemCopyMatching, and then create an instance of CryptoImportExportManager and call its exportPublicKeyToDER(data, keyType, keySize) or exportPublicKeyToPEM(data, keyType, keySize) methods, depending on your desired output format. keyType can be either kSecAttrKeyTypeRSA or kSecAttrKeyTypeEC (the two key types allowed by Apple). Valid key sizes are 1024, 2048 and 4096 for RSA keys, and 256, 384 and 521 for EC keys (corresponding to the curves ansiX9p256r1, secpr384r1 and secp521r1).
52 |
53 | ```swift
54 | let keyType = kSecAttrKeyTypeEC
55 | let keySize = 256
56 | let exportImportManager = CryptoExportImportManager()
57 | if let exportablePEMKey = exportImportManager.exportPublicKeyToPEM(pubKeyData, keyType: keyType, keySize: keySize) {
58 | // send pem string to server.
59 | } else { ... }
60 | ```
61 |
62 | > Note: contrary to what's specified in the documentation for kSecECCurveSecp256r1, the curve that needs to be specified in order for the generated key to be valid is not a prime256v1 (aka secp256r1), but a ansiX9p256r1 (OID 1 2 840 10045 3 1 7).
63 |
64 | ## Checking the keys:
65 | The project comes also with a sample PHP file that would read a key obtained from the CryptoImportExportManager. You just need to copy the PEM formatted output from the export controller, save it in a file, and then call readkey.php:
66 |
67 | ```
68 | php readkey.php public_key_file.pem
69 | ```
70 | ## License
71 |
72 | The MIT License (MIT)
73 |
74 | Copyright (c) 2015 Ignacio Nieto Carvajal (http://digitalleaves.com)
75 |
76 | Permission is hereby granted, free of charge, to any person obtaining a copy
77 | of this software and associated documentation files (the "Software"), to deal
78 | in the Software without restriction, including without limitation the rights
79 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
80 | copies of the Software, and to permit persons to whom the Software is
81 | furnished to do so, subject to the following conditions:
82 |
83 | The above copyright notice and this permission notice shall be included in
84 | all copies or substantial portions of the Software.
85 |
86 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
87 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
88 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
89 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
90 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
91 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
92 | THE SOFTWARE.
93 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/ExportViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ExportViewController.swift
3 | // CryptoLoadExternalCertificate
4 | //
5 | // Created by Ignacio Nieto Carvajal on 11/10/15.
6 | // Copyright © 2015 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private let kExportKeyTag = "com.CryptoLoadExternalCertificate.exampleKey"
12 |
13 | class ExportViewController: UIViewController {
14 | @IBOutlet weak var textView: UITextView!
15 | @IBOutlet weak var keyTypeSegment: UISegmentedControl!
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | // Do any additional setup after loading the view.
21 | }
22 |
23 | override func didReceiveMemoryWarning() {
24 | super.didReceiveMemoryWarning()
25 | // Dispose of any resources that can be recreated.
26 | }
27 |
28 | func getKeyTypeFromSegmentedControl() -> String {
29 | if self.keyTypeSegment.selectedSegmentIndex == 0 {
30 | return kSecAttrKeyTypeEC as String
31 | } else {
32 | return kSecAttrKeyTypeRSA as String
33 | }
34 | }
35 |
36 | func getKeyLengthFromSegmentedControl() -> Int {
37 | if self.keyTypeSegment.selectedSegmentIndex == 0 {
38 | return 256 // EC key length
39 | } else {
40 | return 2048 // RSA 2048
41 | }
42 | }
43 |
44 | @IBAction func generateAndExportPublicKey(_ sender: AnyObject) {
45 | self.view.isUserInteractionEnabled = false
46 | self.textView.text = "Trying to get public key data from Keychain first..."
47 | let keyType = getKeyTypeFromSegmentedControl()
48 | if let publicKeyData = getPublicKeyData(kExportKeyTag + keyType) {
49 | self.textView.text = self.textView.text + "Success!\nPublic key raw bytes: \(publicKeyData.hexDescription)\n\n"
50 | self.exportKeyFromRawBytesAndShowInTextView(publicKeyData)
51 | self.view.isUserInteractionEnabled = true
52 | } else {
53 | self.textView.text = self.textView.text + "Failed! Will try to generate keypair...\n"
54 | createSecureKeyPair(kExportKeyTag + keyType) { (success, pubKeyData) -> Void in
55 | if success && pubKeyData != nil {
56 | self.textView.text = self.textView.text + "Success!\nPublic key raw bytes:\(pubKeyData!)\n"
57 | self.exportKeyFromRawBytesAndShowInTextView(pubKeyData!)
58 | } else {
59 | self.textView.text = self.textView.text + "Oups! I was unable to generate the keypair to test the export functionality."
60 | }
61 | self.view.isUserInteractionEnabled = true
62 | }
63 | }
64 | }
65 |
66 | func exportKeyFromRawBytesAndShowInTextView(_ rawBytes: Data) {
67 | let keyType = getKeyTypeFromSegmentedControl()
68 | let keySize = getKeyLengthFromSegmentedControl()
69 | let exportImportManager = CryptoExportImportManager()
70 | if let exportableDERKey = exportImportManager.exportPublicKeyToDER(rawBytes, keyType: keyType, keySize: keySize) {
71 | self.textView.text = self.textView.text + "Exportable key in DER format:\n\(exportableDERKey.hexDescription)\n\n"
72 | print("Exportable key in DER format:\n\(exportableDERKey.hexDescription)\n")
73 | let exportablePEMKey = exportImportManager.PEMKeyFromDERKey(exportableDERKey)
74 | self.textView.text = self.textView.text + "Exportable key in PEM format:\n\(exportablePEMKey)\n\n"
75 | print("Exportable key in PEM format:\n\(exportablePEMKey)\n")
76 | } else {
77 | self.textView.text = self.textView.text + "Unable to generate DER key from raw bytes."
78 | }
79 | }
80 |
81 | @IBAction func deleteGeneratedKey(_ sender: AnyObject) {
82 | self.deleteSecureKeyPair(kExportKeyTag + getKeyTypeFromSegmentedControl()) { (success) -> Void in
83 | self.textView.text = success ? "Successfully deleted keypair" : "Error deleting keypair. Maybe the key didn't exist?"
84 | }
85 | }
86 |
87 |
88 | // MARK: - Auxiliary key generation and management methods
89 |
90 | func createSecureKeyPair(_ keyTag: String, completion: ((_ success: Bool, _ pubKeyData: Data?) -> Void)? = nil) {
91 | // private key parameters
92 | let privateKeyParams: [String: AnyObject] = [
93 | kSecAttrIsPermanent as String: true as AnyObject,
94 | kSecAttrApplicationTag as String: keyTag as AnyObject,
95 | ]
96 |
97 | // private key parameters
98 | let publicKeyParams: [String: AnyObject] = [
99 | kSecAttrApplicationTag as String: keyTag as AnyObject,
100 | kSecAttrIsPermanent as String: true as AnyObject
101 | ]
102 |
103 | // global parameters for our key generation
104 | let parameters: [String: AnyObject] = [
105 | kSecAttrKeyType as String: getKeyTypeFromSegmentedControl() as AnyObject,
106 | kSecAttrKeySizeInBits as String: getKeyLengthFromSegmentedControl() as AnyObject,
107 | kSecPublicKeyAttrs as String: publicKeyParams as AnyObject,
108 | kSecPrivateKeyAttrs as String: privateKeyParams as AnyObject,
109 | ]
110 |
111 | // asynchronously generate the key pair and call the completion block
112 | DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in
113 | var pubKey, privKey: SecKey?
114 | let status = SecKeyGeneratePair(parameters as CFDictionary, &pubKey, &privKey)
115 | if status == errSecSuccess {
116 | DispatchQueue.main.async(execute: {
117 | print("Successfully generated keypair!\nPrivate key: \(privKey)\nPublic key: \(pubKey)")
118 | let publicKeyData = self.getPublicKeyData(kExportKeyTag + self.getKeyTypeFromSegmentedControl())
119 | completion?(true, publicKeyData)
120 | })
121 | } else {
122 | DispatchQueue.main.async(execute: {
123 | print("Error generating keypair: \(status)")
124 | completion?(false, nil)
125 | })
126 | }
127 | }
128 | }
129 |
130 | func getPublicKeyData(_ keyTag: String) -> Data? {
131 | let parameters = [
132 | kSecClass as String: kSecClassKey,
133 | kSecAttrKeyType as String: getKeyTypeFromSegmentedControl(),
134 | kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
135 | kSecAttrApplicationTag as String: keyTag,
136 | kSecReturnData as String: true
137 | ] as [String : Any]
138 | var data: AnyObject?
139 | let status = SecItemCopyMatching(parameters as CFDictionary, &data)
140 | if status == errSecSuccess {
141 | return data as? Data
142 | } else { print("Error getting public key data: \(status)"); return nil }
143 | }
144 |
145 | func deleteSecureKeyPair(_ keyTag: String, completion: ((_ success: Bool) -> Void)?) {
146 | // private query dictionary
147 | let query = [
148 | kSecClass as String: kSecClassKey,
149 | kSecAttrKeyType as String: getKeyTypeFromSegmentedControl(),
150 | kSecAttrApplicationTag as String: keyTag,
151 | ] as [String : Any]
152 |
153 | DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async { () -> Void in
154 | let status = SecItemDelete(query as CFDictionary) // delete key
155 | DispatchQueue.main.async(execute: { completion?(status == errSecSuccess) })
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/CryptoExportImportManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CryptoExportImportManager
3 | // CryptoExportImportManager
4 | //
5 | // Created by Ignacio Nieto Carvajal on 6/10/15.
6 | // Copyright © 2015 Ignacio Nieto Carvajal. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /*
12 |
13 | EC keys: http://www.opensource.apple.com/source/security_certtool/security_certtool-55103/src/dumpasn1.cfg
14 |
15 | EC param 1
16 | OID = 06 07 2A 86 48 CE 3D 02 01
17 | Comment = ANSI X9.62 public key type
18 | Description = ecPublicKey (1 2 840 10045 2 1)
19 |
20 | EC param 2
21 | OID = 06 08 2A 86 48 CE 3D 03 01 07
22 | Comment = ANSI X9.62 named elliptic curve
23 | Description = ansiX9p256r1 (1 2 840 10045 3 1 7)
24 |
25 | OID = 06 05 2B 81 04 00 22
26 | Comment = SECG (Certicom) named elliptic curve
27 | Description = secp384r1 (1 3 132 0 34)
28 |
29 | OID = 06 05 2B 81 04 00 23
30 | Comment = SECG (Certicom) named elliptic curve
31 | Description = secp521r1 (1 3 132 0 35)
32 |
33 | EC params sequence: public key + curve 256r1
34 | 30 13 06 07 2A 86 48 CE 3D 02 01 06 08 2A 86 48 CE 3D 03 01 07
35 | */
36 |
37 | // SECP256R1 EC public key header (length + EC params (sequence) + bitstring
38 | private let kCryptoExportImportManagerSecp256r1CurveLen = 256
39 | private let kCryptoExportImportManagerSecp256r1header: [UInt8] = [0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00]
40 | private let kCryptoExportImportManagerSecp256r1headerLen = 26
41 |
42 | private let kCryptoExportImportManagerSecp384r1CurveLen = 384
43 | private let kCryptoExportImportManagerSecp384r1header: [UInt8] = [0x30, 0x76, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x03, 0x62, 0x00]
44 | private let kCryptoExportImportManagerSecp384r1headerLen = 23
45 |
46 | private let kCryptoExportImportManagerSecp521r1CurveLen = 521
47 | private let kCryptoExportImportManagerSecp521r1header: [UInt8] = [0x30, 0x81, 0x9B, 0x30, 0x10, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, 0x00]
48 | private let kCryptoExportImportManagerSecp521r1headerLen = 25
49 |
50 | /*
51 |
52 | RSA keys: http://www.opensource.apple.com/source/security_certtool/security_certtool-55103/src/dumpasn1.cfg
53 |
54 | OID = 06 09 2A 86 48 86 F7 0D 01 01 01
55 | Comment = PKCS #1
56 | Description = rsaEncryption (1 2 840 113549 1 1 1)
57 |
58 | NULL byte: 05 00
59 | */
60 |
61 | // RSA OID header
62 | private let kCryptoExportImportManagerRSAOIDHeader: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
63 | private let kCryptoExportImportManagerRSAOIDHeaderLength = 15
64 |
65 | // ASN.1 encoding parameters.
66 | private let kCryptoExportImportManagerASNHeaderSequenceMark: UInt8 = 48 // 0x30
67 | private let kCryptoExportImportManagerASNHeaderIntegerMark: UInt8 = 02 // 0x32
68 | private let kCryptoExportImportManagerASNHeaderBitstringMark: UInt8 = 03 //0x03
69 | private let kCryptoExportImportManagerASNHeaderNullMark: UInt8 = 05 //0x05
70 | private let kCryptoExportImportManagerASNHeaderRSAEncryptionObjectMark: UInt8 = 06 //0x06
71 | private let kCryptoExportImportManagerExtendedLengthMark: UInt8 = 128 // 0x80
72 | private let kCryptoExportImportManagerASNHeaderLengthForRSA = 15
73 |
74 | // PEM encoding constants
75 | private let kCryptoExportImportManagerPublicKeyInitialTag = "-----BEGIN PUBLIC KEY-----\n"
76 | private let kCryptoExportImportManagerPublicKeyFinalTag = "-----END PUBLIC KEY-----"
77 | private let kCryptoExportImportManagerPublicNumberOfCharactersInALine = 64
78 |
79 | /**
80 | * This class exists due to the easy and intuitive way of using public keys generated outside iOS in
81 | * the Security framework and CommonCrypto tools (yes, I'm being sarcastic here).
82 | * CryptoCertificateImportManager is in charge of importing a certificate and obtaining a valid key
83 | * reference to use in any of SecKey operations (SecKeyEncrypt, SecKeyRawVerify...).
84 | * As far as I know, any other way of importing and using public keys from the outside is not
85 | * advised: https://devforums.apple.com/message/301532#301532
86 | */
87 | class CryptoExportImportManager: NSObject {
88 | // MARK: - Import methods.
89 |
90 | /**
91 | * Extracts the public key from a X.509 certificate and returns a valid SecKeyRef that can be
92 | * used in any of SecKey operations (SecKeyEncrypt, SecKeyRawVerify...).
93 | * Receives the certificate data in DER format.
94 | */
95 | func importPublicKeyReferenceFromDERCertificate(_ certData: Data) -> SecKey? {
96 | // first we create the certificate reference
97 | guard let certRef = SecCertificateCreateWithData(nil, certData as CFData) else { return nil }
98 | print("Successfully generated a valid certificate reference from the data.")
99 |
100 | // now create a SecTrust structure from the certificate where to extract the key from
101 | var secTrust: SecTrust?
102 | let secTrustStatus = SecTrustCreateWithCertificates(certRef, nil, &secTrust)
103 | print("Generating a SecTrust reference from the certificate: \(secTrustStatus)")
104 | if secTrustStatus != errSecSuccess { return nil }
105 |
106 | // now evaluate the certificate.
107 | var resultType: SecTrustResultType = SecTrustResultType(rawValue: UInt32(0))! // result will be ignored.
108 | let evaluateStatus = SecTrustEvaluate(secTrust!, &resultType)
109 | print("Evaluating the obtained SecTrust reference: \(evaluateStatus)")
110 | if evaluateStatus != errSecSuccess { return nil }
111 |
112 | // lastly, once evaluated, we can export the public key from the certificate leaf.
113 | let publicKeyRef = SecTrustCopyPublicKey(secTrust!)
114 | print("Got public key reference: \(String(describing: publicKeyRef))")
115 | return publicKeyRef
116 | }
117 |
118 | // MARK: - Export methods.
119 |
120 | /**
121 | * Exports a key retrieved from the keychain so it can be used outside iOS (i.e: in OpenSSL).
122 | * Returns a DER representation of the key.
123 | */
124 | func exportPublicKeyToDER(_ rawPublicKeyBytes: Data, keyType: String, keySize: Int) -> Data? {
125 | if keyType == kSecAttrKeyTypeEC as String {
126 | return exportECPublicKeyToDER(rawPublicKeyBytes, keyType: keyType, keySize: keySize)
127 | } else if keyType == kSecAttrKeyTypeRSA as String {
128 | return exportRSAPublicKeyToDER(rawPublicKeyBytes, keyType: keyType, keySize: keySize)
129 | }
130 | // unknown key type? return nil
131 | return nil
132 | }
133 |
134 | /**
135 | * Exports a key retrieved from the keychain so it can be used outside iOS (i.e: in OpenSSL).
136 | * Returns a PEM representation of the key.
137 | */
138 | func exportPublicKeyToPEM(_ rawPublicKeyBytes: Data, keyType: String, keySize: Int) -> String? {
139 | if keyType == kSecAttrKeyTypeEC as String {
140 | return exportECPublicKeyToPEM(rawPublicKeyBytes, keyType: keyType, keySize: keySize)
141 | } else if keyType == kSecAttrKeyTypeRSA as String {
142 | return exportRSAPublicKeyToPEM(rawPublicKeyBytes, keyType: keyType, keySize: keySize)
143 | }
144 | // unknown key type? return nil
145 | return nil
146 | }
147 |
148 | /**
149 | * This function prepares a RSA public key generated with Apple SecKeyGeneratePair to be exported
150 | * and used outisde iOS, be it openSSL, PHP, Perl, whatever. By default Apple exports RSA public
151 | * keys in a very raw format. If we want to use it on OpenSSL, PHP or almost anywhere outside iOS, we
152 | * need to remove add the full PKCS#1 ASN.1 wrapping. Returns a DER representation of the key.
153 | */
154 | func exportRSAPublicKeyToDER(_ rawPublicKeyBytes: Data, keyType: String, keySize: Int) -> Data {
155 | // first we create the space for the ASN.1 header and decide about its length
156 | let bitstringEncodingLength = bytesNeededForRepresentingInteger(rawPublicKeyBytes.count)
157 |
158 | // start building the ASN.1 header
159 | var headerBuffer = [UInt8](repeating: 0, count: kCryptoExportImportManagerASNHeaderLengthForRSA);
160 | headerBuffer[0] = kCryptoExportImportManagerASNHeaderSequenceMark;
161 |
162 | // total size (OID + encoding + key size) + 2 (marks)
163 | let totalSize = kCryptoExportImportManagerRSAOIDHeaderLength + bitstringEncodingLength + rawPublicKeyBytes.count + 3
164 | let totalSizebitstringEncodingLength = encodeASN1LengthParameter(totalSize, buffer: &(headerBuffer[1]))
165 |
166 | // bitstring header
167 | var keyLengthBytesEncoded = 0
168 | var bitstringBuffer = [UInt8](repeating: 0, count: kCryptoExportImportManagerASNHeaderLengthForRSA);
169 | bitstringBuffer[0] = kCryptoExportImportManagerASNHeaderBitstringMark
170 | keyLengthBytesEncoded = encodeASN1LengthParameter(rawPublicKeyBytes.count+1, buffer: &(bitstringBuffer[1]))
171 | bitstringBuffer[keyLengthBytesEncoded + 1] = 0x00
172 |
173 | // build DER key.
174 | var derKey = Data(capacity: totalSize + totalSizebitstringEncodingLength)
175 | derKey.append(headerBuffer, count: totalSizebitstringEncodingLength + 1)
176 | derKey.append(kCryptoExportImportManagerRSAOIDHeader, count: kCryptoExportImportManagerRSAOIDHeaderLength) // Add OID header
177 | derKey.append(bitstringBuffer, count: keyLengthBytesEncoded + 2) // 0x03 + key bitstring length + 0x00
178 | derKey.append(rawPublicKeyBytes) // public key raw data.
179 |
180 | return derKey
181 | }
182 |
183 | /**
184 | * This function prepares a RSA public key generated with Apple SecKeyGeneratePair to be exported
185 | * and used outisde iOS, be it openSSL, PHP, Perl, whatever. By default Apple exports RSA public
186 | * keys in a very raw format. If we want to use it on OpenSSL, PHP or almost anywhere outside iOS, we
187 | * need to remove add the full PKCS#1 ASN.1 wrapping. Returns a DER representation of the key.
188 | */
189 | func exportRSAPublicKeyToPEM(_ rawPublicKeyBytes: Data, keyType: String, keySize: Int) -> String {
190 | return PEMKeyFromDERKey(exportRSAPublicKeyToDER(rawPublicKeyBytes, keyType: keyType, keySize: keySize))
191 | }
192 |
193 |
194 | /**
195 | * Returns the number of bytes needed to represent an integer.
196 | */
197 | func bytesNeededForRepresentingInteger(_ number: Int) -> Int {
198 | if number <= 0 { return 0 }
199 | var i = 1
200 | while (i < 8 && number >= (1 << (i * 8))) { i += 1 }
201 | return i
202 | }
203 |
204 | /**
205 | * Generates an ASN.1 length sequence for the given length. Modifies the buffer parameter by
206 | * writing the ASN.1 sequence. The memory of buffer must be initialized (i.e: from an NSData).
207 | * Returns the number of bytes used to write the sequence.
208 | */
209 | func encodeASN1LengthParameter(_ length: Int, buffer: UnsafeMutablePointer) -> Int {
210 | if length < Int(kCryptoExportImportManagerExtendedLengthMark) {
211 | buffer[0] = UInt8(length)
212 | return 1 // just one byte was used, no need for length starting mark (0x80).
213 | } else {
214 | let extraBytes = bytesNeededForRepresentingInteger(length)
215 | var currentLengthValue = length
216 |
217 | buffer[0] = kCryptoExportImportManagerExtendedLengthMark + UInt8(extraBytes)
218 | for i in 0 ..< extraBytes {
219 | buffer[extraBytes - i] = UInt8(currentLengthValue & 0xff)
220 | currentLengthValue = currentLengthValue >> 8
221 | }
222 | return extraBytes + 1 // 1 byte for the starting mark (0x80 + bytes used) + bytes used to encode length.
223 | }
224 | }
225 |
226 |
227 | /**
228 | * This function prepares a EC public key generated with Apple SecKeyGeneratePair to be exported
229 | * and used outisde iOS, be it openSSL, PHP, Perl, whatever. It basically adds the proper ASN.1
230 | * header and codifies the result as valid base64 string, 64 characters split.
231 | * Returns a DER representation of the key.
232 | */
233 | func exportECPublicKeyToDER(_ rawPublicKeyBytes: Data, keyType: String, keySize: Int) -> Data {
234 | print("Exporting EC raw key: \(rawPublicKeyBytes)")
235 | // first retrieve the header with the OID for the proper key curve.
236 | let curveOIDHeader: [UInt8]
237 | let curveOIDHeaderLen: Int
238 | switch (keySize) {
239 | case kCryptoExportImportManagerSecp256r1CurveLen:
240 | curveOIDHeader = kCryptoExportImportManagerSecp256r1header
241 | curveOIDHeaderLen = kCryptoExportImportManagerSecp256r1headerLen
242 | case kCryptoExportImportManagerSecp384r1CurveLen:
243 | curveOIDHeader = kCryptoExportImportManagerSecp384r1header
244 | curveOIDHeaderLen = kCryptoExportImportManagerSecp384r1headerLen
245 | case kCryptoExportImportManagerSecp521r1CurveLen:
246 | curveOIDHeader = kCryptoExportImportManagerSecp521r1header
247 | curveOIDHeaderLen = kCryptoExportImportManagerSecp521r1headerLen
248 | default:
249 | curveOIDHeader = []
250 | curveOIDHeaderLen = 0
251 | }
252 | var data = Data(bytes: curveOIDHeader, count: curveOIDHeaderLen)
253 |
254 | // now add the raw data from the retrieved public key
255 | data.append(rawPublicKeyBytes)
256 | return data
257 | }
258 |
259 | /**
260 | * This function prepares a EC public key generated with Apple SecKeyGeneratePair to be exported
261 | * and used outisde iOS, be it openSSL, PHP, Perl, whatever. It basically adds the proper ASN.1
262 | * header and codifies the result as valid base64 string, 64 characters split.
263 | * Returns a DER representation of the key.
264 | */
265 | func exportECPublicKeyToPEM(_ rawPublicKeyBytes: Data, keyType: String, keySize: Int) -> String {
266 | return PEMKeyFromDERKey(exportECPublicKeyToDER(rawPublicKeyBytes, keyType: keyType, keySize: keySize))
267 | }
268 |
269 | /**
270 | * This method transforms a DER encoded key to PEM format. It gets a Base64 representation of
271 | * the key and then splits this base64 string in 64 character chunks. Then it wraps it in
272 | * BEGIN and END key tags.
273 | */
274 | func PEMKeyFromDERKey(_ data: Data) -> String {
275 | // base64 encode the result
276 | let base64EncodedString = data.base64EncodedString(options: [])
277 |
278 | // split in lines of 64 characters.
279 | var currentLine = ""
280 | var resultString = kCryptoExportImportManagerPublicKeyInitialTag
281 | var charCount = 0
282 | for character in base64EncodedString {
283 | charCount += 1
284 | currentLine.append(character)
285 | if charCount == kCryptoExportImportManagerPublicNumberOfCharactersInALine {
286 | resultString += currentLine + "\n"
287 | charCount = 0
288 | currentLine = ""
289 | }
290 | }
291 | // final line (if any)
292 | if currentLine.count > 0 { resultString += currentLine + "\n" }
293 | // final tag
294 | resultString += kCryptoExportImportManagerPublicKeyFinalTag
295 | return resultString
296 | }
297 |
298 | }
299 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6686BBC41BC3E2A000E9D1CA /* CryptoExportImportManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6686BBC31BC3E2A000E9D1CA /* CryptoExportImportManager.swift */; };
11 | 6686BBC81BC3F06D00E9D1CA /* certificate.der in Resources */ = {isa = PBXBuildFile; fileRef = 6686BBC71BC3F06D00E9D1CA /* certificate.der */; };
12 | 668B94D81BC3D19F00913B83 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 668B94D71BC3D19F00913B83 /* AppDelegate.swift */; };
13 | 668B94DA1BC3D19F00913B83 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 668B94D91BC3D19F00913B83 /* ViewController.swift */; };
14 | 668B94DD1BC3D19F00913B83 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 668B94DB1BC3D19F00913B83 /* Main.storyboard */; };
15 | 668B94DF1BC3D19F00913B83 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 668B94DE1BC3D19F00913B83 /* Assets.xcassets */; };
16 | 668B94E21BC3D19F00913B83 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 668B94E01BC3D19F00913B83 /* LaunchScreen.storyboard */; };
17 | 66B460A61BCA9886000FFCCE /* ImportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66B460A51BCA9886000FFCCE /* ImportViewController.swift */; };
18 | 66B460A81BCA988E000FFCCE /* ExportViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66B460A71BCA988E000FFCCE /* ExportViewController.swift */; };
19 | 66E9AEAB1DA67A8B00863AA6 /* Data+HexDescription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66E9AEAA1DA67A8B00863AA6 /* Data+HexDescription.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | 6686BBC31BC3E2A000E9D1CA /* CryptoExportImportManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CryptoExportImportManager.swift; sourceTree = ""; };
24 | 6686BBC71BC3F06D00E9D1CA /* certificate.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = certificate.der; sourceTree = ""; };
25 | 668B94D41BC3D19F00913B83 /* CryptoLoadExternalCertificate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CryptoLoadExternalCertificate.app; sourceTree = BUILT_PRODUCTS_DIR; };
26 | 668B94D71BC3D19F00913B83 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
27 | 668B94D91BC3D19F00913B83 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
28 | 668B94DC1BC3D19F00913B83 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
29 | 668B94DE1BC3D19F00913B83 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
30 | 668B94E11BC3D19F00913B83 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
31 | 668B94E31BC3D19F00913B83 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | 66B460A51BCA9886000FFCCE /* ImportViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportViewController.swift; sourceTree = ""; };
33 | 66B460A71BCA988E000FFCCE /* ExportViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExportViewController.swift; sourceTree = ""; };
34 | 66E9AEA81DA6789100863AA6 /* CryptoLoadExternalCertificate.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CryptoLoadExternalCertificate.entitlements; sourceTree = ""; };
35 | 66E9AEAA1DA67A8B00863AA6 /* Data+HexDescription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+HexDescription.swift"; sourceTree = ""; };
36 | /* End PBXFileReference section */
37 |
38 | /* Begin PBXFrameworksBuildPhase section */
39 | 668B94D11BC3D19F00913B83 /* Frameworks */ = {
40 | isa = PBXFrameworksBuildPhase;
41 | buildActionMask = 2147483647;
42 | files = (
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | 668B94CB1BC3D19F00913B83 = {
50 | isa = PBXGroup;
51 | children = (
52 | 668B94D61BC3D19F00913B83 /* CryptoLoadExternalCertificate */,
53 | 668B94D51BC3D19F00913B83 /* Products */,
54 | );
55 | sourceTree = "";
56 | };
57 | 668B94D51BC3D19F00913B83 /* Products */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 668B94D41BC3D19F00913B83 /* CryptoLoadExternalCertificate.app */,
61 | );
62 | name = Products;
63 | sourceTree = "";
64 | };
65 | 668B94D61BC3D19F00913B83 /* CryptoLoadExternalCertificate */ = {
66 | isa = PBXGroup;
67 | children = (
68 | 66E9AEA91DA67A7700863AA6 /* Categories and Extensions */,
69 | 66B460A41BCA9853000FFCCE /* Model */,
70 | 66B460A31BCA9844000FFCCE /* Storyboards */,
71 | 66B460A21BCA9829000FFCCE /* App */,
72 | 66B460A11BCA97C7000FFCCE /* Resources & Assets */,
73 | 66B460A01BCA97BB000FFCCE /* View Controllers */,
74 | );
75 | path = CryptoLoadExternalCertificate;
76 | sourceTree = "";
77 | };
78 | 66B460A01BCA97BB000FFCCE /* View Controllers */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 668B94D91BC3D19F00913B83 /* ViewController.swift */,
82 | 66B460A51BCA9886000FFCCE /* ImportViewController.swift */,
83 | 66B460A71BCA988E000FFCCE /* ExportViewController.swift */,
84 | );
85 | name = "View Controllers";
86 | sourceTree = "";
87 | };
88 | 66B460A11BCA97C7000FFCCE /* Resources & Assets */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 66E9AEA81DA6789100863AA6 /* CryptoLoadExternalCertificate.entitlements */,
92 | 6686BBC71BC3F06D00E9D1CA /* certificate.der */,
93 | 668B94DE1BC3D19F00913B83 /* Assets.xcassets */,
94 | );
95 | name = "Resources & Assets";
96 | sourceTree = "";
97 | };
98 | 66B460A21BCA9829000FFCCE /* App */ = {
99 | isa = PBXGroup;
100 | children = (
101 | 668B94D71BC3D19F00913B83 /* AppDelegate.swift */,
102 | 668B94E31BC3D19F00913B83 /* Info.plist */,
103 | );
104 | name = App;
105 | sourceTree = "";
106 | };
107 | 66B460A31BCA9844000FFCCE /* Storyboards */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 668B94DB1BC3D19F00913B83 /* Main.storyboard */,
111 | 668B94E01BC3D19F00913B83 /* LaunchScreen.storyboard */,
112 | );
113 | name = Storyboards;
114 | sourceTree = "";
115 | };
116 | 66B460A41BCA9853000FFCCE /* Model */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 6686BBC31BC3E2A000E9D1CA /* CryptoExportImportManager.swift */,
120 | );
121 | name = Model;
122 | sourceTree = "";
123 | };
124 | 66E9AEA91DA67A7700863AA6 /* Categories and Extensions */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 66E9AEAA1DA67A8B00863AA6 /* Data+HexDescription.swift */,
128 | );
129 | name = "Categories and Extensions";
130 | sourceTree = "";
131 | };
132 | /* End PBXGroup section */
133 |
134 | /* Begin PBXNativeTarget section */
135 | 668B94D31BC3D19F00913B83 /* CryptoLoadExternalCertificate */ = {
136 | isa = PBXNativeTarget;
137 | buildConfigurationList = 668B94E61BC3D19F00913B83 /* Build configuration list for PBXNativeTarget "CryptoLoadExternalCertificate" */;
138 | buildPhases = (
139 | 668B94D01BC3D19F00913B83 /* Sources */,
140 | 668B94D11BC3D19F00913B83 /* Frameworks */,
141 | 668B94D21BC3D19F00913B83 /* Resources */,
142 | );
143 | buildRules = (
144 | );
145 | dependencies = (
146 | );
147 | name = CryptoLoadExternalCertificate;
148 | productName = CryptoLoadExternalCertificate;
149 | productReference = 668B94D41BC3D19F00913B83 /* CryptoLoadExternalCertificate.app */;
150 | productType = "com.apple.product-type.application";
151 | };
152 | /* End PBXNativeTarget section */
153 |
154 | /* Begin PBXProject section */
155 | 668B94CC1BC3D19F00913B83 /* Project object */ = {
156 | isa = PBXProject;
157 | attributes = {
158 | LastUpgradeCheck = 0800;
159 | ORGANIZATIONNAME = "Ignacio Nieto Carvajal";
160 | TargetAttributes = {
161 | 668B94D31BC3D19F00913B83 = {
162 | CreatedOnToolsVersion = 7.0.1;
163 | DevelopmentTeam = 97P9DZHP78;
164 | LastSwiftMigration = 0800;
165 | SystemCapabilities = {
166 | com.apple.Keychain = {
167 | enabled = 1;
168 | };
169 | };
170 | };
171 | };
172 | };
173 | buildConfigurationList = 668B94CF1BC3D19F00913B83 /* Build configuration list for PBXProject "CryptoLoadExternalCertificate" */;
174 | compatibilityVersion = "Xcode 3.2";
175 | developmentRegion = English;
176 | hasScannedForEncodings = 0;
177 | knownRegions = (
178 | en,
179 | Base,
180 | );
181 | mainGroup = 668B94CB1BC3D19F00913B83;
182 | productRefGroup = 668B94D51BC3D19F00913B83 /* Products */;
183 | projectDirPath = "";
184 | projectRoot = "";
185 | targets = (
186 | 668B94D31BC3D19F00913B83 /* CryptoLoadExternalCertificate */,
187 | );
188 | };
189 | /* End PBXProject section */
190 |
191 | /* Begin PBXResourcesBuildPhase section */
192 | 668B94D21BC3D19F00913B83 /* Resources */ = {
193 | isa = PBXResourcesBuildPhase;
194 | buildActionMask = 2147483647;
195 | files = (
196 | 668B94E21BC3D19F00913B83 /* LaunchScreen.storyboard in Resources */,
197 | 668B94DF1BC3D19F00913B83 /* Assets.xcassets in Resources */,
198 | 668B94DD1BC3D19F00913B83 /* Main.storyboard in Resources */,
199 | 6686BBC81BC3F06D00E9D1CA /* certificate.der in Resources */,
200 | );
201 | runOnlyForDeploymentPostprocessing = 0;
202 | };
203 | /* End PBXResourcesBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | 668B94D01BC3D19F00913B83 /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | 6686BBC41BC3E2A000E9D1CA /* CryptoExportImportManager.swift in Sources */,
211 | 668B94DA1BC3D19F00913B83 /* ViewController.swift in Sources */,
212 | 66E9AEAB1DA67A8B00863AA6 /* Data+HexDescription.swift in Sources */,
213 | 668B94D81BC3D19F00913B83 /* AppDelegate.swift in Sources */,
214 | 66B460A61BCA9886000FFCCE /* ImportViewController.swift in Sources */,
215 | 66B460A81BCA988E000FFCCE /* ExportViewController.swift in Sources */,
216 | );
217 | runOnlyForDeploymentPostprocessing = 0;
218 | };
219 | /* End PBXSourcesBuildPhase section */
220 |
221 | /* Begin PBXVariantGroup section */
222 | 668B94DB1BC3D19F00913B83 /* Main.storyboard */ = {
223 | isa = PBXVariantGroup;
224 | children = (
225 | 668B94DC1BC3D19F00913B83 /* Base */,
226 | );
227 | name = Main.storyboard;
228 | sourceTree = "";
229 | };
230 | 668B94E01BC3D19F00913B83 /* LaunchScreen.storyboard */ = {
231 | isa = PBXVariantGroup;
232 | children = (
233 | 668B94E11BC3D19F00913B83 /* Base */,
234 | );
235 | name = LaunchScreen.storyboard;
236 | sourceTree = "";
237 | };
238 | /* End PBXVariantGroup section */
239 |
240 | /* Begin XCBuildConfiguration section */
241 | 668B94E41BC3D19F00913B83 /* Debug */ = {
242 | isa = XCBuildConfiguration;
243 | buildSettings = {
244 | ALWAYS_SEARCH_USER_PATHS = NO;
245 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
246 | CLANG_CXX_LIBRARY = "libc++";
247 | CLANG_ENABLE_MODULES = YES;
248 | CLANG_ENABLE_OBJC_ARC = YES;
249 | CLANG_WARN_BOOL_CONVERSION = YES;
250 | CLANG_WARN_CONSTANT_CONVERSION = YES;
251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
252 | CLANG_WARN_EMPTY_BODY = YES;
253 | CLANG_WARN_ENUM_CONVERSION = YES;
254 | CLANG_WARN_INFINITE_RECURSION = YES;
255 | CLANG_WARN_INT_CONVERSION = YES;
256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
257 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
258 | CLANG_WARN_UNREACHABLE_CODE = YES;
259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
260 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
261 | COPY_PHASE_STRIP = NO;
262 | DEBUG_INFORMATION_FORMAT = dwarf;
263 | ENABLE_STRICT_OBJC_MSGSEND = YES;
264 | ENABLE_TESTABILITY = YES;
265 | GCC_C_LANGUAGE_STANDARD = gnu99;
266 | GCC_DYNAMIC_NO_PIC = NO;
267 | GCC_NO_COMMON_BLOCKS = YES;
268 | GCC_OPTIMIZATION_LEVEL = 0;
269 | GCC_PREPROCESSOR_DEFINITIONS = (
270 | "DEBUG=1",
271 | "$(inherited)",
272 | );
273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
275 | GCC_WARN_UNDECLARED_SELECTOR = YES;
276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
277 | GCC_WARN_UNUSED_FUNCTION = YES;
278 | GCC_WARN_UNUSED_VARIABLE = YES;
279 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
280 | MTL_ENABLE_DEBUG_INFO = YES;
281 | ONLY_ACTIVE_ARCH = YES;
282 | SDKROOT = iphoneos;
283 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
284 | };
285 | name = Debug;
286 | };
287 | 668B94E51BC3D19F00913B83 /* Release */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ALWAYS_SEARCH_USER_PATHS = NO;
291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
292 | CLANG_CXX_LIBRARY = "libc++";
293 | CLANG_ENABLE_MODULES = YES;
294 | CLANG_ENABLE_OBJC_ARC = YES;
295 | CLANG_WARN_BOOL_CONVERSION = YES;
296 | CLANG_WARN_CONSTANT_CONVERSION = YES;
297 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
298 | CLANG_WARN_EMPTY_BODY = YES;
299 | CLANG_WARN_ENUM_CONVERSION = YES;
300 | CLANG_WARN_INFINITE_RECURSION = YES;
301 | CLANG_WARN_INT_CONVERSION = YES;
302 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
303 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
304 | CLANG_WARN_UNREACHABLE_CODE = YES;
305 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
306 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
307 | COPY_PHASE_STRIP = NO;
308 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
309 | ENABLE_NS_ASSERTIONS = NO;
310 | ENABLE_STRICT_OBJC_MSGSEND = YES;
311 | GCC_C_LANGUAGE_STANDARD = gnu99;
312 | GCC_NO_COMMON_BLOCKS = YES;
313 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
314 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
315 | GCC_WARN_UNDECLARED_SELECTOR = YES;
316 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
317 | GCC_WARN_UNUSED_FUNCTION = YES;
318 | GCC_WARN_UNUSED_VARIABLE = YES;
319 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
320 | MTL_ENABLE_DEBUG_INFO = NO;
321 | SDKROOT = iphoneos;
322 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
323 | VALIDATE_PRODUCT = YES;
324 | };
325 | name = Release;
326 | };
327 | 668B94E71BC3D19F00913B83 /* Debug */ = {
328 | isa = XCBuildConfiguration;
329 | buildSettings = {
330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
331 | CODE_SIGN_ENTITLEMENTS = CryptoLoadExternalCertificate/CryptoLoadExternalCertificate.entitlements;
332 | DEVELOPMENT_TEAM = 97P9DZHP78;
333 | INFOPLIST_FILE = CryptoLoadExternalCertificate/Info.plist;
334 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
335 | PRODUCT_BUNDLE_IDENTIFIER = "com.Ignacio-Nieto-Carvajal.CryptoLoadExternalCertificate";
336 | PRODUCT_NAME = "$(TARGET_NAME)";
337 | SWIFT_VERSION = 3.0;
338 | };
339 | name = Debug;
340 | };
341 | 668B94E81BC3D19F00913B83 /* Release */ = {
342 | isa = XCBuildConfiguration;
343 | buildSettings = {
344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
345 | CODE_SIGN_ENTITLEMENTS = CryptoLoadExternalCertificate/CryptoLoadExternalCertificate.entitlements;
346 | DEVELOPMENT_TEAM = 97P9DZHP78;
347 | INFOPLIST_FILE = CryptoLoadExternalCertificate/Info.plist;
348 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
349 | PRODUCT_BUNDLE_IDENTIFIER = "com.Ignacio-Nieto-Carvajal.CryptoLoadExternalCertificate";
350 | PRODUCT_NAME = "$(TARGET_NAME)";
351 | SWIFT_VERSION = 3.0;
352 | };
353 | name = Release;
354 | };
355 | /* End XCBuildConfiguration section */
356 |
357 | /* Begin XCConfigurationList section */
358 | 668B94CF1BC3D19F00913B83 /* Build configuration list for PBXProject "CryptoLoadExternalCertificate" */ = {
359 | isa = XCConfigurationList;
360 | buildConfigurations = (
361 | 668B94E41BC3D19F00913B83 /* Debug */,
362 | 668B94E51BC3D19F00913B83 /* Release */,
363 | );
364 | defaultConfigurationIsVisible = 0;
365 | defaultConfigurationName = Release;
366 | };
367 | 668B94E61BC3D19F00913B83 /* Build configuration list for PBXNativeTarget "CryptoLoadExternalCertificate" */ = {
368 | isa = XCConfigurationList;
369 | buildConfigurations = (
370 | 668B94E71BC3D19F00913B83 /* Debug */,
371 | 668B94E81BC3D19F00913B83 /* Release */,
372 | );
373 | defaultConfigurationIsVisible = 0;
374 | defaultConfigurationName = Release;
375 | };
376 | /* End XCConfigurationList section */
377 | };
378 | rootObject = 668B94CC1BC3D19F00913B83 /* Project object */;
379 | }
380 |
--------------------------------------------------------------------------------
/CryptoLoadExternalCertificate/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
27 |
41 |
48 |
55 |
69 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
124 |
131 |
132 |
133 |
134 | Click on "Export key" to generate a key pair. The raw bytes retrieved from the public key will be wrapped in a valid ASN.1 object and shown.
135 |
136 |
137 |
138 |
152 |
166 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
236 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
--------------------------------------------------------------------------------