Repeat the OpenSSL smime command on iPhone / Cocoa

What I'm trying to do is repeat the following command executed via Terminal on Mac, but on iPhone / in Cocoa:

openssl smime -binary -sign -signer cert.pem -inkey key.pem -in file.txt -out encrypted -outform DER 

where "encrypted" is the encrypted file that is the result of the command.

Although it specifies 2 separate keys (public and private keys), they can be used as a single .p12 file.

After this cocoa fragment to encrypt the file using the .p12 certificate, I'm not sure if this is the right way to go.

What is the best approach for replicating the smime command on an iPhone (as per the Terminal command above), or is it not possible at all with the available Security.framework / CommonCrypto methods?

+7
source share
1 answer

As far as I know - you're a little behind the wheel - with a paddle locked in the Appstore.

  • iOS is missing the CMSEncoderAddSigners, CMSEncoderUpdateContent, CMSEncoderCopyEncodedContent that you need for this.
  • Using openssl or Chilkat is also not ideal: since the iOS keychain API does not give you (more) access to the private key after import.

I have solved this in the past with both opensl and Chilkat.

In each case, however, I "cache" a copy of the private key - as soon as it falls into the key chain - all I can get is SecKeyRef (you need to enter an additional agreement / permission with the apple in order to be able to return it and that's it still be in the Appstore. Reverse engineer of any of the VPN (for example, juniper) applications to see the methods / framework for communication).

For openssl - just grab the smime.c code in openssl applications and change. For chilkat, things are much simpler:

  CkoCert * mine = [identity ckoCert]; assert([mime AddEncryptCert: mine] == YES); for(id cc in backupCerts) { assert([mime AddEncryptCert:cc] == YES); } for(id key in [headers allKeys]) { [mime SetHeaderField:[NSString stringWithFormat:@"%s%@", X_HDR_PREFIX, key] value:[headers objectForKey:key] ]; }; [mime SetBodyFromBinary:data]; assert([mime EncryptN] == YES); return [mime GetMimeBytes]; 

and where in the identity field is used "save your own cache":

 -(id)initWithPKCS12:(NSData*)pkcs12der password:(NSString *)password { if (password == nil) password = [APPSETTINGS wellKnownPkcsPassword]; NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: password, kSecImportExportPassphrase, nil]; CFArrayRef items; OSStatus status = SecPKCS12Import((__bridge CFDataRef)pkcs12der, (__bridge CFDictionaryRef)options, &items); if (status != noErr) { NSLog(@"PKCS12 importAsDer failed: Error %ld",(long)status); ... } if (!items || CFArrayGetCount(items) < 1) { NSLog(@"PKCS12 importAsDer failed - nothing returned (%ld bytes DER)", (long)[pkcs12der length]); ... } CFDictionaryRef dict0 = (CFDictionaryRef) CFArrayGetValueAtIndex(items, 0); if (!dict0) return nil; SecIdentityRef iRef = (SecIdentityRef) CFDictionaryGetValue(dict0, kSecImportItemIdentity); CFArrayRef cRef = (CFArrayRef) CFDictionaryGetValue(dict0, kSecImportItemCertChain); self = [self initWithIdentityRef:iRef withChainArrayRef:cRef]; CFRelease(items); #if TARGET_OS_IPHONE // We lack SecPrivate* on iOS. So we cheat a bit - rather than // use the keychain we limt ourselves to our own *.p12 and // keep a copy of the private key in memory. // # ifdef WITH_OPENSSL const unsigned char * ptr = [pkcs12der bytes]; PKCS12 * p12 = d2i_PKCS12(NULL, &ptr, len); char buff[1024]; if (!p12) { NSLog(@"Could not decode PKCS#12: %s", ERR_error_string(ERR_get_error(), buff)); ... }; const char * pass = [password cStringUsingEncoding:NSASCIIStringEncoding]; if (PKCS12_parse(p12, pass, &pkey, &x509, NULL) != 1) { NSLog(@"Could not parse PKCS#12: %s", ERR_error_string(ERR_get_error(), buff)); ... }; .... # else ckoCert = [[CkoCert alloc] init]; if (!([ckoCert LoadPfxData:pkcs12der password:[APPSETTINGS wellKnownPkcsPassword]])) { NSLog(@"PKCS12 loadPfxData failed: %@", [ckoCert LastErrorText]); ... } ckoPrivateKey = [ckoCert ExportPrivateKey]; # endif // chilkat or openssl #endif // iOS return self; } 

Warning: in the above, I stripped most of the mngt / error management and / or replaced it with statements, because otherwise it took too much damage.

Thanks,

Dw.

+3
source

All Articles