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!
Related
I'm using this website to generate public and private key
http://travistidwell.com/jsencrypt/demo/ and I'm trying to figure it out how to create SecKeyRef from the private key
I've found this project which looks very promising but it doesn't work for me.
Here is my code which try to create the SecKeyRef
NSString* publicKey = #"MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHKzKc/6vphvntLiP1r/YvxjSLolPeDeOCy48ao5ymwNU2Nqbfeir/qHqbqSAhclAO8TGq8QIpE5ObAKNp2j01pu8Cu9AqwdtZ6EZa/NYahfITKS8iYGs6cHzk2LGw8AqFOEJqHrW/xR8MOS1J765KeZOBCSrWZ5Ag/lpb5jxiDlAgMBAAE=";
[[RSA sharedInstance] setPublicKey:publicKey];
and
- (BOOL)setPublicKey: (NSString *)keyAsBase64 {
NSData *extractedKey =
[[NSData alloc] initWithBase64EncodedString:keyAsBase64 options:0];
/* Load as a key ref */
OSStatus error = noErr;
CFTypeRef persistPeer = NULL;
NSData * refTag = [self.serverPublicIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary * keyAttr = [[NSMutableDictionary alloc] init];
[keyAttr setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[keyAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyAttr setObject:refTag forKey:(__bridge id)kSecAttrApplicationTag];
/* First we delete any current keys */
error = SecItemDelete((__bridge CFDictionaryRef) keyAttr);
[keyAttr setObject:extractedKey forKey:(__bridge id)kSecValueData];
[keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
error = SecItemAdd((__bridge CFDictionaryRef) keyAttr, (CFTypeRef *)&persistPeer);
if (persistPeer == nil || ( error != noErr && error != errSecDuplicateItem)) {
NSLog(#"Problem adding public key to keychain");
return FALSE;
}
CFRelease(persistPeer);
serverPublicRef = nil;
/* Now we extract the real ref */
[keyAttr removeAllObjects];
/*
[keyAttr setObject:(id)persistPeer forKey:(id)kSecValuePersistentRef];
[keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
*/
[keyAttr setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[keyAttr setObject:refTag forKey:(__bridge id)kSecAttrApplicationTag];
[keyAttr setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[keyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// Get the persistent key reference.
error = SecItemCopyMatching((__bridge CFDictionaryRef)keyAttr, (CFTypeRef *)&serverPublicRef);
if (serverPublicRef == nil || ( error != noErr && error != errSecDuplicateItem)) {
NSLog(#"Error retrieving public key reference from chain");
return FALSE;
}
return TRUE;
}
I'm getting serverPublicRef == nil but the error is 0 (which is ok.)
The kSecClassKey does not have a kSecValueData field. You should look into using SecPKCS12Import().
Some related questions:
How to create private key from file in Objective C?
How to make a valid p12 file to be correctly imported by SecPKCS12Import
Can anybody tell me where is the miastake? I have no idea now.
//Get a query Dic
+ (NSMutableDictionary *)keychainQueryDictionary:(NSString *)service
{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(__bridge id)kSecClassGenericPassword,(__bridge id)kSecClass,
service, (__bridge id)kSecAttrService,
service, (__bridge id)kSecAttrAccount,
(__bridge id)kSecAttrAccessibleAfterFirstUnlock,(__bridge id)kSecAttrAccessible,
nil];
}
//Here i save some data to keychain
+ (OSStatus)saveData:(id)data service:(NSString *)serviceIdentify
{
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
return SecItemAdd((__bridge CFDictionaryRef)keychainQuery, NULL);
}
//delete opration
+ (OSStatus)deleteData:(NSString *)serviceIdentify
{
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
return SecItemDelete((__bridge CFDictionaryRef)keychainQuery);
}
//Here i try to update one item in keychian, but i get an error -50, but i have no idea where is the wrong param
+ (OSStatus)updataData:(id)data service:(NSString *)serviceIdentify
{
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
CFDataRef keyData = NULL;
OSStatus status = errSecNotAvailable;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
NSMutableDictionary *queryDict = nil;
NSDictionary *ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
queryDict = [NSMutableDictionary dictionaryWithDictionary:ret];
[queryDict setObject:[keychainQuery objectForKey:(__bridge id)kSecClass] forKey:(__bridge id)kSecClass];
NSMutableDictionary *attributesToUpdate = [self keychainQueryDictionary:serviceIdentify];
[attributesToUpdate setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge id)kSecValueData];
status = SecItemUpdate((__bridge CFDictionaryRef)queryDict,(__bridge CFDictionaryRef)attributesToUpdate);
}
return status;
}
//read opration
+ (id)loadData:(NSString *)serviceIdentify
{
id ret = nil;
NSMutableDictionary *keychainQuery = [self keychainQueryDictionary:serviceIdentify];
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
[keychainQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
CFDataRef keyData = NULL;
if (SecItemCopyMatching((__bridge CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
}
return ret;
}
-50 is errSecParam. I think you have the parameters flipped for SecItemUpdate. The first param is the search query to find the matching item that needs to be updated. This probably should be created by calling your keychainQueryDictionary method. The second parameter should contain the change - in your example this is the new data for kSecValueData like you are doing, but the dictionary generally doesn't include any of search attributes. In your example it looks like it also contains all the search attributes again which will cause an errSecParam.
As a separate point, also note that any values in the keychainQuery dictionary that are not applicable to the update will also cause errSecParam. If these values are present they can simply be removed, for example:
CFDictionaryRemoveValue(keychainQuery, kSecReturnData);
CFDictionaryRemoveValue(keychainQuery, kSecMatchLimit);
I have two keys, public and private, that are both stored in SecKeyRef-variables. For simplicity's sake, let's start with the public one. What I wanna do is export it to an NSData object. For that, there is an almost famous code snippet provide by Apple, which is here:
- (NSData *)getPublicKeyBits {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
// Set the public key query dictionary.
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag];
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
[queryPublicKey release];
return publicKeyBits;
}
I have Xcode 4.6.2, however, and the code appears erroneous ("__bridge" is added before each conversion to id). The new version looks like this:
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
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)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
return publicKeyBits;
}
There are still two errors, though:
use of undeclared identifier 'publicTag'
Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
Now, I hope that after your help, the first issue will no longer be a problem, for I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there. The variable's name is givenPublicKey, and that's the key I wish to convert to NSData.
So, how would I go about doing this and solving this ARC-issue?
Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.
use of undeclared identifier 'publicTag'
The publicTag is just some unique identifier added to the Keychain items. In the CryptoExercise sample project it is defined as
#define kPublicKeyTag "com.apple.sample.publickey"
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
Cast of an indirect pointer to an Objective-C pointer to 'CFTypeRef ' (aka 'const void *') is disallowed with ARC
This can be solved by using a temporary CFTypeRef variable:
CFTypeRef result;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryPublicKey, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
}
I do not want to build a query or whatnot to extract the key from the keychain. I have it in a variable and I wish to extract it from there ...
As far as I know, you have to store the SecKeyRef to the Keychain temporarily. SecItemAdd
has the option to return the added item as data. From the documentation:
To obtain the data of the added item as an object of type CFDataRef,
specify the return type key kSecReturnData with a value of
kCFBooleanTrue.
Putting all that together, the following code should do what you want:
- (NSData *)getPublicKeyBitsFromKey:(SecKeyRef)givenKey {
static const uint8_t publicKeyIdentifier[] = "com.your.company.publickey";
NSData *publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
[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];
// Temporarily add key to the Keychain, return as data:
NSMutableDictionary * attributes = [queryPublicKey mutableCopy];
[attributes setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
[attributes setObject:#YES forKey:(__bridge id)kSecReturnData];
CFTypeRef result;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) attributes, &result);
if (sanityCheck == errSecSuccess) {
publicKeyBits = CFBridgingRelease(result);
// Remove from Keychain again:
(void)SecItemDelete((__bridge CFDictionaryRef) queryPublicKey);
}
return publicKeyBits;
}
I hope that this works, I cannot test it at the moment.
Follow-up: How can I export a private key to NSData, since I've read several time that the function I'm trying to work with only works for public keys.
I don't know.
Is there any example for use ECC in iOS?
I noticed that the kSecAttrKeyTypeEC in Apple Developer Documents, but I can't use it to generic Key pair.
Below code is modified from the example CryptoExercise
// 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:(id)kSecAttrKeyTypeEC forKey:(id)kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
// Set the private key dictionary.
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set the public key dictionary.
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag];
// See SecKey.h to set other flag values.
// Set attributes to top level dictionary.
[keyPairAttr setObject:privateKeyAttr forKey:(id)kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(id)kSecPublicKeyAttrs];
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes.
sanityCheck = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef);
LOGGING_FACILITY( sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL, #"Something really bad went wrong with generating the key pair." );
The sanityCheck always return -50 which means 'errSecParam'.
I really don't know how to use it, thank you for read this.
NSDictionary *parameters = #{
(__bridge id)kSecAttrKeyType: (__bridge id)kSecAttrKeyTypeEC,
(__bridge id)kSecAttrKeySizeInBits: #256,
(__bridge id)kSecPrivateKeyAttrs: #{
(__bridge id)kSecAttrIsPermanent: #YES,
(__bridge id)kSecAttrApplicationTag: [#"my.key.tag" dataUsingEncoding:NSUTF8StringEncoding],
},
(__bridge id)kSecPublicKeyAttrs: #{
(__bridge id)kSecAttrIsPermanent: #YES,
(__bridge id)kSecAttrApplicationTag: [#"my.key.pubtag" dataUsingEncoding:NSUTF8StringEncoding],
}
};
SecKeyRef publicKey, privateKey;
OSStatus status = SecKeyGeneratePair((__bridge CFDictionaryRef)parameters, &publicKey, &privateKey);
This works, double check your key size parameter.
Just a note, currently EC keys can only be used for signing/verifying data. Encryption/decryption returns errSecUnimplemented = -4.
CryptoKit now supports Ed25519 in iOS13+
https://developer.apple.com/documentation/cryptokit/curve25519/signing
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