Is there a way to decode CSR to String to get attributes? - ios

I have CSR file with content like
-----BEGIN CERTIFICATE REQUEST-----
XXXX
-----END CERTIFICATE REQUEST-----
The CSR was generated with attributes
Attributes:
Requested Extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
xxx:
..TC200
yyy:
..1234
I tried to
guard let base64data = Data(base64Encoded: request.body) else {
return getResponse(message: "Most likely, the request sent was in a wrong format.", code: 400)
}
But then, base64data is nil.
Is it possible to decode the CSR, to access somehow xxx and yyy attribute in Swift?
Additional information is that it's encoded with base64.

I assume that you are looking for decoding an ASN1 DER encoded X.509 certificate. Check out the ASN1Decoder CocoaPod.
After installation you can use the Pod like in the following example:
import ASN1Decoder
let base64CertData = "MIID..."
guard let data = Data(base64Encoded: base64CertData) else {
return
}
do {
let x509 = try X509Certificate(data: certData)
let subject = x509.subjectDistinguishedName ?? ""
} catch {
print(error)
}
Please mind to remove the -----BEGIN CERTIFICATE REQUEST----- header and -----END CERTIFICATE REQUEST----- footer.

Related

How can I create a Public Key from a PEM File (String) in Swift?

I'm pretty new to RSA Keys and Certificates and all of that. So I'm getting a PEM File from a network request, I understand the PEM file is basically the certificate without header and footer, so essentially I have a string. I need to create a PublicKey from that in order to Encrypt some text, but I think I'm doing something wrong. This is my code so far, I have talked to other teams (Android) and when they print out the Encrypted Data, they are getting a regular String and I'm getting lots of weird characters. Thanks in advance guys! (:
func encryptBase64(text: String, certificateStr: String) throws -> Data {
let encryptionError = EncrpytionError(message: "There has been an issue with encryption")
guard
let certData = Data(base64Encoded: certificateStr),
let certificate = SecCertificateCreateWithData(nil, certData as CFData),
let publicKey = SecCertificateCopyKey(certificate)
else {
throw encryptionError
}
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA256AESGCM
guard SecKeyIsAlgorithmSupported(publicKey, .encrypt, algorithm) else {
throw encryptionError
}
guard let cipherText = SecKeyCreateEncryptedData(
publicKey,
algorithm,
text.data(using: .utf8)! as CFData, nil)
else {
throw encryptionError
}
return cipherText as Data
}
And when I try to print cipherText as a String, I get this weird thing:
"D�aj�\\m꒤h,�A�{��8�~�\nY\u0003F�Cˤ�#��\"�\u0018�\u0007\u001fX#VC�U_��E\u0005dž1���X��\/4Px��P\u0016�8}% ��<��#�I_�K\u000e�\bR*�� ���斋�7,�%���F^q�\u0000\\�'�ZTD\u0013Q�_\u0010\u001f>i]&��B���#1\u0006\b��E\u0004�F���yS\u0013�3����SB)��m\u0017%��5ʲ����s\u0003��r�&�?�8b��W#\u001e��؞ۡ��8�s~��ӹ�u\"�2��U�&\b�3XV���˔Y��xt[\fm&P:\\�\f� y��6jy"
Android team is doing something like this on Kotlin:
private fun extractPublicKey(certificateString: String): PublicKey {
val encodedCertificateBytes = certificateString.toByteArray()
val decodedCertificateBytes = Base64.decode(encodedCertificateBytes, Base64.DEFAULT)
val inStream = decodedCertificateBytes.inputStream()
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificate = certificateFactory.generateCertificate(inStream)
inStream.close()
return certificate.publicKey
}

How to use SecPKCS12Import with private key encrypted with AES_256

I am generating a PKCS12 file using OpenSSL library in Swift. Following is the code that I use to generate the file:
guard let p12 = PKCS12_create(passPhrase, name, privateKey, certificate, nil, NID_aes_256_cbc, 0, 0, 0, 0) else {
ERR_print_errors_fp(stderr)
return
}
Please note that I am using NID_aes_256_cbc to encrypt the private key.
Then when I use the following code to import the p12 file in swift, I had the error code of -26275.
var items: CFArray?
let certOptions: NSDictionary = [kSecImportExportPassphrase as NSString: passwordStr as NSString]
self.securityError = SecPKCS12Import(p12Data as NSData, certOptions, &items)
print(securityError!.description)
After consulting OSStatus for error code explanation, I found that it's a errSecInvalidKey.
If I change the parameter from NID_aes_256_cbc back to 0, which uses the default 3DES algorithm, everything works fine.
So I was wondering if SecPKCS12Import decrypting the private key with 3DES algorithm by default. If yes, how should I make SecPKCS12Import to decrypt the AES encrypted private key?

Client certificates and identities in iOS

I have generated private key and public key to my Swift-based iOS application using SecKeyGeneratePair function.Then, I generated Certificate Signing Request using iOS CSR generationand my server replied with certificate chain in PEM format.I converted PEM-certificate to DER-format using following code:
var modifiedCert = certJson.replacingOccurrences(of: "-----BEGIN CERTIFICATE-----", with: "")
modifiedCert = modifiedCert.replacingOccurrences(of: "-----END CERTIFICATE-----", with: "")
modifiedCert = modifiedCert.replacingOccurrences(of: "\n", with: "")
let dataDecoded = NSData(base64Encoded: modifiedCert, options: [])
Now, I should create certificate from DER-data using let certificate = SecCertificateCreateWithData(nil, certDer)
My question is following : How can I connect the certificate with private key I have created in the beginning and get the identity where both of these(keys and certificate) belongs?Maybe, add certificate to keychain and get the identity using SecItemCopyMatching? I have followed the procedure presented in question SecIdentityRef procedure
Edit:
When adding the certificate to keychain, I get the status response 0, which I believe means that certificate has been added to keychain.
let certificate: SecCertificate? = SecCertificateCreateWithData(nil, certDer)
if certificate != nil{
let params : [String: Any] = [
kSecClass as String : kSecClassCertificate,
kSecValueRef as String : certificate!
]
let status = SecItemAdd(params as CFDictionary, &certRef)
print(status)
}
Now when I'm trying to get the identity, I get status -25300 (errSecItemNotFound). Following code is used to get the identity. tag is the private key tag I have used to generate private/public key.
let query: [String: Any] = [
kSecClass as String : kSecClassIdentity,
kSecAttrApplicationTag as String : tag,
kSecReturnRef as String: true
]
var retrievedData: SecIdentity?
var extractedData: AnyObject?
let status = SecItemCopyMatching(query as NSDictionary, &extractedData)
if (status == errSecSuccess) {
retrievedData = extractedData as! SecIdentity?
}
I'm able to get the private key & public key & certificate from the keychain using SecItemCopyMatching and add the certificate to keychain, but querying the SecIdentity does not work. Is it possible that my certificate does not match to my keys? How is that checked?
I printed public key from iOS in base64 format. The following was printed:
MIIBCgKCAQEAo/MRST9oZpO3nTl243o+ocJfFCyKLtPgO/QiO9apb2sWq4kqexHy
58jIehBcz4uGJLyKYi6JHx/NgxdSRKE3PcjU2sopdMN35LeO6jZ34auH37gX41Sl
4HWkpMOB9v/OZvMoKrQJ9b6/qmBVZXYsrSJONbr+74/mI/m1VNtLOM2FIzewVYcL
HHsM38XOg/kjSUsHEUKET/FfJkozgp76r0r3E0khcbxwU70qc77YPgeJHglHcZKF
ZHFbvNz4E9qUy1mWJvoCmAEItWnyvuw+N9svD1Rri3t5qlaBwaIN/AtayHwJWoWA
/HF+Jg87eVvEErqeT1wARzJL2xv5V1O4ZwIDAQAB
Then from the certificate signing request I extracted the public key using openssl (openssl req -in ios.csr -pubkey -noout). The following response was printed:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo/MRST9oZpO3nTl243o+
ocJfFCyKLtPgO/QiO9apb2sWq4kqexHy58jIehBcz4uGJLyKYi6JHx/NgxdSRKE3
PcjU2sopdMN35LeO6jZ34auH37gX41Sl4HWkpMOB9v/OZvMoKrQJ9b6/qmBVZXYs
rSJONbr+74/mI/m1VNtLOM2FIzewVYcLHHsM38XOg/kjSUsHEUKET/FfJkozgp76
r0r3E0khcbxwU70qc77YPgeJHglHcZKFZHFbvNz4E9qUy1mWJvoCmAEItWnyvuw+
N9svD1Rri3t5qlaBwaIN/AtayHwJWoWA/HF+Jg87eVvEErqeT1wARzJL2xv5V1O4
ZwIDAQAB
-----END PUBLIC KEY----
It seems that there is a minor difference in the beginning of the key generated from CSR. (MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A). Based on the question RSA encryption, it seems that MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A is base64-formatted identifier for RSA encryption "1.2.840.113549.1.1.1". So I guess the public key might be fine?
We don't use that same method of CSR, but we have an equivalent thing where we do the following:
Generate key pair
Ship the public key to the remote server
Remote server generates a signed client certificate using the public key
Ship the client certificate back to the iOS device
Add the client certificate to the keychain
Later on, use the client certificate in an NSURLSession or similar.
As you seem to have discovered, iOS needs this extra thing called an "identity" to tie the client cert.
We also discovered that iOS has a weird thing where you need to DELETE the public key from the keychain before you add the client cert and identity into it, otherwise the identity doesn't seem to locate the client certificate properly instead. We chose to add the public key back in but as a "generic password" (i.e arbitrary user data) - we only do this because iOS doesn't have a sensible API for extracting a public key from a cert on the fly, and we need the public key for other strange things we happen to be doing.
If you're just doing TLS client certificate auth, once you have the certificate you won't need an explicit copy of the public key so you can simplify the process by simply deleting it, and skip the "add-back-in-as-generic-password" bit
Please excuse the giant pile of code, crypto stuff always seems to require a lot of work.
Here's bits of code to perform the above tasks:
Generating the keypair, and deleting/re-saving the public key
/// Returns the public key binary data in ASN1 format (DER encoded without the key usage header)
static func generateKeyPairWithPublicKeyAsGenericPassword(privateKeyTag: String, publicKeyAccount: String, publicKeyService: String) throws -> Data {
let tempPublicKeyTag = "TMPPUBLICKEY:\(privateKeyTag)" // we delete this public key and replace it with a generic password, but it needs a tag during the transition
let privateKeyAttr: [NSString: Any] = [
kSecAttrApplicationTag: privateKeyTag.data(using: .utf8)!,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrIsPermanent: true ]
let publicKeyAttr: [NSString: Any] = [
kSecAttrApplicationTag: tempPublicKeyTag.data(using: .utf8)!,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrIsPermanent: true ]
let keyPairAttr: [NSString: Any] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 2048,
kSecPrivateKeyAttrs: privateKeyAttr,
kSecPublicKeyAttrs: publicKeyAttr ]
var publicKey: SecKey?, privateKey: SecKey?
let genKeyPairStatus = SecKeyGeneratePair(keyPairAttr as CFDictionary, &publicKey, &privateKey)
guard genKeyPairStatus == errSecSuccess else {
log.error("Generation of key pair failed. Error = \(genKeyPairStatus)")
throw KeychainError.generateKeyPairFailed(genKeyPairStatus)
}
// Would need CFRelease(publicKey and privateKey) here but swift does it for us
// we store the public key in the keychain as a "generic password" so that it doesn't interfere with retrieving certificates
// The keychain will normally only store the private key and the certificate
// As we want to keep a reference to the public key itself without having to ASN.1 parse it out of the certificate
// we can stick it in the keychain as a "generic password" for convenience
let findPubKeyArgs: [NSString: Any] = [
kSecClass: kSecClassKey,
kSecValueRef: publicKey!,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecReturnData: true ]
var resultRef:AnyObject?
let status = SecItemCopyMatching(findPubKeyArgs as CFDictionary, &resultRef)
guard status == errSecSuccess, let publicKeyData = resultRef as? Data else {
log.error("Public Key not found: \(status))")
throw KeychainError.publicKeyNotFound(status)
}
// now we have the public key data, add it in as a generic password
let attrs: [NSString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrService: publicKeyService,
kSecAttrAccount: publicKeyAccount,
kSecValueData: publicKeyData ]
var result: AnyObject?
let addStatus = SecItemAdd(attrs as CFDictionary, &result)
if addStatus != errSecSuccess {
log.error("Adding public key to keychain failed. Error = \(addStatus)")
throw KeychainError.cannotAddPublicKeyToKeychain(addStatus)
}
// delete the "public key" representation of the public key from the keychain or it interferes with looking up the certificate
let pkattrs: [NSString: Any] = [
kSecClass: kSecClassKey,
kSecValueRef: publicKey! ]
let deleteStatus = SecItemDelete(pkattrs as CFDictionary)
if deleteStatus != errSecSuccess {
log.error("Deletion of public key from keychain failed. Error = \(deleteStatus)")
throw KeychainError.cannotDeletePublicKeyFromKeychain(addStatus)
}
// no need to CFRelease, swift does this.
return publicKeyData
}
NOTE that publicKeyData isn't strictly in DER format, it's in "DER with the first 24 bytes trimmed off" format. I'm not sure what this is called officially, but both microsoft and apple seem to use it as the raw format for public keys. If your server is a microsoft one running .NET (desktop or core) then it will probably be happy with the public key bytes as-is. If it's Java and expects DER you may need to generate the DER header - this is a fixed sequence of 24 bytes you can probably just concatenate on.
Adding the client certificate to the keychain, generating an Identity
static func addIdentity(clientCertificate: Data, label: String) throws {
log.info("Adding client certificate to keychain with label \(label)")
guard let certificateRef = SecCertificateCreateWithData(kCFAllocatorDefault, clientCertificate as CFData) else {
log.error("Could not create certificate, data was not valid DER encoded X509 cert")
throw KeychainError.invalidX509Data
}
// Add the client certificate to the keychain to create the identity
let addArgs: [NSString: Any] = [
kSecClass: kSecClassCertificate,
kSecAttrAccessible: kSecAttrAccessibleAlwaysThisDeviceOnly,
kSecAttrLabel: label,
kSecValueRef: certificateRef,
kSecReturnAttributes: true ]
var resultRef: AnyObject?
let addStatus = SecItemAdd(addArgs as CFDictionary, &resultRef)
guard addStatus == errSecSuccess, let certAttrs = resultRef as? [NSString: Any] else {
log.error("Failed to add certificate to keychain, error: \(addStatus)")
throw KeychainError.cannotAddCertificateToKeychain(addStatus)
}
// Retrieve the client certificate issuer and serial number which will be used to retrieve the identity
let issuer = certAttrs[kSecAttrIssuer] as! Data
let serialNumber = certAttrs[kSecAttrSerialNumber] as! Data
// Retrieve a persistent reference to the identity consisting of the client certificate and the pre-existing private key
let copyArgs: [NSString: Any] = [
kSecClass: kSecClassIdentity,
kSecAttrIssuer: issuer,
kSecAttrSerialNumber: serialNumber,
kSecReturnPersistentRef: true] // we need returnPersistentRef here or the keychain makes a temporary identity that doesn't stick around, even though we don't use the persistentRef
let copyStatus = SecItemCopyMatching(copyArgs as CFDictionary, &resultRef);
guard copyStatus == errSecSuccess, let _ = resultRef as? Data else {
log.error("Identity not found, error: \(copyStatus) - returned attributes were \(certAttrs)")
throw KeychainError.cannotCreateIdentityPersistentRef(addStatus)
}
// no CFRelease(identityRef) due to swift
}
In our code we chose to return a label, and then look up the identity as-required using the label, and the following code. You could also chose to just return the identity ref from the above function rather than the label. Here's our getIdentity function anyway
Getting the identity later on
// Remember any OBJECTIVE-C code that calls this method needs to call CFRetain
static func getIdentity(label: String) -> SecIdentity? {
let copyArgs: [NSString: Any] = [
kSecClass: kSecClassIdentity,
kSecAttrLabel: label,
kSecReturnRef: true ]
var resultRef: AnyObject?
let copyStatus = SecItemCopyMatching(copyArgs as CFDictionary, &resultRef)
guard copyStatus == errSecSuccess else {
log.error("Identity not found, error: \(copyStatus)")
return nil
}
// back when this function was all ObjC we would __bridge_transfer into ARC, but swift can't do that
// It wants to manage CF types on it's own which is fine, except they release when we return them out
// back into ObjC code.
return (resultRef as! SecIdentity)
}
// Remember any OBJECTIVE-C code that calls this method needs to call CFRetain
static func getCertificate(label: String) -> SecCertificate? {
let copyArgs: [NSString: Any] = [
kSecClass: kSecClassCertificate,
kSecAttrLabel: label,
kSecReturnRef: true]
var resultRef: AnyObject?
let copyStatus = SecItemCopyMatching(copyArgs as CFDictionary, &resultRef)
guard copyStatus == errSecSuccess else {
log.error("Identity not found, error: \(copyStatus)")
return nil
}
// back when this function was all ObjC we would __bridge_transfer into ARC, but swift can't do that
// It wants to manage CF types on it's own which is fine, except they release when we return them out
// back into ObjC code.
return (resultRef as! SecCertificate)
}
And finally
Using the identity to authenticate against a server
This bit is in objc because that's how our app happens to work, but you get the idea:
SecIdentityRef _clientIdentity = [XYZ getClientIdentityWithLabel: certLabel];
if(_clientIdentity) {
CFRetain(_clientIdentity);
}
SecCertificateRef _clientCertificate = [XYZ getClientCertificateWithLabel:certLabel];
if(_clientCertificate) {
CFRetain(_clientCertificate);
}
...
- (void)URLSession:(nullable NSURLSession *)session
task:(nullable NSURLSessionTask *)task
didReceiveChallenge:(nullable NSURLAuthenticationChallenge *)challenge
completionHandler:(nullable void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodClientCertificate) {
// supply the appropriate client certificate
id bridgedCert = (__bridge id)_clientCertificate;
NSArray* certificates = bridgedCert ? #[bridgedCert] : #[];
NSURLCredential* credential = [NSURLCredential credentialWithIdentity:identity certificates:certificates persistence:NSURLCredentialPersistenceForSession];
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
}
This code took a lot of time to get right. iOS certificate stuff is exceedingly poorly documented, hopefully this helps.
The usual way to generate SSL certificates is that private key is used to generate the CSR, Certificate Signing Request info. In fact, you're hidding as well company, email, etc info with that key signature. With that CSR, then, you sign your certificate, so it will be associated with your private key and info stored in CSR, nevermind the public key. I'm currently not able to see in IOS CSR Generation project where you can pass your generated key: seems to me that CSR generated with IOS CSR Generation project is using it's own generated key, or no private key at all. That will got then logic with the fact that you cannot extract private key from CER or DER, because it isn't there.

