Skip to content

Instantly share code, notes, and snippets.

@mittsh
Last active August 19, 2020 10:04
Show Gist options
  • Save mittsh/09b6980be353166708965d231ccbc58e to your computer and use it in GitHub Desktop.
Save mittsh/09b6980be353166708965d231ccbc58e to your computer and use it in GitHub Desktop.
SecureIdentity.swift
void LMOpenSSL_Init(void)
{
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
ERR_load_crypto_strings();
}
void LMOpenSSL_ClearError(void)
{
ERR_clear_error();
}
dispatch_queue_t LMOpenSSL_GetQueue()
{
static dispatch_once_t onceToken;
static dispatch_queue_t openSSLQueue;
dispatch_once(&onceToken, ^{
openSSLQueue = dispatch_queue_create("com.luckymarmot.Paw.OpenSSLQueue", DISPATCH_QUEUE_SERIAL);
});
return openSSLQueue;
}
#pragma mark - Create BIO
BIO* LMOpenSSL_CreateBIOWithData(CFDataRef data)
{
if (data == NULL) {
return NULL;
}
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, CFDataGetBytePtr(data), (int)CFDataGetLength(data));
return bio;
}
#pragma mark - Create or Write BIO
CFDataRef LMOpenSSL_CreateDataFromBIO(BIO* bio)
{
size_t len = BIO_pending(bio);
if (len == 0) {
return CFDataCreate(kCFAllocatorDefault, NULL, 0);
}
// get writing buffer
UInt8* buf = malloc(sizeof(UInt8) * len);
// copy to writing buffer
BIO_read(bio, buf, (int)len);
// creata data
CFDataRef data = CFDataCreate(kCFAllocatorDefault, buf, len);
// return
free(buf);
return data;
}
void LMOpenSSL_AppendBIO(BIO* bio, CFMutableDataRef mutableData)
{
size_t len = BIO_pending(bio);
if (len > 0) {
// get current length and increase by `len`
CFIndex idx = CFDataGetLength(mutableData);
CFDataIncreaseLength(mutableData, len);
// get writing buffer
UInt8* buf = CFDataGetMutableBytePtr(mutableData) + idx;
// copy to writing buffer
BIO_read(bio, buf, (int)len);
}
}
#pragma mark - Read PEM
bool LMOpenSSL_PEM_ReadCertificates(CFDataRef inputPemData, X509** __cert, STACK_OF(X509)** __certStack)
{
// get BIO (bytes IO) buffer
BIO* dataBio = LMOpenSSL_CreateBIOWithData(inputPemData);
if (dataBio == NULL) {
return false;
}
// parse X509 certificates
X509* x;
X509* cert = NULL;
STACK_OF(X509)* certStack = sk_X509_new_null();
while (NULL != (x = PEM_read_bio_X509(dataBio, NULL, NULL, NULL))) {
if (cert == NULL) {
// certificate (main)
cert = x;
}
else {
// certificate chain (CA)
sk_X509_push(certStack, x);
}
}
if (__cert != NULL) {
*__cert = cert;
}
if (__certStack != NULL) {
*__certStack = certStack;
}
BIO_free(dataBio);
return (cert != NULL);
}
bool LMOpenSSL_PEM_ReadPrivateKey(CFDataRef inputPemData, EVP_PKEY** __pkey, CFStringRef password, LMOpenSSL_Error * __error)
{
// get password data
char passwordBuf[2048];
if (password != NULL) {
CFStringGetCString(password, passwordBuf, 2048, kCFStringEncodingUTF8);
}
// get BIO (bytes IO) buffer
BIO* dataBio = LMOpenSSL_CreateBIOWithData(inputPemData);
if (dataBio == NULL) {
if (__error != NULL) {
*__error = LMOpenSSL_UnknownError;
}
return false;
}
// parse Private Key
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(dataBio, NULL, NULL, (password != NULL ? passwordBuf : "" /* pass empty string to prevent user prompt */));
if (pkey == NULL) {
if (__error != NULL) {
unsigned long error = ERR_get_error();
// printf("ERROR: lib = %d, reason = %d\n", ERR_GET_LIB(error), ERR_GET_REASON(error));
if (ERR_R_PEM_LIB == ERR_GET_LIB(error) && (
PEM_R_BAD_DECRYPT == ERR_GET_REASON(error) ||
PEM_R_NO_START_LINE == ERR_GET_REASON(error))) {
*__error = LMOpenSSL_InvalidPassword;
}
else {
*__error = LMOpenSSL_UnknownError;
}
}
BIO_free(dataBio);
return false;
}
if (__pkey != NULL) {
*__pkey = pkey;
}
if (__error != NULL) {
*__error = LMOpenSSL_Success;
}
BIO_free(dataBio);
return true;
}
bool LMOpenSSL_PEM_Read(CFDataRef inputPemData, X509** __cert, STACK_OF(X509)** __certStack, EVP_PKEY** __pkey, CFStringRef inputPassword, LMOpenSSL_Error * __error)
{
// read X509 certificate(s)
X509* cert;
STACK_OF(X509)* certStack;
if (!LMOpenSSL_PEM_ReadCertificates(inputPemData, &cert, &certStack)) {
if (__error != NULL) {
*__error = LMOpenSSL_MissingCertificateError;
}
return false;
}
// read private key
EVP_PKEY* pkey;
LMOpenSSL_Error pkeyError;
if (!LMOpenSSL_PEM_ReadPrivateKey(inputPemData, &pkey, inputPassword, &pkeyError)) {
if (__error != NULL) {
*__error = pkeyError;
}
X509_free(cert);
sk_X509_free(certStack);
return false;
}
// make sure the certificate matches the private key
if (1 != X509_check_private_key(cert, pkey)) {
if (__error != NULL) {
*__error = LMOpenSSL_CertificateDoesNotMatchPrivateKeyError;
}
X509_free(cert);
sk_X509_free(certStack);
EVP_PKEY_free(pkey);
return false;
}
// set return values
if (__cert != NULL) {
*__cert = cert;
}
if (__certStack != NULL) {
*__certStack = certStack;
}
if (__pkey != NULL) {
*__pkey = pkey;
}
if (__error != NULL) {
*__error = LMOpenSSL_Success;
}
return true;
}
#pragma mark - Write PEM
bool LMOpenSSL_PEM_AppendCertificate(X509* cert, CFMutableDataRef mutableData)
{
BIO* bio = BIO_new(BIO_s_mem());
if (1 != PEM_write_bio_X509(bio, cert)) {
BIO_free(bio);
return false;
}
LMOpenSSL_AppendBIO(bio, mutableData);
BIO_free(bio);
return true;
}
bool LMOpenSSL_PEM_AppendCertificateStack(STACK_OF(X509)* certStack, CFMutableDataRef mutableData)
{
if (certStack == NULL || sk_X509_num(certStack) == 0) {
return true;
}
for (int k = 0; k < sk_X509_num(certStack); k++) {
X509* cert = sk_X509_value(certStack, k);
if (!LMOpenSSL_PEM_AppendCertificate(cert, mutableData)) {
return false;
}
}
return true;
}
bool LMOpenSSL_PEM_AppendPrivateKey(EVP_PKEY* pkey, CFMutableDataRef mutableData)
{
BIO* bio = BIO_new(BIO_s_mem());
if (1 != PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) {
BIO_free(bio);
return false;
}
LMOpenSSL_AppendBIO(bio, mutableData);
BIO_free(bio);
return true;
}
CFDataRef LMOpenSSL_PEM_CreateFullChain(X509* cert, STACK_OF(X509)* certStack, EVP_PKEY* pkey)
{
CFMutableDataRef mutableData = CFDataCreateMutable(kCFAllocatorDefault, 0);
// output certificate
if (cert != NULL) {
if (!LMOpenSSL_PEM_AppendCertificate(cert, mutableData)) {
CFRelease(mutableData);
return NULL;
}
}
// output certificate chain (other certificates)
if (certStack != NULL) {
if (!LMOpenSSL_PEM_AppendCertificateStack(certStack, mutableData)) {
CFRelease(mutableData);
return NULL;
}
}
// output PEM private key
if (pkey != NULL) {
if (!LMOpenSSL_PEM_AppendPrivateKey(pkey, mutableData)) {
CFRelease(mutableData);
return NULL;
}
}
// copy immutable
CFDataRef pemData = CFDataCreateCopy(kCFAllocatorDefault, mutableData);
// free
CFRelease(mutableData);
return pemData;
}
#pragma mark - Convert to PEM
CFDataRef LMCreatePEMDataFromPEM(CFDataRef inputPemData, CFStringRef inputPassword, LMOpenSSL_Error* __error)
{
// read PEM
X509* cert;
STACK_OF(X509)* certStack;
EVP_PKEY* pkey;
if (!LMOpenSSL_PEM_Read(inputPemData, &cert, &certStack, &pkey, inputPassword, __error)) {
return NULL;
}
// output PEM
CFDataRef pemData = LMOpenSSL_PEM_CreateFullChain(cert, certStack, pkey);
if (__error != NULL) {
*__error = LMOpenSSL_Success;
}
// free
X509_free(cert);
sk_X509_free(certStack);
EVP_PKEY_free(pkey);
return pemData;
}
CFDataRef LMCreatePEMDataFromPKCS12Archive(CFDataRef pkcs12Data, CFStringRef password, LMOpenSSL_Error * __error)
{
// get password data
char passwordBuf[2048];
if (password != NULL) {
CFStringGetCString(password, passwordBuf, 2048, kCFStringEncodingUTF8);
}
// get BIO (bytes IO) buffer
BIO* dataBio = LMOpenSSL_CreateBIOWithData(pkcs12Data);
if (dataBio == NULL) {
if (__error != NULL) {
*__error = LMOpenSSL_UnknownError;
}
return NULL;
}
// get PKCS12 object
PKCS12* p12 = d2i_PKCS12_bio(dataBio, NULL);
if (p12 == NULL) {
if (__error != NULL) {
*__error = LMOpenSSL_InvalidPKCS12Archive;
}
BIO_free(dataBio);
return NULL;
}
// parse PKCS12 object
EVP_PKEY* pkey;
X509* cert;
STACK_OF(X509)* certStack = NULL;
if (PKCS12_OK != PKCS12_parse(p12, (password == NULL ? NULL : passwordBuf), &pkey, &cert, &certStack /* additional certificates (chain) */)) {
if (__error != NULL) {
unsigned long error = ERR_get_error();
if (ERR_LIB_PKCS12 == ERR_GET_LIB(error) &&
PKCS12_R_MAC_VERIFY_FAILURE == ERR_GET_REASON(error)) {
*__error = LMOpenSSL_InvalidPassword;
}
else {
*__error = LMOpenSSL_InvalidPKCS12Archive;
}
}
PKCS12_free(p12);
BIO_free(dataBio);
return NULL;
}
// output PEM
CFDataRef pemData = LMOpenSSL_PEM_CreateFullChain(cert, certStack, pkey);
if (__error != NULL) {
*__error = LMOpenSSL_Success;
}
// free
PKCS12_free(p12);
BIO_free(dataBio);
sk_X509_free(certStack);
EVP_PKEY_free(pkey);
return pemData;
}
#pragma mark - Convert to PKCS12
CFDataRef LMCreatePKCS12ArchiveDataFromPEM(CFDataRef inputPemData, CFStringRef inputPassword, CFStringRef outputPassword, LMOpenSSL_Error * __error)
{
// read PEM
X509* cert;
STACK_OF(X509)* certStack;
EVP_PKEY* pkey;
if (!LMOpenSSL_PEM_Read(inputPemData, &cert, &certStack, &pkey, inputPassword, __error)) {
return NULL;
}
// get ouput password data
char outputPasswordBuf[2048];
CFStringGetCString(outputPassword, outputPasswordBuf, 2048, kCFStringEncodingUTF8);
// create PKCS12 object
PKCS12* p12 = PKCS12_create(outputPasswordBuf, "", pkey, cert, certStack, 0, 0, 0, 0, 0);
if (p12 == NULL) {
if (__error != NULL) {
*__error = LMOpenSSL_UnknownError;
}
PKCS12_free(p12);
sk_X509_free(certStack);
EVP_PKEY_free(pkey);
return NULL;
}
// get data from PKCS12 object
BIO* bio = BIO_new(BIO_s_mem());
i2d_PKCS12_bio(bio, p12);
CFDataRef p12Data = LMOpenSSL_CreateDataFromBIO(bio);
// return
BIO_free(bio);
PKCS12_free(p12);
sk_X509_free(certStack);
EVP_PKEY_free(pkey);
return p12Data;
}
#pragma mark - Extract data from PEM
CFStringRef LMCreateSubjectNameStringWithPEMData(CFDataRef pemData)
{
// if NULL or empty, return NULL
if (pemData == NULL || CFDataGetLength(pemData) == 0) {
return NULL;
}
// get X509
BIO* certBio = LMOpenSSL_CreateBIOWithData(pemData);
X509* certX509 = PEM_read_bio_X509(certBio, NULL, NULL, NULL);
if (certX509 == NULL) {
BIO_free(certBio);
return NULL;
}
// get subject
X509_NAME* subjectName = X509_get_subject_name(certX509);
if (subjectName == NULL) {
BIO_free(certBio);
return NULL;
}
// get subject as string
char subjectCString[2048];
X509_NAME_oneline(subjectName, subjectCString, 2048);
CFStringRef subjectString = CFStringCreateWithCString(kCFAllocatorDefault, subjectCString, kCFStringEncodingUTF8);
BIO_free(certBio);
X509_free(certX509);
return subjectString;
}
CFDataRef LMCreateDigestDataWithPEMData(CFDataRef pemData, CFStringRef digestName)
{
// if NULL or empty, return NULL
if (pemData == NULL || CFDataGetLength(pemData) == 0) {
return NULL;
}
// get X509
BIO* certBio = LMOpenSSL_CreateBIOWithData(pemData);
X509* certX509 = PEM_read_bio_X509(certBio, NULL, NULL, NULL);
if (certX509 == NULL) {
BIO_free(certBio);
return NULL;
}
// get digest type
char digestNameBuf[2048];
CFStringGetCString(digestName, digestNameBuf, 2048, kCFStringEncodingUTF8);
const EVP_MD* digestType = EVP_get_digestbyname(digestNameBuf);
if (digestType == NULL) {
BIO_free(certBio);
X509_free(certX509);
return NULL;
}
// get digest buffer
unsigned char buf[EVP_MAX_MD_SIZE];
unsigned int len;
if (0 == X509_digest(certX509, digestType, buf, &len)) {
BIO_free(certBio);
X509_free(certX509);
return NULL;
}
// get CFData and return
CFDataRef digestData = CFDataCreate(kCFAllocatorDefault, buf, len);
BIO_free(certBio);
X509_free(certX509);
return digestData;
}
OSStatus LMSecCertificateQueryByName(CFStringRef certificateName, SecCertificateRef* __certificate)
{
const void* keys[] = { kSecClass, kSecAttrLabel, kSecReturnRef };
const void* values[] = { kSecClassCertificate, certificateName, kCFBooleanTrue };
CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 3, NULL, NULL);
CFTypeRef certificate = NULL;
OSStatus err = SecItemCopyMatching(query, &certificate);
if (noErr != err) {
CFRelease(query);
return err;
}
if (__certificate != NULL) {
*__certificate = (SecCertificateRef)certificate;
}
CFRelease(query);
return noErr;
}
OSStatus LMSecIdentityCreateWithCertificate(SecCertificateRef certificate, SecIdentityRef* __identity)
{
OSStatus err = noErr;
SecIdentityRef identity = NULL;
err = SecIdentityCreateWithCertificate(NULL /* search in all Keychains */, certificate, &identity);
if (noErr != err) {
return err;
}
if (__identity != NULL) {
*__identity = identity;
}
return noErr;
}
CFStringRef LMCreateSecKeychainDefaultPath()
{
CFStringRef appSupportPath = LMCreateApplicationSupportDirectoryPathString();
CFStringRef keychainPath = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@/Paw.keychain"), appSupportPath);
CFRelease(appSupportPath);
return keychainPath;
}
OSStatus LMSecKeychainGetPawDefault(SecKeychainRef * __pawDefaultKeychain)
{
OSStatus status = noErr;
// get path to keychain
CFStringRef keychainPath = LMCreateSecKeychainDefaultPath();
char keychainPathBuf[10240];
CFStringGetCString(keychainPath, keychainPathBuf, 10240, kCFStringEncodingUTF8);
CFRelease(keychainPath);
// open keychain
SecKeychainRef keychain;
if (noErr != (status = SecKeychainOpen(keychainPathBuf, &keychain))) {
printf("ERROR: cannot open keychain = %d\n", status);
return status;
}
// get status and create if needed
SecKeychainStatus keychainStatus;
if (noErr != (status = SecKeychainGetStatus(keychain, &keychainStatus))) {
printf("ERROR: cannot get keychain status = %d\n", status);
if (noErr != (status = SecKeychainCreate(keychainPathBuf, 3, "paw", false, NULL, &keychain))) {
printf("ERROR: cannot create new keychain = %d\n", status);
if (noErr != (status = SecKeychainGetStatus(keychain, &keychainStatus))) {
printf("ERROR: cannot get (new) keychain status = %d\n", status);
return status;
}
}
}
// unlock if needed
if (0 == (keychainStatus & kSecUnlockStateStatus)) {
SecKeychainUnlock(keychain, 3, "paw", true);
}
// assign return value
if (NULL != __pawDefaultKeychain) {
*__pawDefaultKeychain = keychain;
}
return noErr;
}
OSStatus LMSecIdentityFromP12(CFDataRef p12Data, CFStringRef p12Passphrase, SecIdentityRef* __identity, CFArrayRef* __certChain)
{
OSStatus status = noErr;
// get Paw default keychain
SecKeychainRef pawDefaultKeychain;
if (noErr != (status = LMSecKeychainGetPawDefault(&pawDefaultKeychain))) {
printf("ERROR: cannot get Paw default keychain, status = %d\n", status);
return status;
}
// options
const void* keys[] = { kSecImportExportPassphrase, kSecImportExportKeychain };
const void* values[] = { p12Passphrase, pawDefaultKeychain };
CFDictionaryRef options = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
// import p12
CFArrayRef items;
if (errSecSuccess != (status = SecPKCS12Import(p12Data, options, &items)) || CFArrayGetCount(items) == 0) {
printf("ERROR: could not import items from P12 file, status = %d\n", status);
CFRelease(options);
CFRelease(pawDefaultKeychain);
return status;
}
// get identity
CFDictionaryRef itemDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identity = (SecIdentityRef)CFDictionaryGetValue(itemDict, kSecImportItemIdentity);
if (__identity != NULL) {
CFRetain(identity);
*__identity = identity;
}
CFArrayRef certChain = (CFArrayRef)CFDictionaryGetValue(itemDict, kSecImportItemCertChain);
if (__certChain != NULL) {
CFRetain(certChain);
*__certChain = certChain;
}
CFRelease(options);
CFRelease(pawDefaultKeychain);
return noErr;
}
OSStatus LMSecCertificateQueryByName(CFStringRef certificateName, SecCertificateRef* __certificate)
{
const void* keys[] = { kSecClass, kSecAttrLabel, kSecReturnRef };
const void* values[] = { kSecClassCertificate, certificateName, kCFBooleanTrue };
CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 3, NULL, NULL);
CFTypeRef certificate = NULL;
OSStatus err = SecItemCopyMatching(query, &certificate);
if (noErr != err) {
CFRelease(query);
return err;
}
if (__certificate != NULL) {
*__certificate = (SecCertificateRef)certificate;
}
CFRelease(query);
return noErr;
}
OSStatus LMSecIdentityCreateWithCertificate(SecCertificateRef certificate, SecIdentityRef* __identity)
{
OSStatus err = noErr;
SecIdentityRef identity = NULL;
err = SecIdentityCreateWithCertificate(NULL /* search in all Keychains */, certificate, &identity);
if (noErr != err) {
return err;
}
if (__identity != NULL) {
*__identity = identity;
}
return noErr;
}
OSStatus LMSecIdentityGetAll(CFArrayRef * __identities)
{
// build query
const void* keys[] = { kSecClass, kSecMatchLimit, kSecReturnRef /* do not specify kSecMatchSearchList to search in all Keychains */ };
const void* values[] = { kSecClassIdentity, kSecMatchLimitAll, kCFBooleanTrue };
CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 3, NULL, NULL);
// get all SecIdentity items
CFArrayRef identities = NULL;
OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&identities);
if (status != noErr) {
printf("ERROR: cannot list identities Keychain (err=%d)", status);
CFRelease(query);
return status;
}
// set result
if (__identities != NULL) {
*__identities = identities;
}
CFRelease(query);
return noErr;
}
@objc(LMSecureIdentity)
protocol SecureIdentity {
@objc var identity: SecIdentity { get }
@objc var urlCredential: URLCredential? { get }
@objc var objc_mainCertificate: SecCertificate? { get }
@objc var objc_commonName: String? { get }
@objc var objc_certificateChain: CFArray { get }
}
@objc(LMKeychainSecureIdentity)
class KeychainSecureIdentity : NSObject, SecureIdentity {
@objc private(set) public var identity: SecIdentity
@objc private(set) public var keychainCertificateName: String
@objc private(set) public var certificate: SecCertificate
@objc public init(keychainCertificateName: String) throws {
self.keychainCertificateName = keychainCertificateName
certificate = try KeychainSecureIdentity.getCertificate(keychainCertificateName: keychainCertificateName)
identity = try KeychainSecureIdentity.getIdentity(certificate: certificate)
super.init()
}
var certificateChain: [SecCertificate] {
return [certificate]
}
var urlCredential: URLCredential? {
return URLCredential(identity: identity, certificates: certificateChain, persistence: .none)
}
// MARK: Static
static public func getCertificate(keychainCertificateName: String) throws -> SecCertificate {
var certificate: SecCertificate?
guard noErr == LMSecCertificateQueryByName(keychainCertificateName as NSString, &certificate) else {
throw SecureIdentityError.keychainCertificateNotFound
}
return certificate!
}
static public func getIdentity(certificate: SecCertificate) throws -> SecIdentity {
var identity: SecIdentity?
guard noErr == LMSecIdentityCreateWithCertificate(certificate, &identity) else {
throw SecureIdentityError.keychainMissingPrivateKey
}
return identity!
}
}
@objc(LMPEMSecureIdentity)
class PEMSecureIdentity : NSObject, SecureIdentity {
@objc private(set) public var pem: Data
@objc private(set) public var identity: SecIdentity
private(set) public var certificateChain: [SecCertificate]
@objc public init(pem: Data, password: String? = nil) throws {
var openSSLError: LMOpenSSL_Error = LMOpenSSL_Success
var _pemData: Data?
LMOpenSSL_GetQueue().sync {
_pemData = LMCreatePEMDataFromPEM(pem as NSData, password as NSString?, &openSSLError)?.takeRetainedValue() as Data?
LMOpenSSL_ClearError()
}
guard let pemData = _pemData else {
switch openSSLError {
case LMOpenSSL_InvalidPassword:
if password != nil && !password!.isEmpty {
throw SecureIdentityError.pemPasswordInvalid
} else {
throw SecureIdentityError.pemPasswordRequired
}
case LMOpenSSL_MissingCertificateError:
throw SecureIdentityError.pemMissingCertificateError
case LMOpenSSL_CertificateDoesNotMatchPrivateKeyError:
throw SecureIdentityError.pemCertificateDoesNotMatchPrivateKeyError
default:
throw SecureIdentityError.pemArchiveParsingError
}
}
self.pem = pemData
(identity, certificateChain) = try PEMSecureIdentity.getIdentity(pem: pemData)
super.init()
}
@objc public init(pkcs12Archive: Data, password: String? = nil) throws {
var openSSLError: LMOpenSSL_Error = LMOpenSSL_Success
var _pemData: Data?
LMOpenSSL_GetQueue().sync {
_pemData = LMCreatePEMDataFromPKCS12Archive(pkcs12Archive as NSData, password as NSString?, &openSSLError)?.takeRetainedValue() as Data?
LMOpenSSL_ClearError()
}
guard let pemData = _pemData else {
switch openSSLError {
case LMOpenSSL_InvalidPassword:
if password != nil && !password!.isEmpty {
throw SecureIdentityError.pkcs12ArchivePasswordInvalid
} else {
throw SecureIdentityError.pkcs12ArchivePasswordRequired
}
default:
throw SecureIdentityError.pkcs12ArchiveParsingError
}
}
self.pem = pemData
(identity, certificateChain) = try PEMSecureIdentity.getIdentity(pem: pemData)
super.init()
}
var urlCredential: URLCredential? {
return URLCredential(identity: identity, certificates: certificateChain, persistence: .none)
}
@objc public func getFingerprint(algorithm: SecureIdentityFingerprintAlgorithm) -> String? {
var _fingerprintData: Data?
LMOpenSSL_GetQueue().sync {
_fingerprintData = LMCreateDigestDataWithPEMData(pem as NSData, algorithm.openSSLName as NSString)?.takeRetainedValue() as Data?
LMOpenSSL_ClearError()
}
guard let fingerprintData = _fingerprintData else {
return nil
}
return (fingerprintData as NSData).fingerprintHexString
}
// MARK: Static
static func pkcs12Data(pem: Data, outputPassword: String) throws -> Data {
var _pkcs12Data: Data?
LMOpenSSL_GetQueue().sync {
_pkcs12Data = LMCreatePKCS12ArchiveDataFromPEM(pem as NSData, nil, outputPassword as NSString, nil)?.takeRetainedValue() as Data?
LMOpenSSL_ClearError()
}
guard let pkcs12Data = _pkcs12Data else {
throw SecureIdentityError.pemUnknownError
}
return pkcs12Data
}
static func getIdentity(pem: Data) throws -> (SecIdentity, [SecCertificate]) {
return try self.getIdentity(pkcs12Data: self.pkcs12Data(pem: pem, outputPassword: "paw"), password: "paw")
}
static func getIdentity(pkcs12Data: Data, password: String) throws -> (SecIdentity, [SecCertificate]) {
var identity: SecIdentity?
var certChain: CFArray?
let err = LMSecIdentityFromP12(pkcs12Data as NSData, password as NSString, &identity, &certChain)
guard noErr == err else {
if err == errSecPassphraseRequired {
throw SecureIdentityError.pkcs12ArchivePasswordRequired
}
else if err == errSecInvalidPasswordRef {
throw SecureIdentityError.pkcs12ArchivePasswordInvalid
}
throw SecureIdentityError.pkcs12ArchiveParsingError
}
return (identity!, certChain as! [SecCertificate])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment