I want to create a EC_Key from NSData, via OpenSSL. So I write the following:
- (void)setPrivateKey:(NSData *)privateKey {
const unsigned char *bits = (unsigned char *) [privateKey bytes];
eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
ec_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
EC_KEY_set_group(eckey, ec_group);
EC_KEY_generate_key(eckey);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
eckey = d2i_ECParameters(&eckey, &bits, privateKey.length);
}
but
eckey = d2i_ECParameters(&eckey, &bits, privateKey.length);
returns null.
What is the problem?
Thanks to #jww for his great comments, finally, I succeed to solve this problem by the following code block
- (void)setPrivateKey:(NSData *)privateKey {
const unsigned char *privateKeyBits = (unsigned char *) [privateKey bytes];
ec_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
eckey = EC_KEY_new();
EC_KEY_set_group(eckey, ec_group);
EC_KEY_generate_key(eckey);
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
BIGNUM *prv = BN_bin2bn(privateKeyBits, sizeof(privateKeyBits), NULL);
EC_KEY_set_private_key(eckey, prv);
EC_POINT *pub = EC_POINT_new(ec_group);
EC_POINT_mul(ec_group, pub, prv, NULL, NULL, NULL);
EC_KEY_set_public_key(eckey, pub);
pkey = EVP_PKEY_new();
EVP_PKEY_set1_EC_KEY(pkey, eckey);
}
Although I had both public key and private key, but with above code I calculated the public key from private key.
Related
I am trying to find a S/MIME email encryption library for an iOS email app I am creating in Swift. I have been having trouble trying to find a library for the encryption, has anyone had any experience with this?
I have tried OpenSSL but have run into issues with importing all the files in need in the bridging header, for example I need to use functions in pem.h but if I try import pem.h the bridging header fails to be imported altogether.
Any help with this would be greatly appreciated.
I had a similar requirement. Eventually I had to import openSSL and write my own code to handle the decrypt of PKCS7. I made a small github repo which should help
https://github.com/zkrige/iOS-pkcs7-decrypt
here is the gist of the code
#include <openssl/bio.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/crypto.h>
#include <openssl/rand.h>
X509 *getCert(const char *certificate) {
BIO *membuf = BIO_new(BIO_s_mem());
BIO_puts(membuf, certificate);
X509 *x509 = PEM_read_bio_X509(membuf, NULL, NULL, NULL);
return x509;
}
EVP_PKEY *getKey(const char *privateKey) {
BIO *membuf = BIO_new(BIO_s_mem());
BIO_puts(membuf, privateKey);
EVP_PKEY *key = PEM_read_bio_PrivateKey(membuf, NULL, 0, NULL);
return key;
}
PKCS7 *getContainer(const char *encrypted) {
BIO* membuf = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(membuf, 0);
BIO_puts(membuf, encrypted);
PKCS7* pkcs7 = SMIME_read_PKCS7(membuf, NULL);
if (!pkcs7) {
fprintf(stderr, "error: %ld\n", ERR_get_error());
}
return pkcs7;
}
char *decrypt(PKCS7 *pkcs7, EVP_PKEY *pkey, X509 *cert) {
BIO *out = BIO_new(BIO_s_mem());
if (PKCS7_decrypt(pkcs7, pkey, cert, out, 0) != 1) {
X509_free(cert);
EVP_PKEY_free(pkey);
PKCS7_free(pkcs7);
fprintf(stderr, "Error decrypting PKCS#7 object: %ld\n", ERR_get_error());
return NULL;
}
BUF_MEM* mem;
BIO_get_mem_ptr(out, &mem);
char *data = malloc(mem->length + 1);
memcpy(data, mem->data, mem->length + 1);
BIO_flush(out);
BIO_free(out);
return data;
}
char *decrypt_smime(const char *encrypted, const char *privateKey, const char *certificate) {
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
X509 *cert = getCert(certificate);
if (!cert) {
return NULL;
}
EVP_PKEY *pkey = getKey(privateKey);
if (!pkey) {
X509_free(cert);
return NULL;
}
PKCS7 *pkcs7 = getContainer(encrypted);
if (!pkcs7) {
X509_free(cert);
EVP_PKEY_free(pkey);
return NULL;
}
char *data = decrypt(pkcs7, pkey, cert);
X509_free(cert);
EVP_PKEY_free(pkey);
PKCS7_free(pkcs7);
return data;
}
I have a method in objective-C which I call from swift. It worked pretty well in swift 2, but in swift 3 the behaviour has changed. It gives me 3 different results, even though I send the same parameters.
Sometimes it doesnt find pfile, sometimes it fails on pin checking, sometimes works good and gives me x509.
char* ParsePKCS12(unsigned char* pkcs12_path, unsigned char * pin) {
printf("PARSE PATH: %s\n", pkcs12_path);
printf("PASSWORD: %s\n", pin);
NSString *pfile = [NSString stringWithUTF8String:pkcs12_path];
FILE *fp;
PKCS12 *p12;
EVP_PKEY *pkey;
X509 *cert;
BIO *databio = BIO_new(BIO_s_mem());
STACK_OF(X509) *ca = NULL;
if([[NSFileManager defaultManager] fileExistsAtPath:pfile]) {
NSLog(#"ok, pfile exists!");
} else {
NSLog(#"error, pfile does not exists!");
return "-1";
}
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
fp = fopen([pfile UTF8String], "rb");
p12 = d2i_PKCS12_fp(fp, NULL);
fclose (fp);
if (!p12) {
fprintf(stderr, "Error reading PKCS#12 file\n");
ERR_print_errors_fp(stderr);
return "-1";
}
if (!PKCS12_parse(p12, (const char *)pin, &pkey, &cert, &ca)) { //Error at parsing or pin error
fprintf(stderr, "Error parsing PKCS#12 file\n");
ERR_print_errors_fp(stderr);
ERR_print_errors(databio);
return "-1";
}
BIO *bio = NULL;
char *pem = NULL;
if (NULL == cert) {
//return NULL;
return "-1";
}
bio = BIO_new(BIO_s_mem());
if (NULL == bio) {
return "-1";
}
if (0 == PEM_write_bio_X509(bio, cert)) {
BIO_free(bio);
//return NULL;
}
pem = (char *) malloc(bio->num_write + 1);
if (NULL == pem) {
BIO_free(bio);
return "-1";
}
memset(pem, 0, bio->num_write + 1);
BIO_read(bio, pem, bio->num_write);
BIO_free(bio);
PKCS12_free(p12);
return pem;
}
this code I call in swift like this:
self.x509 = String(cString:ParsePKCS12(UnsafeMutablePointer<UInt8>(mutating: self.path),
UnsafeMutablePointer<UInt8>(mutating: "123456"))!)
Your call
self.x509 = String(cString:ParsePKCS12(UnsafeMutablePointer<UInt8>(mutating: self.path),
UnsafeMutablePointer<UInt8>(mutating: "123456"))!)
does not work reliably because in both
UnsafeMutablePointer<UInt8>(mutating: someSwiftString)
calls, the compiler creates a temporary C string representation of
the Swift string and passes that to the function. But that C string
is only valid until the UnsafeMutablePointer constructor returns, which means that the second
string conversion can overwrite the first, or any other undefined
behaviour.
The simplest solution would be to change the C function to
take constant C strings (and use the default signedness):
char* ParsePKCS12(const char * pkcs12_path, const char * pin)
Then you can simply call it as
self.x509 = String(cString: ParsePKCS12(self.path, "123456"))
and the compiler creates temporary C strings which are valid during
the call of ParsePKCS12().
I'm using OpenSSL to generate digital signature for a PDF by PoDoFo library.
Here is the logic for signature handler
OpenSSLSignatureHandler.h
#import <Foundation/Foundation.h>
// OpenSSL includes
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pkcs12.h>
#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#interface OpenSSLSignatureHandler : NSObject
{
SHA_CTX m_sha_ctx;
EVP_PKEY* mp_pkey; // private key
X509* mp_x509; // signing certificate
STACK_OF(X509)* mp_ca; // certificate chain up to the CA
}
- (id) initWithCert:(NSString*) p12file password: (NSString*) password;
- (void) AppendData: (NSData*)data;
- (NSData*) getSignature;
#end
OpenSSLSignatureHandler.m
#import "OpenSSLSignatureHandler.h"
#include <string>
#implementation OpenSSLSignatureHandler
- (id) initWithCert:(NSString*) p12file password: (NSString*) password{
if (self = [super init]) {
// Initialize OpenSSL library
CRYPTO_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
FILE* fp = fopen([p12file cStringUsingEncoding: NSASCIIStringEncoding], "rb");
if (fp == NULL)
#throw ([NSException exceptionWithName: #"PDFNet Exception" reason: #"Cannot open private key." userInfo: nil]);
PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
fclose(fp);
if (p12 == NULL)
#throw ([NSException exceptionWithName: #"PDFNet Exception" reason: #"Cannot parse private key." userInfo: nil]);
mp_pkey = NULL;
mp_x509 = NULL;
mp_ca = NULL;
int parseResult = PKCS12_parse(p12, [password cStringUsingEncoding: NSASCIIStringEncoding], &mp_pkey, &mp_x509, &mp_ca);
PKCS12_free(p12);
if (parseResult == 0)
#throw ([NSException exceptionWithName: #"PDFNet Exception" reason: #"Cannot parse private key." userInfo: nil]);
//initialize sha context
SHA1_Init(&m_sha_ctx);
}
return self;
}
- (void) AppendData: (NSData*)data
{
SHA1_Update(&m_sha_ctx, [data bytes], [data length]);
return;
}
- (BOOL) Reset
{
SHA1_Init(&m_sha_ctx);
return (YES);
}
- (NSData*) getSignature
{
unsigned char sha_buffer[SHA_DIGEST_LENGTH];
memset((void*) sha_buffer, 0, SHA_DIGEST_LENGTH);
SHA1_Final(sha_buffer, &m_sha_ctx);
PKCS7* p7 = PKCS7_new();
PKCS7_set_type(p7, NID_pkcs7_signed);
PKCS7_SIGNER_INFO* p7Si = PKCS7_add_signature(p7, mp_x509, mp_pkey, EVP_sha1());
PKCS7_add_attrib_content_type(p7Si, OBJ_nid2obj(NID_pkcs7_data));
PKCS7_add0_attrib_signing_time(p7Si, NULL);
PKCS7_add1_attrib_digest(p7Si, (const unsigned char*) sha_buffer, SHA_DIGEST_LENGTH);
PKCS7_add_certificate(p7, mp_x509);
int c = 0;
for ( ; c < sk_X509_num(mp_ca); c++) {
X509* cert = sk_X509_value(mp_ca, c);
PKCS7_add_certificate(p7, cert);
}
PKCS7_set_detached(p7, 1);
PKCS7_content_new(p7, NID_pkcs7_data);
PKCS7_SIGNER_INFO_sign(p7Si);
int p7Len = i2d_PKCS7(p7, NULL);
NSMutableData* signature = [NSMutableData data];
unsigned char* p7Buf = (unsigned char*) malloc(p7Len);
if (p7Buf != NULL) {
unsigned char* pP7Buf = p7Buf;
i2d_PKCS7(p7, &pP7Buf);
[signature appendBytes: (const void*) p7Buf length: p7Len];
free(p7Buf);
}
PKCS7_free(p7);
return (signature);
}
- (void) dealloc
{
sk_X509_free(mp_ca);
X509_free(mp_x509);
EVP_PKEY_free(mp_pkey);
// Release OpenSSL resource usage
ERR_free_strings();
EVP_cleanup();
[super dealloc];
}
#end
Using podofo to embed signature
void CreateSimpleForm( PoDoFo::PdfPage* pPage, PoDoFo::PdfStreamedDocument* pDoc, const PoDoFo::PdfData &signatureData )
{
PoDoFo::PdfPainter painter;
PoDoFo::PdfFont* pFont = pDoc->CreateFont( "Courier" );
painter.SetPage( pPage );
painter.SetFont( pFont );
painter.DrawText( 10000 * CONVERSION_CONSTANT, 280000 * CONVERSION_CONSTANT, "PoDoFo Sign Test" );
painter.FinishPage();
PoDoFo::PdfSignatureField signField( pPage, PoDoFo::PdfRect( 0, 0, 0, 0 ), pDoc );
signField.SetFieldName("SignatureFieldName");
signField.SetSignature(signatureData);
signField.SetSignatureReason("Document verification");
// Set time of signing
signField.SetSignatureDate( PoDoFo::PdfDate() );
}
+(void)addDigitalSignatureOnPage:(NSInteger)pageIndex outpath:(NSString*)path/*doc:(PoDoFo::PdfMemDocument*)aDoc*/{
PoDoFo::PdfPage* pPage;
PoDoFo::PdfSignOutputDevice signer([path UTF8String]);
// Reserve space for signature
signer.SetSignatureSize(1024);
if([[NSFileManager defaultManager] fileExistsAtPath:path]){
PoDoFo::PdfStreamedDocument writer( &signer, PoDoFo::ePdfVersion_1_5 );
// Disable default appearance
writer.GetAcroForm(PoDoFo::ePdfCreateObject, PoDoFo::PdfAcroForm::ePdfAcroFormDefaultAppearance_None);
pPage = writer.CreatePage(PoDoFo::PdfPage::CreateStandardPageSize(PoDoFo::ePdfPageSize_A4 ) );
TEST_SAFE_OP( CreateSimpleForm( pPage, &writer, *signer.GetSignatureBeacon() ) );
TEST_SAFE_OP( writer.Close() );
}
// Check if position of signature was found
if(signer.HasSignaturePosition()) {
// Adjust ByteRange for signature
signer.AdjustByteRange();
// Read data for signature and count it
// We have to seek at the beginning of the file
signer.Seek(0);
//OpenSSLSignatureHandler
NSString * p12certpath = [[NSBundle mainBundle] pathForResource:#"iphone-cert" ofType:#"p12"];
OpenSSLSignatureHandler*signatureHandler = [[OpenSSLSignatureHandler alloc] initWithCert:p12certpath password:#"test123$"];
char buff[65536];
size_t len;
while( (len = signer.ReadForSignature(buff, 65536))>0 )
{
NSData* data = [NSData dataWithBytes:(const void *)buff length:len];
[signatureHandler AppendData:data];
}
const PoDoFo::PdfData *pSignature = NULL;
// NSString *pkcsMessage = [[signatureHandler getSignature] base64EncodedString];
// NSLog(#"OpenSSLSignatureHandler signature message = %#",pkcsMessage);
// const char * cstr = [pkcsMessage UTF8String];
// if(pSignature==NULL)pSignature = new PoDoFo::PdfData(cstr, sizeof(cstr));
unsigned char *bytePtr = (unsigned char *)[[signatureHandler getSignature] bytes];
std::string str;
str.append(reinterpret_cast<const char*>(bytePtr));
// Paste signature to the file
if(pSignature==NULL)pSignature = new PoDoFo::PdfData(str.c_str(), sizeof(str));
NSLog(#"str = %s",str.c_str());
NSLog(#"sizeof(str) = %lu",sizeof(str));
signer.SetSignature(*pSignature);
}
signer.Flush();
}
But the signature that's embeded in the PDF is always empty
can some help with this issue ?
I'm receiving a String containing a PEM encoded X.509 certificate from somewhere. I'd like to import this certificate into the KeyChain of iOS.
I'm planning to do the following:
convert NSString to openssl X509
create PKCS12
convert PKCS12 to NSData
import NSData with SecPKCS12Import
So far I came up with the following code:
const char *cert_chars = [certStr 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);
EVP_PKEY *privateKey;
const unsigned char *privateBits = (unsigned char *) [privateKeyData bytes];
int privateLength = [privateKeyData length];
privateKey = d2i_AutoPrivateKey(NULL, &privateBits, privateLength);
if (!X509_check_private_key(cert, privateKey)) {
NSLog(#"PK error");
}
PKCS12 *p12 = PKCS12_create("test", "David's Cert", privateKey, cert, NULL, 0, 0, 0, 0, 0);
Unfortunately, p12 is nil even though X509_check_private_key was successful and X509_print_fp(stdout, cert) prints a valid certificate.
is my approach correct
how come PKCS12_create seems to fail?
Update:
The call PKCS12_create seems to fail in the following method:
int EVP_PBE_CipherInit(ASN1_OBJECT *pbe_obj, const char *pass, int passlen,
ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de)
{
const EVP_CIPHER *cipher;
const EVP_MD *md;
int cipher_nid, md_nid;
EVP_PBE_KEYGEN *keygen;
if (!EVP_PBE_find(EVP_PBE_TYPE_OUTER, OBJ_obj2nid(pbe_obj),
&cipher_nid, &md_nid, &keygen))
{
char obj_tmp[80];
EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_PBE_ALGORITHM);
if (!pbe_obj) BUF_strlcpy (obj_tmp, "NULL", sizeof obj_tmp);
else i2t_ASN1_OBJECT(obj_tmp, sizeof obj_tmp, pbe_obj);
ERR_add_error_data(2, "TYPE=", obj_tmp);
return 0;
}
if(!pass)
passlen = 0;
else if (passlen == -1)
passlen = strlen(pass);
if (cipher_nid == -1)
cipher = NULL;
else
{
cipher = EVP_get_cipherbynid(cipher_nid);
if (!cipher)
{
EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_CIPHER);
return 0;
}
}
if (md_nid == -1)
md = NULL;
else
{
md = EVP_get_digestbynid(md_nid);
if (!md)
{
EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_UNKNOWN_DIGEST);
return 0;
}
}
if (!keygen(ctx, pass, passlen, param, cipher, md, en_de))
{
EVPerr(EVP_F_EVP_PBE_CIPHERINIT,EVP_R_KEYGEN_FAILURE);
return 0;
}
return 1;
}
Retrieving the cipher
cipher = EVP_get_cipherbynid(cipher_nid);
somehow returns nil for "RC2-40-CBC".
The following calls were missing before creating the PKCS12:
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
These solved the problems with the missing cipher and also a subsequent problem of a missing digest.
Im attempting to implement an RSA encryption algorithm into my iOS app, but when I attempt to generate a public and private key pair, the function returns me the errSecUnimplemented error. I am using the 5.1 SDK and targeting 5.1 at the moment.
Can I not use this function, or did I set up something wrong in attempting to generate the pair?
Here is my code for the key generation:
SecKeyRef publicKey, privateKey;
CFDictionaryRef parameters;
const void* keys[] = {kSecAttrKeyType, kSecAttrKeyTypeRSA};
int keySize = 1024;
const void *values[] = {kSecAttrKeySizeInBits, &keySize};
parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
if ( ret == errSecSuccess )
{
NSLog(#"Key success!");
}
else
{
NSLog(#"Key Failure! %li", ret);
}
I've revised it to just complete the solution for you. 1) You need to use a CFNumberRef and not a pointer to an int for the numerical value. 2) The values need to be the values, the keys need to be the keys - you were mixing a key and value in each of "keys" and "values".
SInt32 iKeySize = 1024;
CFNumberRef keySize = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &iKeySize);
const void* values[] = { kSecAttrKeyTypeRSA, keySize };
const void* keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
CFDictionaryRef parameters = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 2, NULL, NULL);
SecKeyRef publicKey, privateKey;
OSStatus ret = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
if ( ret == errSecSuccess )
NSLog(#"Key success!");
else
NSLog(#"Key Failure! %li", ret);
Shouldn't this be:
const void* keys[] = {kSecAttrKeyType, kSecAttrKeySizeInBits};
int keySize = 1024;
const void *values[] = {kSecAttrKeyTypeRSA, &keySize};
i.e., the keys should be the keys of the dict and the values the values, currently you have one (key,value) pair in keys and one in values.
Using kCFAllocatorSystemDefault instead of kCFAllocatorDefault return errSecSuccess.