Generate a P12 file Xcode?

I know there is a function called SecPKCS12Import that allows you to import data from a p12 file. However, I want to go the reverse route. I have a SecCertificateRef and a public/private SecKeyRef, which I want to use to create a P12 file. Does anyone know how to do this on iPhone?
Thanks
Unfortunately, there CommonCrypto does not provide any means to export PKCS12 containers let alone any other export functionality (even though its OSX counterpart can do that). There are ways to extract the SecKeyRef raw data from the key chain but then you still need to write all the PKCS12 wrapping yourself.
We were facing a similar issue and went with OpenSSL.
Compiling OpenSSL for iOS
Integrating OpenSSL requires a bit of work as you need to compile and link the OpenSSL sources yourself. Fortunately, there are some build scripts available so you do not have to do that yourself, e.g, https://github.com/x2on/OpenSSL-for-iPhone . I suggest you use them as you need to patch some of the Makefiles which is a bit of a hazel. Those build scripts generate static linked libraries for both iOS and tvOS. You just need to link them against your project and set the Header and Library Search Path accordingly.
CocoaPods
You can also use the official OpenSSL CocoaPod . That saves you the trouble of configuring your project.
Exporting PKCS12
As you might know, OpenSSL is a C library. That means you might want to encapsulate all the C functions into a Objective-C or Swift wrapper. There are some open source wrappers that support im- and exporting PKCS12 containers but I have not found a single one with good documentation. You should be able to derive the relevant snippets from some of the sources though.
https://github.com/microsec/MscX509Common/blob/master/src/MscPKCS12.m
You can have a look at this example as well http://fm4dd.com/openssl/pkcs12test.htm .
Hope that helps!
I agree that this task can only be performed using OpenSSL. It is a bit tricky to compile it for iOS but with OpenSSL-for-iPhone it is quite possible.
To solve the given task of creating a PKCS12 keystore from a SecCertificate and a SecKey with Swift 3 just add the static libraries libssl.aand libcrypto.a to your project and create the following bridging header:
#import <openssl/err.h>
#import <openssl/pem.h>
#import <openssl/pkcs12.h>
#import <openssl/x509.h>
To create the keystore the input data have to be converted to OpenSSL data structures, which requires some creativity. The SecCertificate can be converted directly to DER format and then read into a X509 structure. The SecKey is even worse to handle. The only possible solution to get the data of the key is to write it to the keychain and get the reference. From the reference we can get the base 64 encoded string, which then can be read into a EVP_PKEY structure. Now, we can create the keystore and save it to a file. To access the data in the keystore via iOS functions we must read the file via let data = FileManager.default.contents(atPath: path)! as NSData
The full solution is shown in the following:
func createP12(secCertificate: SecCertificate, secPrivateKey: SecKey) {
// Read certificate
// Convert sec certificate to DER certificate
let derCertificate = SecCertificateCopyData(secCertificate)
// Create strange pointer to read DER certificate with OpenSSL
// data must be a two-dimensional array containing the pointer to the DER certificate as single element at position [0][0]
let certificatePointer = CFDataGetBytePtr(derCertificate)
let certificateLength = CFDataGetLength(derCertificate)
let certificateData = UnsafeMutablePointer<UnsafePointer<UInt8>?>.allocate(capacity: 1)
certificateData.pointee = certificatePointer
// Read DER certificate
let certificate = d2i_X509(nil, certificateData, certificateLength)
// Print certificate
X509_print_fp(stdout, certificate)
// Read private key
// Convert sec key to PEM key
let tempTag = "bundle.temp"
let tempAttributes = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: tempTag,
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecValueRef: secPrivateKey,
kSecReturnData: kCFBooleanTrue
] as NSDictionary
var privateKeyRef: AnyObject?
// Store private key in keychain
SecItemDelete(tempAttributes)
guard SecItemAdd(tempAttributes, &privateKeyRef) == noErr else {
NSLog("Cannot store private key")
return
}
// Get private key data
guard let privateKeyData = privateKeyRef as? Data else {
NSLog("Cannot get private key data")
return
}
let pemPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n\(privateKeyData.base64EncodedString())\n-----END RSA PRIVATE KEY-----\n"
// Delete private key in keychain
SecItemDelete(tempAttributes)
let privateKeyBuffer = BIO_new(BIO_s_mem())
pemPrivateKey.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer<Int8>) -> Void in
BIO_puts(privateKeyBuffer, bytes)
})
let privateKey = PEM_read_bio_PrivateKey(privateKeyBuffer, nil, nil, nil)
// !!! Remove in production: Print private key
PEM_write_PrivateKey(stdout, privateKey, nil, nil, 0, nil, nil)
// Check if private key matches certificate
guard X509_check_private_key(certificate, privateKey) == 1 else {
NSLog("Private key does not match certificate")
return
}
// Set OpenSSL parameters
OPENSSL_add_all_algorithms_noconf()
ERR_load_crypto_strings()
// Create P12 keystore
let passPhrase = UnsafeMutablePointer(mutating: ("" as NSString).utf8String)
let name = UnsafeMutablePointer(mutating: ("SSL Certificate" as NSString).utf8String)
guard let p12 = PKCS12_create(passPhrase, name, privateKey, certificate, nil, 0, 0, 0, 0, 0) else {
NSLog("Cannot create P12 keystore:")
ERR_print_errors_fp(stderr)
return
}
// Save P12 keystore
let fileManager = FileManager.default
let tempDirectory = NSTemporaryDirectory() as NSString
let path = tempDirectory.appendingPathComponent("ssl.p12")
fileManager.createFile(atPath: path, contents: nil, attributes: nil)
guard let fileHandle = FileHandle(forWritingAtPath: path) else {
NSLog("Cannot open file handle: \(path)")
return
}
let p12File = fdopen(fileHandle.fileDescriptor, "w")
i2d_PKCS12_fp(p12File, p12)
fclose(p12File)
fileHandle.closeFile()
}

