2 level RSA Encryption - ios

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.

Related

Convert SecKeyRef to EC_KEY in 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);

RSA encryption using SecKeyEncrypt gives error -4 (errSecUnimplemented)

I'm trying to encrypt some data with RSA using the Security framework on iOS. I want to encrypt a simple base64-encoded string as follows:
NSData *data = [[NSData alloc] initWithBase64EncodedString:#"aGFsbG8=" options:0x0];
NSData *encrypted = [pair encrypt:data];
The pair variable holds references to a private key and a public key that were succesfully generated before using SecKeyGeneratePair.
The encrypt function looks like this:
- (NSData *)encrypt:(NSData *)data {
void *buffer = malloc([self blockSize] * sizeof(uint8_t));
memset(buffer, 0x0, [self blockSize]);
size_t ciphertextBufferLength = [data length];
OSStatus res = SecKeyEncrypt([self keyRef], 0x1, [data bytes], [data length], &buffer[0], &ciphertextBufferLength);
NSLog(#"Result of encryption: %d", res);
return [NSData dataWithBytesNoCopy:buffer length:[self blockSize] freeWhenDone:YES];
}
The implementation of [self blockSize] is rather straightforward:
- (unsigned long)blockSize {
return SecKeyGetBlockSize(keyRef);
}
I generate my keys with the following function:
- (BOOL)generateNewKeyPairOfSize:(unsigned int)keySize
{
SecKeyRef privKey = [[self publicKey] keyRef];
SecKeyRef pubKey = [[self publicKey] keyRef];
NSDictionary *privateKeyDict = #{ (__bridge id)kSecAttrIsPermanent : #(YES), (__bridge id)kSecAttrApplicationTag : [[self privateKey] tag] };
NSDictionary *publicKeyDict = #{ (__bridge id)kSecAttrIsPermanent : #(YES), (__bridge id)kSecAttrApplicationTag : [[self publicKey] tag] };
NSDictionary *keyDict = #{ (__bridge id)kSecAttrKeyType : (__bridge id)kSecAttrKeyTypeRSA, (__bridge id)kSecAttrKeySizeInBits : #(keySize), (__bridge id)kSecPublicKeyAttrs : publicKeyDict, (__bridge id)kSecPrivateKeyAttrs : privateKeyDict };
OSStatus res = SecKeyGeneratePair((__bridge CFDictionaryRef)keyDict, &privKey, &pubKey);
NSLog(#"Result of generating keys: %d", res);
[[self publicKey] setKeyRef:pubKey];
[[self privateKey] setKeyRef:privKey];
return YES;
}
The problem is that the value of res is -4, meaning errSecUnimplemented according to the documentation. I'm not sure what I'm doing wrong here since I have all parameters required. I'm not sure whether there is a mistake in the parameters and where. The call the [self blockSize] return 128.
Can anyone help me with this?
From documentation:
cipherTextLen - On entry, the size of the buffer provided in the
cipherText parameter. On return, the amount of data actually placed in
the buffer.
You don't set any value to ciphertextBufferLength.
Update #1
In SecKeyGeneratePair() you have wrong arguments: public key argument must be first, private key is the second. I think that is the reason why you have error code -4.
Update #2
When you fix problem from Update #1 you will see error code -50 (errSecParam) because your cipher text length is wrong. Here is how correct encryption/decryption looks like:
[self generateNewKeyPairOfSize:1024];
NSData *data = [[NSData alloc] initWithBase64EncodedString:#"aGFsbG8=" options:0x0];
size_t cipherTextSize = [self blockSize];
uint8_t *cipherText = malloc(cipherTextSize);
memset(cipherText, 0, cipherTextSize);
OSStatus res = SecKeyEncrypt(_publicKey, kSecPaddingPKCS1, [data bytes], data.length, cipherText, &cipherTextSize);
NSLog(#"Result of encryption: %d", res);
size_t plainTextSize = cipherTextSize;
uint8_t *plainText = malloc(plainTextSize);
res = SecKeyDecrypt(_privateKey, kSecPaddingPKCS1, cipherText, cipherTextSize, plainText, &plainTextSize);
NSLog(#"Result of decryption: %d", res);
In addition to the correct answer above, what solved it for me was the following knowledge:
You will get -4 if you are trying to encrypt anything using a SecKeyRef that refers to a private key.
Think about it. Nothing that was encrypted with a private key would be secure because the public key is, well, public. /facepalm
So yeah, Apple's framework does the responsible thing, and simply blocks you from encrypting something with a private key. Because if it allowed you to do something that stupid, then it would be giving you a false sense of security, which would be irresponsible.

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