Convert SecKeyRef to EC_KEY in iOS - ios

I'm creating CSR from openSSL but due to OpenSSL isn't storing keys in secure enclave so I chooses objective C to create key pair (private key and public key) in secure enclave and send to OpenSSL for X509 certificate. I get successfully public key in NSData and then convert const unsigned char * bitsOfKeyDataPublicKey = (unsigned char *) [publicKey bytes]; and then create public key EC_KEY*_ec_keyPublic = d2i_EC_PUBKEY(NULL,&bitsOfKeyDataPublicKey, publicKeyLegnth);. But For Private key we get SecKeyRef from objective c so for creation EC_Key how can we convert private key or is this any way to convert or use private key ?
Looking for response.
Thanks

You can change private key from SecKeyRef to NSData
Example:
- (NSData *)getPrivateKeyBits {
OSStatus sanityCheck = noErr;
NSData * privateKeyBits = nil;
NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPrivateKey setObject:_privateTag forKey:(id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPrivateKey, (void *)&privateKeyBits);
if (sanityCheck != noErr) {
privateKeyBits = nil;
}
else if (sanityCheck == errSecItemNotFound) {
privateKeyBits = nil;
}
return privateKeyBits;
}
Don't forget to use the _privateTag used for generating private key
now you can use:
const unsigned char *bitsOfKeyDataPrivateKey = (unsigned char *) [[self getPrivateKeyBits] bytes];
EC_KEY *_ec_keyPrivate = d2i_EC_PUBKEY(NULL,&bitsOfKeyDataPrivateKey, privateKeyLegnth);

Related

Crash when signing with RSA/SHA-1

I am using below code for sing string with private key.
I have private key in iPhone keychain. Now get private key in keychain and pass into PEM_read_RSAPrivateKey:
- (NSString *)RSASHA1HashForString:(NSString *)source {
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]
initWithIdentifier:#"TestKeychain"
accessGroup:#"keys"];
if (source == nil) return nil;
OpenSSL_add_all_algorithms();
NSString *signature = nil;
// make a SHA-1 digest of the source string
const char* sourceChars = [source UTF8String];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *)sourceChars, strlen(sourceChars), digest);
FILE *secretFile;
RSA *rsa = NULL;
#try {
NSData *privateKeyFileData = [keychainItem objectForKey:(__bridge id)kSecAttrLabel];
secretFile = (__bridge FILE *)(privateKeyFileData);
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
}
#catch (NSException *exception) {
NSLog(#"Error %#",[exception description]);
}
if (rsa != NULL) {
unsigned int sigLen = 0;
unsigned char *sigBuff = malloc(RSA_size(rsa));
int result = RSA_sign(NID_sha1, digest, (unsigned int) sizeof(digest),
sigBuff, &sigLen, rsa);
if (result != 0) {
NSData *sigData = [NSData dataWithBytes:sigBuff length:sigLen];
signature = [self base64forData:sigData];
}
free(sigBuff);
RSA_free(rsa);
}
return signature;
}
But it getting crash on below code,
FILE *secretFile;
RSA *rsa = NULL;
#try {
NSData *privateKeyFileData = [keychainItem objectForKey:(__bridge id)kSecAttrLabel];
secretFile = (__bridge FILE *)(privateKeyFileData);
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
}
Is there any way to resolve this issue, I don't have PEM file, I have private key in Keychain.
PEM_read_RSAPrivateKey(secretFile, &rsa, NULL, NULL);
Try:
rsa = PEM_read_RSAPrivateKey(privateKeyFile, NULL, NULL, NULL);
// make a SHA-1 digest of the source string
const char* sourceChars = [source UTF8String];
This is not needed. RSA_sign will digest the data for you.

2 level RSA Encryption

