I'm using native RSA in one iOS app and Chilkat RSA library in another. On a native iOS side I encrypt (OAEP SHA256) data with the following function:
static func encryptWithKey(_ data: Data, rsaKey: SecKey) -> Data? {
let algorithm = SecKeyAlgorithm.rsaEncryptionOAEPSHA256
guard SecKeyIsAlgorithmSupported(rsaKey, .encrypt, algorithm) else { return nil }
var error: Unmanaged<CFError>?
let encryptedData = SecKeyCreateEncryptedData(rsaKey, algorithm, data as CFData, &error)
if let encryptionError = error {
print(encryptionError.takeRetainedValue())
}
return encryptedData as Data?
}
and then in another app, I decrypt this data with Chilkat library:
static func decrypt(base64 encryptedText: String, with xmlKey: String) -> Data? {
let privateKey = CkoPrivateKey()
guard let success = privateKey?.loadXml(xmlKey), success else { return nil }
guard let rsa = CkoRsa() else { return nil }
rsa.unlockComponent(Chilkat.key)
rsa.oaepPadding = true
rsa.littleEndian = false
rsa.oaepHash = "sha256"
rsa.encodingMode = "base64"
rsa.importPrivateKeyObj(privateKey)
if let decryptedData = rsa.decryptBytesENC(encryptedText, bUsePrivateKey: true) {
return decryptedData
} else {
print(rsa.lastErrorText)
}
return nil
}
Even I used one private / public keys pair I'm getting an error (from rsa.lastErrorText):
"ChilkatLog:
DecryptBytesENC(7ms):
DllDate: Feb 1 2018
ChilkatVersion: 9.5.0.72
UnlockPrefix: XXXXXXXXXXXX
Architecture: Little Endian; 64-bit
Language: IOS C/C++/Swift/Objective-C
VerboseLogging: 1
usePrivateKey: 1
Component successfully unlocked using purchased unlock code.
rsaDecryptBytes(7ms):
rsa_decrypt(7ms):
KeyType: Private
InputSize: 256
Padding: OAEP
OaepHashAlg: SHA-256
MgfHashAlg: SHA-1
ParamLen: 0
ModulusBitLen: 2048
inlen: 256
modulus_bytelen: 256
modulus_bitlen: 2048
bigEndian: 0
Byte swapping from big-endian to little-endian
padding: OAEP
No leading zero byte for OAEP decoding.
OAEP decoding failed.
--rsa_decrypt
--rsaDecryptBytes
Failed.
--DecryptBytesENC
--ChilkatLog"
Any ideas?
I added some extra logging so we can see the bytes (in hex) after the RSA decrypt but before OAEP unpadding. After RSA decrypt (but before OAEP unpadding), we should see bytes in this format:
0x00 || maskedseed || maskedDB
In other words, the 1st byte of the result SHOULD be a 0 byte. If the RSA private key used to decrypt did not correspond to the public key used to encrypt, then we would see random bytes. (i.e. the 1st byte would not be 0). The maskedseed and maskedDB will still appear as random bytes.
The problem was in OAEP Mgf hash function. If you set oaepMgfHash = "sha256" it becomes compatible with native iOS RSA implementation. In addition, Chilkat library provides an automatic fallback to a proper oaepMgfHash function in case you've specified it incorrectly. Thanks, Matt!
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?
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.
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()
}
Dose somebody had a luck converting p12 crt to DER? What I am doing wrong?
Convert p12 to DER I am using i2d_PKCS12_bio. To get ref to certificate I am using SecCertificateCreateWithData() method which returns nil. This method is taking only DER data as written in the Apple doc.
In OpenSSL documentations is written:
i2d is the standard ASN1 function that converts the internal
structure into the DER representation: for example
i2d_ASN1_IA5STRING().
PKCS12 *p12; // p12 was created ok.
char *data_p12 = NULL;
BIO *mem = BIO_new(BIO_s_mem());
i2d_PKCS12_bio(mem, p12); // In the documentation is written that data should be converted to DER.
int _flush __attribute__((unused)) = BIO_flush(mem);
long size = BIO_get_mem_data(mem, &data_p12);
NSData *retData = [[NSData alloc] initWithBytes:data_p12 length:size];
CFDataRef dataRef = CFDataCreate(nil, retData.bytes, retData.length);
SecCertificateRef secRef = SecCertificateCreateWithData(nil, dataRef);
// secRef is nil. FAIL!
PS: I did save p12 data as file and it work as p12 file.