Seckey from public key string from server in Swift

I want to encrypt data using RSA , I tried to generate the key in my code and it's working , But what I actually need is to get the public key as a string from server and then use it as Seckey so I can use it to encrypt data using RSA,
I tried this code:
//KeyString is the string of the key from server
let KeyData = (keyString as NSString).dataUsingEncoding(NSUTF8StringEncoding) as NSData!
var cert : Unmanaged<SecCertificateRef>!;
var policy : Unmanaged<SecPolicy>!;
cert = SecCertificateCreateWithData(kCFAllocatorDefault, KeyData);
policy = SecPolicyCreateBasicX509();
var status : OSStatus = noErr
var trust: SecTrust?
var certArray : [Unmanaged<SecCertificateRef>!] = [cert];
var certArrayPointer = UnsafeMutablePointer<UnsafePointer<Void>>(certArray)
status = SecTrustCreateWithCertificates(cert, policy, trust);
let publicKey: SecKeyRef = SecTrustCopyPublicKey(trust!).takeUnretainedValue()
I couldn't run this code because SecTrustCreateWithCertificates Method is expecting certificate as anyObject! , I don't Know how to fix this,And if solving this will let me get the SecKey.
I got the code above from this answer in objective-c
So if any one can help me getting the right code to solve this , I will be very thankful :)
For mac:
let pubKey = "-----BEGIN PUBLIC KEY-----MIICIjANBgAgK.......InbFk1FkucQqruMyUCAwEAAQ==-----END PUBLIC KEY-----"
let pubKeyData = pubKey.dataUsingEncoding(NSASCIIStringEncoding)
var error: Unmanaged<CFErrorRef>?
let secKey = SecKeyCreateFromData(NSDictionary(), pubKeyData!, &error)
Where pubKey is a string representation of your public key.
If you don't know your public key, you can infer it from your private key with the following command:
openssl rsa -in server.key -pubout > mykey.pub
Where server.key is the file containing -----BEGIN RSA PRIVATE KEY-----
as the first line.
For iOS:
It's a bit more complicate.
You need a der file. It's a binary representation of your certificate.
If you need to convert an existing certificate, you can do so with the following command:
openssl x509 -outform der -in file.crt|pem -out mycert.der
The .crt or .pem file contains -----BEGIN CERTIFICATE----- as the first line.
Put the der file in your bundle and do:
let certificateData = NSData(contentsOfURL:NSBundle.mainBundle().URLForResource("mycert", withExtension: "der")!)
let certificate = SecCertificateCreateWithData(nil, certificateData!)
var trust: SecTrustRef?
let policy = SecPolicyCreateBasicX509()
let status = SecTrustCreateWithCertificates(certificate!, policy, &trust)
if status == errSecSuccess {
let key = SecTrustCopyPublicKey(trust!)!;
}
Yatta ! Key now contains a SecKey representation of your public key. Happy Pinning.
Here's how I did this:
let cert = SecCertificateCreateWithData(kCFAllocatorDefault, certData)?.takeRetainedValue()
if cert != nil {
var trust: Unmanaged<SecTrust>?
let policy = SecPolicyCreateBasicX509().takeRetainedValue()
let status = SecTrustCreateWithCertificates(cert, policy, &trust)
if status == errSecSuccess {
let trustRef = trust!.takeRetainedValue()
let key = SecTrustCopyPublicKey(trustRef)!.takeRetainedValue();
}
}
This works, but you need to make sure that what you pass to SecCertificateCreateWithData() is a DER-encoded certificate, and not just a DER-encoded key. You need a certificate signed by your server's private key to the get the associated public key.
I Did this used Alamofire:
private static func publicKeyForCertificate(certificate: SecCertificate) -> SecKey? {
var publicKey: SecKey?
var trust: Unmanaged<SecTrust>?
let policy = SecPolicyCreateBasicX509().takeRetainedValue()
let status = SecTrustCreateWithCertificates(certificate, policy, &trust)
if status == errSecSuccess {
let trustRef = trust!.takeRetainedValue()
publicKey = SecTrustCopyPublicKey(trustRef)!.takeRetainedValue()
}
return publicKey
}

Resources