I have requirement in the app to do 2 level RSA encryption with 2 different public key.
1) Encrypt plainText with first public key.
2) Encrypt output of first with second public key.
I am using iOS Security framework to do the same. The first level of encryption works fine but when I try to encrypt again the output of step one with second public key the sanity check fails returning -50.
I assume this is due to the reason that buffer size is less for the required text to encrypt. But I am not sure what parameter to change to achieve the same. I tried changing padding from kSecPaddingPKCS1 to other types but it is not giving the required output.
Following is my function for the encryption:
+ (NSData*)getRSAEncryptedText:(NSString*)plaintext withPublicKeyIdSuffix:(NSString*)idSuffix {
SecKeyRef publicKey = NULL;
NSString *publicKeyIdentifier = [NSString stringWithFormat:#"%#.%#",[[NSBundle mainBundle] bundleIdentifier], idSuffix];
NSData * publicTag = [publicKeyIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
[queryPublicKey setObject:kSecClassKey forKey:kSecClass];
[queryPublicKey setObject:publicTag forKey:kSecAttrApplicationTag];
[queryPublicKey setObject:kSecAttrKeyTypeRSA forKey:kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:kSecReturnRef];
SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey);
if (!publicKey)
{
if(publicKey) CFRelease(publicKey);
return nil;
}
size_t cipherBufferSize = SecKeyGetBlockSize(publicKey);
SecPadding kTypeOfWrapPadding = kSecPaddingPKCS1;
// SecPadding kTypeOfWrapPadding = kSecPaddingNone;
uint8_t* cipherBuffer = malloc( cipherBufferSize * sizeof(uint8_t) );
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
OSStatus sanityCheck = noErr;
// Encrypt using the public key.
sanityCheck = SecKeyEncrypt(publicKey,
kTypeOfWrapPadding,
(const uint8_t *)[[plaintext dataUsingEncoding:NSUTF8StringEncoding] bytes],
[[plaintext dataUsingEncoding:NSUTF8StringEncoding] length],
cipherBuffer,
&cipherBufferSize
);
if (sanityCheck != noErr) {
//NSLog(#"error with encryption");
free(cipherBuffer);
return nil;
}
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
return encryptedData;
}
Please suggest how to achieve the second level of encryption.
The issue is resolved now, have altered the order in which keys were used for encryption. The one with smaller size is used first now for encryption and then the second one.

iOS verify digital signature

In my application, I have a public key (represented as string), plain message and digital signature, represented as base64 encoded string, hashed with SHA256 and encrypted with RSA).
Now, I need to verify digital signature. I was trying to do as follows:
create SecKeyRef from NSString (taken from here)
create SHA256 digest from original message
verify signature using SecKeyRawVerify function
(I am trying to avoid using OpenSSL function)
Additionally, my digital signature was created using Java's SHA256withRSA method. I was reading here that SHA256WithRSA appends algorithm identifier with the actual hash. Now, I am not sure whether or not I need to append it to the hash.
Anyway, in both cases I get error -50, which according to Apple's documentations means One
or more parameters passed to a function were not valid.
Here is my code:
-(BOOL) verifySignature:(NSString*) rawData andKey:(NSString*) key andSignature:(NSString*)signature {
NSData* originalData = [rawData dataUsingEncoding:NSUTF8StringEncoding];
NSData *signatureData = [NSData dataFromBase64String:signature];
SecKeyRef publicKey = [self generatePublicKey:key];
uint8_t sha2HashDigest[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([originalData bytes], [originalData length], sha2HashDigest);
//DO I NEED THIS?
NSString *algIdentifier = #"1.3.14.3.2.26";
NSData *algData = [algIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSData* d_hash = [NSData dataWithBytes:sha2HashDigest length:CC_SHA256_DIGEST_LENGTH];
NSMutableData *concatenatedData = [NSMutableData data];
[concatenatedData appendData:algData];
[concatenatedData appendData:d_hash];
OSStatus verficationResult = SecKeyRawVerify (publicKey,
kSecPaddingPKCS1SHA256,
(const uint8_t *)[d_hash bytes],
(size_t)[d_hash length],
(const uint8_t *)[signatureData bytes],
(size_t)[signatureData length]
);
CFRelease(publicKey);
if (verficationResult == errSecSuccess){
NSLog(#"Verified");
return YES;
}
return NO;
}
- (SecKeyRef)generatePublicKey:(NSString *)key
{
// This will be base64 encoded, decode it.
NSData *d_key = [NSData dataFromBase64String:key];
d_key = [self stripPublicKeyHeader:d_key];
if (d_key == nil) return(nil);
NSData *d_tag = [NSData dataWithBytes:[#"pubKey" UTF8String] length:[#"pubKey" length]];
NSMutableDictionary *publicKey = [[NSMutableDictionary alloc] init];
[publicKey setObject:(id) kSecClassKey forKey:(id)kSecClass];
[publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[publicKey setObject:d_tag forKey:(id)kSecAttrApplicationTag];
SecItemDelete((CFDictionaryRef)publicKey);
CFTypeRef persistKey = nil;
// Add persistent version of the key to system keychain
[publicKey setObject:d_key forKey:(id)kSecValueData];
[publicKey setObject:(id) kSecAttrKeyClassPublic forKey:(id)
kSecAttrKeyClass];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)
kSecReturnPersistentRef];
OSStatus secStatus = SecItemAdd((CFDictionaryRef)publicKey, &persistKey);
if (persistKey != nil) CFRelease(persistKey);
if ((secStatus != noErr) && (secStatus != errSecDuplicateItem)) {
[publicKey release];
return(nil);
}
// Now fetch the SecKeyRef version of the key
SecKeyRef keyRef = nil;
[publicKey removeObjectForKey:(id)kSecValueData];
[publicKey removeObjectForKey:(id)kSecReturnPersistentRef];
[publicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef
];
[publicKey setObject:(id) kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
secStatus = SecItemCopyMatching((CFDictionaryRef)publicKey,
(CFTypeRef *)&keyRef);
[publicKey release];
return keyRef;
}
Maybe this answer is a little late but I had the same problem.
It turns out that Java handles the hashing for you, but iOS does not.
So if you have a plaintext called plainText you might generate a signature on it in Java doing this:
public static byte[] sign(PrivateKey key, byte[] plainText) {
try {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(key);
signature.update(plainText);
return signature.sign();
} catch (Exception e) {
return null;
}
}
But then to verify it in iOS you need to manually take a hash of the plaintext like so:
+ (BOOL)verifySignature:(uint8_t*)signature signatureLen:(size_t)sLen
withPlainText:(uint8_t*)plainText plainTextLen:(size_t)pLen
andKey:(SecKeyRef)key {
uint8_t hash[32];
CC_SHA256(plainText, pLen, hash);
OSStatus returnCode = SecKeyRawVerify(key,
kSecPaddingPKCS1SHA256,
hash,
32,
signature,
sLen);
return returnCode == 0;
}
In the above method, signature is the bytes generated by the Java method.
Of course, you may not want to hardcode parameters such as the the hash function used (and length of hash).

Encrypt a NSString using RSA algorithm

I have a exponent and a modulus. How do i encrypt a NSString using RSA algorithm. I have gone through many forums. But still find it confusing. Can anyone give me the right way to encrypt a NSString using RSA algorithm with the exponent and modulus?
I currently try this. But still get a wrong encrypted string
publicTag = [self PublicKeyItems];
SecKeyRef publicKeyData = [self getPublicKeyRef];
NSString* result = (NSString*)[self encryptRSA:#"Shob" key:publicKeyData];
And the following implementations
- (NSData *)PublicKeyItems
{
NSMutableArray *publicarray = [[NSMutableArray alloc] init];
[publicarray addObject:encryptionExponent];
[publicarray addObject:encryptionModulus];
NSData *testData = [publicarray berData];
NSLog(#"testdata = %#",testData);
return testData;
}
-(SecKeyRef)getPublicKeyRef
{
OSStatus sanityCheck = noErr;
SecKeyRef publicKeyReference = NULL;
if (publicKeyReference == NULL) {
[self generateKeyPair:512];
NSMutableDictionary *queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// Get the key.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference);
if (sanityCheck != noErr)
{
publicKeyReference = NULL;
}
// [queryPublicKey release];
} else { publicKeyReference = publicKey; }
return publicKeyReference;
}
- (void)generateKeyPair:(NSUInteger)keySize {
OSStatus sanityCheck = noErr;
publicKey = NULL;
privateKey = NULL;
// LOGGING_FACILITY1( keySize == 512 || keySize == 1024 || keySize == 2048, #"%d is an invalid and unsupported key size.", keySize );
// First delete current keys.
// [self deleteAsymmetricKeys];
// Container dictionaries.
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
// Set top level dictionary for the keypair.
[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(__bridge id)kSecAttrKeySizeInBits];
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr, &publicKey, &privateKey);
// LOGGING_FACILITY( sanityCheck == noErr && publicKey != NULL && privateKey != NULL, #"Something really bad went wrong with generating the key pair." );
if(sanityCheck == noErr && publicKey != NULL && privateKey != NULL)
{
NSLog(#"Successful");
}
// [privateKeyAttr release];
// [publicKeyAttr release];
// [keyPairAttr release];
}
-(NSString *)encryptRSA:(NSString *)plainTextString key:(SecKeyRef)publicKeyNext {
size_t cipherBufferSize = SecKeyGetBlockSize(publicKeyNext);
uint8_t *cipherBuffer = malloc(cipherBufferSize);
uint8_t *nonce = (uint8_t *)[plainTextString UTF8String];
SecKeyEncrypt(publicKeyNext,
kSecPaddingOAEP,
nonce,
strlen( (char*)nonce ),
&cipherBuffer[0],
&cipherBufferSize);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
NSString* encryptedString = [NSString stringWithFormat:#"%#",encryptedData];
return encryptedString;
}
The easy way
Use Chilkat iOS RSA Library. One major downside: cost $189! :O
The hard way
Parse the XML, use SCZ-BasicEncodingRules-iOS to generate a public key data out of the modulus and exponent. If that works, create a dummy keychain using that public key (follow sample code here), extract the public key now in SecKeyRef format and pass it to the encryptRSA method in the question. Finally, cleanup, remove the dummy keychain. Sounds good in theory, but I have never tested this thoroughly, if you do, let me know!

SecKeyGetBlockSize or SecKeyRawVerify for Public Key throw EXC_BAD_ACCESS code=2

Upon trying to implement Security.Framework SecKeyRawVerify iOS function from Apple's example, programm halts with bad pointer error (EXC_BAD_ACCESS code=2). Any help or suggestions would be appreciated.
Here is my code:
- (BOOL)verifySignature:(NSData *)plainText signature:(NSData *)sig {
size_t signedHashBytesSize = 0;
OSStatus sanityCheck = noErr;
SecKeyRef publicKeyA = NULL;
NSMutableDictionary * queryPublicKeyA = [[NSMutableDictionary alloc] init];
NSData * publicTag = [NSData dataWithBytes:publicKeyAIdentifier length:strlen((const char *)publicKeyAIdentifier)]; //
// Set the public key query dictionary.
[queryPublicKeyA setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPublicKeyA setObject:publicTag forKey:(id)kSecAttrApplicationTag];
[queryPublicKeyA setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[queryPublicKeyA setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKeyA, (CFTypeRef *)&publicKeyA);
if (sanityCheck == noErr) {
// Get the size of the assymetric block.
signedHashBytesSize = SecKeyGetBlockSize(publicKeyA); // Halts here
sanityCheck = SecKeyRawVerify(publicKeyA,
kSecPaddingPKCS1SHA1,
(const uint8_t *)[[self getHashBytes:plainText] bytes],
CC_SHA1_DIGEST_LENGTH,
(const uint8_t *)[sig bytes],
signedHashBytesSize
); // And here
}
if(publicKeyA) CFRelease(publicKeyA);
if(queryPublicKeyA) [queryPublicKeyA release];
return (sanityCheck == noErr) ? YES : NO;
}
Link to Apple CryptoExcersize:
http://developer.apple.com/library/ios/#samplecode/CryptoExercise/Introduction/Intro.html#//apple_ref/doc/uid/DTS40008019-Intro-DontLinkElementID_2

Resources