iOS: Как создать хранилище ключей PKCS12 (P12) из закрытого ключа и x509certificate в приложении программно?
этот вопрос был, по-видимому, похож, но не имел никаких ответов:программно создайте сертификат x509 для iPhone без использования OpenSSL
в нашем приложении (сервер, клиент) мы реализуем аутентификацию клиента (SSL на основе X509Certificate). У нас уже есть способ создания keypair
создать PKCS10 Certificate Signing Request
, подпишите это self-signed CA
и создать X509Certificate
отправить это обратно. Однако для использования этого сертификата в SSL-запросах private key
и X509Certificate
должны быть экспортированы в PKCS12
(P12) keystore
.
кто-нибудь знает что-нибудь о том, как это сделать, или даже если это возможно? Клиент и создать файл P12 (мы не хотим выдавать закрытый ключ), а клиент под iOS и мобильные устройства. Решение работало для Android с помощью BouncyCastle (губка), но мы ничего не нашли для iOS.
EDIT: в Java этот экспорт выполняется следующий:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
KeyStore ks = KeyStore.getInstance("PKCS12", BouncyCastleProvider.PROVIDER_NAME);
ks.load(null);
ks.setKeyEntry("key-alias", (Key) key, password.toCharArray(),
new java.security.cert.Certificate[] { x509Certificate });
ks.store(bos, password.toCharArray());
bos.close();
return bos.toByteArray();
4 ответов
Если вы используете openssl, вам не нужно копировать полный исходный код в свой проект, достаточно добавить библиотеки и заголовки, поэтому библиотеку openssl можно использовать без каких-либо проблем с размером. Вы можете создать ключ и сертификат, как это с openssl:
EVP_PKEY * pkey;
pkey = EVP_PKEY_new();
RSA * rsa;
rsa = RSA_generate_key(
2048, /* number of bits for the key - 2048 is a sensible value */
RSA_F4, /* exponent - RSA_F4 is defined as 0x10001L */
NULL, /* callback - can be NULL if we aren't displaying progress */
NULL /* callback argument - not needed in this case */
);
EVP_PKEY_assign_RSA(pkey, rsa);
X509 * x509;
x509 = X509_new();
ASN1_INTEGER_set(X509_get_serialNumber(x509), 1);
X509_gmtime_adj(X509_get_notBefore(x509), 0);
X509_gmtime_adj(X509_get_notAfter(x509), 31536000L);
X509_set_pubkey(x509, pkey);
X509_NAME * name;
name = X509_get_subject_name(x509);
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
(unsigned char *)"CA", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
(unsigned char *)"MyCompany Inc.", -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(unsigned char *)"localhost", -1, -1, 0);
X509_set_issuer_name(x509, name);
//X509_sign(x509, pkey, EVP_sha1());
const EVP_CIPHER *aConst = EVP_des_ede3_cbc();
и вы можете записать это в формат pem с помощью следующих функций:
PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL);
PEM_write_X509(
f, /* write the certificate to the file we've opened */
x509 /* our certificate */
);
после этого можно записать эти файлы в файл P12, источником здесь: https://github.com/luvit/openssl/blob/master/openssl/demos/pkcs12/pkwrite.c
/* pkwrite.c */
#include <stdio.h>
#include <stdlib.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
/* Simple PKCS#12 file creator */
int main(int argc, char **argv)
{
FILE *fp;
EVP_PKEY *pkey;
X509 *cert;
PKCS12 *p12;
if (argc != 5) {
fprintf(stderr, "Usage: pkwrite infile password name p12file\n");
exit(1);
}
SSLeay_add_all_algorithms();
ERR_load_crypto_strings();
if (!(fp = fopen(argv[1], "r"))) {
fprintf(stderr, "Error opening file %s\n", argv[1]);
exit(1);
}
cert = PEM_read_X509(fp, NULL, NULL, NULL);
rewind(fp);
pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
fclose(fp);
p12 = PKCS12_create(argv[2], argv[3], pkey, cert, NULL, 0,0,0,0,0);
if(!p12) {
fprintf(stderr, "Error creating PKCS#12 structure\n");
ERR_print_errors_fp(stderr);
exit(1);
}
if (!(fp = fopen(argv[4], "wb"))) {
fprintf(stderr, "Error opening file %s\n", argv[1]);
ERR_print_errors_fp(stderr);
exit(1);
}
i2d_PKCS12_fp(fp, p12);
PKCS12_free(p12);
fclose(fp);
return 0;
}
спасибо всем большое за это хорошее решение!
Я перевел ваш код на Swift 3 и построил следующую функцию для создания хранилища ключей P12 с использованием подписанного сертификата X509 и закрытого ключа RSA, как в формате PEM:
func createP12(pemCertificate: String, pemPrivateKey: String) {
// Read certificate
let buffer = BIO_new(BIO_s_mem())
pemCertificate.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer<Int8>) -> Void in
BIO_puts(buffer, bytes)
})
let certificate = PEM_read_bio_X509(buffer, nil, nil, nil)
X509_print_fp(stdout, certificate)
// Read private key
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)
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()
}
EDIT:
OpenSSL можно использовать в iOS с OpenSSL-для-iPhone:
- Проверьте репозиторий
- создайте статические библиотеки с помощью
./build-libssl.sh
- добавить
$(YOUR_PATH)/OpenSSL-for-iPhone/include
в пути поиска заголовка - добавить
$(YOUR_PATH)/OpenSSL-for-iPhone/lib
в пути поиска библиотеки - добавить
libcrypto.a
иlibssl.a
к связанным фреймворкам и библиотекам - добавьте следующие заголовки в заголовок моста:
Проект-Мост-Заголовок.h:
#import <openssl/err.h>
#import <openssl/pem.h>
#import <openssl/pkcs12.h>
#import <openssl/x509.h>
проблема решена! Спасибо, ребята.
файл p12 теперь создан правильно.
код теперь:
NSString *certPem = [certificate pemCertificate];
[certPem writeToFile:[self certFilePath] atomically:YES encoding:NSUTF8StringEncoding error:nil];
const char *cert_chars = [certPem cStringUsingEncoding:NSUTF8StringEncoding];
BIO *buffer = BIO_new(BIO_s_mem());
BIO_puts(buffer, cert_chars);
X509 *cert;
cert = PEM_read_bio_X509(buffer, NULL, 0, NULL);
if (cert == NULL) {
NSLog(@"error");
}
X509_print_fp(stdout, cert);
if (!X509_check_private_key(cert, [certificate privateKey])) {
NSLog(@"PK error");
}
PKCS12 *p12;
SSLeay_add_all_algorithms();
ERR_load_crypto_strings();
p12 = PKCS12_create("passPhrase", "iOSMobileCertificate", [certificate privateKey], cert, NULL, 0,0,0,0,0);
if(!p12) {
fprintf(stderr, "Error creating PKCS#12 structure\n");
ERR_print_errors_fp(stderr);
exit(1);
}
[self saveP12File:p12];
saveP12File является:
//create empty file
NSString *p12FilePath = [self p12FilePath];
if (![[NSFileManager defaultManager] createFileAtPath:p12FilePath contents:nil attributes:nil])
{
NSLog(@"Error creating file for P12");
@throw [[NSException alloc] initWithName:@"Fail getP12File" reason:@"Fail Error creating file for P12" userInfo:nil];
}
//get a FILE struct for the P12 file
NSFileHandle *outputFileHandle = [NSFileHandle fileHandleForWritingAtPath:p12FilePath];
FILE *p12File = fdopen([outputFileHandle fileDescriptor], "w");
i2d_PKCS12_fp(p12File, p12);
PKCS12_free(p12);
fclose(p12File);
и p12FilePath это:
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
return [documentsFolder stringByAppendingPathComponent:@"CERT.p12"];
спасибо!
мое решение похоже на sundance, я переработал его, чтобы обойти проблемы с дезинфицирующим средством / переполнением кучи, возникающие в XCode 9.
func createP12(secCertificate: SecCertificate, secPrivateKeyBase64: String, p12FileName: String, _ p12Password: String = "") throws -> String {
// 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
#if DEBUG
X509_print_fp(stdout, certificate)
#endif
let pemPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n\(secPrivateKeyBase64)\n-----END RSA PRIVATE KEY-----\n"
let p12Path = try pemPrivateKey.data(using: .utf8)!.withUnsafeBytes({ (bytes: UnsafePointer<Int8>) -> String in
let privateKeyBuffer = BIO_new_mem_buf(bytes, Int32(pemPrivateKey.characters.count))
let privateKey = PEM_read_bio_PrivateKey(privateKeyBuffer, nil, nil, nil)
defer {
BIO_free(privateKeyBuffer)
}
// Print private key
#if DEBUG
PEM_write_PrivateKey(stdout, privateKey, nil, nil, 0, nil, nil)
#endif
// Check if private key matches certificate
guard X509_check_private_key(certificate, privateKey) == 1 else {
throw X509Error.privateKeyDoesNotMatchCertificate
}
// Set OpenSSL parameters
OPENSSL_add_all_algorithms_noconf()
ERR_load_crypto_strings()
// Create P12 keystore
let passPhrase = UnsafeMutablePointer(mutating: (p12Password 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 {
ERR_print_errors_fp(stderr)
throw X509Error.cannotCreateP12Keystore
}
// Save P12 keystore
let fileManager = FileManager.default
let documentsPathURL = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let path = documentsPathURL.appendingPathComponent(p12FileName).path
fileManager.createFile(atPath: path, contents: nil, attributes: nil)
guard let fileHandle = FileHandle(forWritingAtPath: path) else {
NSLog("Cannot open file handle: \(path)")
throw X509Error.cannotOpenFileHandles
}
let p12File = fdopen(fileHandle.fileDescriptor, "w")
i2d_PKCS12_fp(p12File, p12)
PKCS12_free(p12)
fclose(p12File)
fileHandle.closeFile()
NSLog("Wrote P12 keystore to: \(path)")
return path
})
return p12Path
}
enum X509Error: Error {
case privateKeyDoesNotMatchCertificate
case cannotCreateP12Keystore
case cannotOpenFileHandles
}