├── 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 | --------------------------------------------------------------------------------