Following Apple example code in: http://developer.apple.com/library/ios/#documentation/Security/Conceptual/CertKeyTrustProgGuide/iPhone_Tasks/iPhone_Tasks.html
I'm able to successfully generate key-pair with the code snippet below, but unable to print the keys...
The function SecKeyGeneratePair() - returns the keys as SecKeyRef type.
I have no idea how to handle this type, I understand that this is the keychain representation but how can I actually view the key-pair as NSString??
More specifically, how to convert SecKeyRef to NSString??
static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";
// 1
- (void)generateKeyPairPlease
{
OSStatus status = noErr;
NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
// 2
NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
length:strlen((const char *)publicKeyIdentifier)];
NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
length:strlen((const char *)privateKeyIdentifier)];
// 3
SecKeyRef publicKey = NULL;
SecKeyRef privateKey = NULL; // 4
[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA
forKey:(id)kSecAttrKeyType]; // 5
[keyPairAttr setObject:[NSNumber numberWithInt:1024]
forKey:(id)kSecAttrKeySizeInBits]; // 6
[privateKeyAttr setObject:[NSNumber numberWithBool:YES]
forKey:(id)kSecAttrIsPermanent]; // 7
[privateKeyAttr setObject:privateTag
forKey:(id)kSecAttrApplicationTag]; // 8
[publicKeyAttr setObject:[NSNumber numberWithBool:YES]
forKey:(id)kSecAttrIsPermanent]; // 9
[publicKeyAttr setObject:publicTag
forKey:(id)kSecAttrApplicationTag]; // 10
[keyPairAttr setObject:privateKeyAttr
forKey:(id)kSecPrivateKeyAttrs]; // 11
[keyPairAttr setObject:publicKeyAttr
forKey:(id)kSecPublicKeyAttrs]; // 12
status = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr,
&publicKey, &privateKey); // 13
// error handling...
if(privateKeyAttr) [privateKeyAttr release];
if(publicKeyAttr) [publicKeyAttr release];
if(keyPairAttr) [keyPairAttr release];
if(publicKey) CFRelease(publicKey);
if(privateKey) CFRelease(privateKey); // 14
}
You can use SecItemCopyMatching to get key's NSData. Check getPublicKeyBits method in Apple's CryptoExercise, it implements exactly what you need.
Then you can convert NSData to a string. Perhaps, Base64 encoding will suite your needs. Here you can find Base64 encoding/decoding sample for iPhone. Alternatively, this answer may also be useful for Base64 encoding.
You can use https://github.com/henrinormak/Heimdall
let localHeimdall = Heimdall(tagPrefix: "com.example")
if let heimdall = localHeimdall {
let publicKeyData = heimdall.X509PublicKey()
var publicKeyString = publicKeyData.base64EncodedStringWithOptions(.allZeros)
// If you want to make this string URL safe,
// you have to remember to do the reverse on the other side later
publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("/", withString: "_")
publicKeyString = publicKeyString.stringByReplacingOccurrencesOfString("+", withString: "-")
println(publicKeyString) // Something along the lines of "MIGfMA0GCSqGSIb3DQEBAQUAA..."
// Data transmission of public key to the other party
}
swift 3:
let localHeimdall = Heimdall(tagPrefix: "com.example")
if let heimdall = localHeimdall, publicKeyData = heimdall.publicKeyDataX509() {
var publicKeyString = publicKeyData.base64EncodedString()
// If you want to make this string URL safe,
// you have to remember to do the reverse on the other side later
publicKeyString = publicKeyString.replacingOccurrences(of: "/", with: "_")
publicKeyString = publicKeyString.replacingOccurrences(of: "+", with: "-")
println(publicKeyString) // Something along the lines of "MIGfMA0GCSqGSIb3DQEBAQUAA..."
// Data transmission of public key to the other party
}
-(void)writePublicKeyModAndExp
{
KeyHelper* keyHelper =[[KeyHelper alloc]init];
NSData* pubkeyData= [keyHelper getPublicKeyBitsWithtag:publicTag];
NSLog(#"pubKey :%#",[pubkeyData base64Encoding]);
NSData *modData= [keyHelper getPublicKeyModFromKeyData:pubkeyData];
NSLog(#"modulus :%#",[modData base64Encoding]);
NSData *expoData= [keyHelper getPublicKeyExpFromKeyData:pubkeyData];
NSLog(#"exponent :%#",[expoData base64Encoding]);
}
You can find whole code here https://github.com/ozgurshn/EncryptionForiOS
Related
I have followed the apple developer documents and specifically the examples showing how to generate key pair, encrypt with the public key and decrypt with the private key. They have three example methods in the guide for this (page 19 onwards here).
I have copied an pasted these three methods into my project, only changing them to be public class methods, added logging and hooked up buttons to call them feeding the output of the encryption into the decrypt:
In the viewcontroller:
-(IBAction)generateKey:(UIButton*)sender
{
[CryptoClass generateKeyPairPlease];
}
-(IBAction)encryptAndDecrypt
{
NSData *data = [CryptoClass encryptWithPublicKey];
[CryptoClass decryptWithPrivateKey:data];
}
The code for the three methods are:
static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";
+ (NSData *)encryptWithPublicKey
{
OSStatus status = noErr;
size_t cipherBufferSize;
uint8_t *cipherBuffer; // 1
// [cipherBufferSize]
const uint8_t dataToEncrypt[] = "the quick brown fox jumps "
"over the lazy dog\0"; // 2
size_t dataLength = sizeof(dataToEncrypt)/sizeof(dataToEncrypt[0]);
SecKeyRef publicKey = NULL; // 3
NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
length:strlen((const char *)publicKeyIdentifier)]; // 4
NSMutableDictionary *queryPublicKey =
[[NSMutableDictionary alloc] init]; // 5
[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];
// 6
status = SecItemCopyMatching
((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey); // 7
// Allocate a buffer
cipherBufferSize = SecKeyGetBlockSize(publicKey);
cipherBuffer = malloc(cipherBufferSize);
// Error handling
if (cipherBufferSize < sizeof(dataToEncrypt)) {
// Ordinarily, you would split the data up into blocks
// equal to cipherBufferSize, with the last block being
// shorter. For simplicity, this example assumes that
// the data is short enough to fit.
printf("Could not decrypt. Packet too large.\n");
return NULL;
}
// Encrypt using the public.
status = SecKeyEncrypt( publicKey,
kSecPaddingPKCS1,
dataToEncrypt,
(size_t) dataLength,
cipherBuffer,
&cipherBufferSize
); // 8
// Error handling
// Store or transmit the encrypted text
if (publicKey) CFRelease(publicKey);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];
free(cipherBuffer);
return encryptedData;
}
+ (void)generateKeyPairPlease
{
OSStatus status = noErr;
NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
// 2
NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
length:strlen((const char *)publicKeyIdentifier)];
NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
length:strlen((const char *)privateKeyIdentifier)];
// 3
SecKeyRef publicKey = NULL;
SecKeyRef privateKey = NULL; // 4
[keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA
forKey:(__bridge id)kSecAttrKeyType]; // 5
[keyPairAttr setObject:[NSNumber numberWithInt:1024]
forKey:(__bridge id)kSecAttrKeySizeInBits]; // 6
[privateKeyAttr setObject:[NSNumber numberWithBool:YES]
forKey:(__bridge id)kSecAttrIsPermanent]; // 7
[privateKeyAttr setObject:privateTag
forKey:(__bridge id)kSecAttrApplicationTag]; // 8
[publicKeyAttr setObject:[NSNumber numberWithBool:YES]
forKey:(__bridge id)kSecAttrIsPermanent]; // 9
[publicKeyAttr setObject:publicTag
forKey:(__bridge id)kSecAttrApplicationTag]; // 10
[keyPairAttr setObject:privateKeyAttr
forKey:(__bridge id)kSecPrivateKeyAttrs]; // 11
[keyPairAttr setObject:publicKeyAttr
forKey:(__bridge id)kSecPublicKeyAttrs]; // 12
status = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr,
&publicKey, &privateKey); // 13
// error handling...
if(publicKey) CFRelease(publicKey);
if(privateKey) CFRelease(privateKey); // 14
}
+ (void)decryptWithPrivateKey: (NSData *)dataToDecrypt
{
OSStatus status = noErr;
size_t cipherBufferSize = [dataToDecrypt length];
uint8_t *cipherBuffer = (uint8_t *)[dataToDecrypt bytes];
size_t plainBufferSize;
uint8_t *plainBuffer;
SecKeyRef privateKey = NULL;
NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
length:strlen((const char *)privateKeyIdentifier)];
NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
// Set the private key query dictionary.
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// 1
status = SecItemCopyMatching
((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKey); // 2
// Allocate the buffer
plainBufferSize = SecKeyGetBlockSize(privateKey);
plainBuffer = malloc(plainBufferSize);
if (plainBufferSize < cipherBufferSize) {
// Ordinarily, you would split the data up into blocks
// equal to plainBufferSize, with the last block being
// shorter. For simplicity, this example assumes that
// the data is short enough to fit.
printf("Could not decrypt. Packet too large.\n");
return;
}
// Error handling
status = SecKeyDecrypt( privateKey,
kSecPaddingPKCS1,
cipherBuffer,
cipherBufferSize,
plainBuffer,
&plainBufferSize
); // 3
// Error handling
// Store or display the decrypted text
NSLog(#"Plain: %#",[NSString stringWithUTF8String:(const char *)plainBuffer]);
if(privateKey) CFRelease(privateKey);
}
I have been trying many different guides and read a lot of posts here trying to get this to work. I also tried Apples KeyChainWrapperItem to store and retrieve the keys with no luck. I also found a post here describing and showing the exact code to get the key in data-format, but that returns nil for some reason.
The last thing I did was using Matt Gallagher's NSData+Base64 category to print the encrypted string and can visually see that the string is wildly different for each pass even if I do not generate a new key with this code:
-(IBAction)encryptAndDecrypt
{
NSData *data = [CryptoClass encryptWithPublicKey];
NSLog(#"String: %#", [data base64EncodedString]); // Print encrypted data as base64
[CryptoClass decryptWithPrivateKey:data];
}
FYI I'm currently only running on the simulator if that is of importance. And I reset it to clear the keychain before each generation.
Can anyone please help me understand this?
Errors in provided code
In + (NSData *)encryptWithPublicKey, this line will strip the encrypted data (and destroy it)
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];
It should be
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
Different result each time
It is not an error to see different results each time. The PKCS#1 encryption algorithm uses some random seed to make the cipher-text different each time. This is called padding and protects against several attacks, e.g. frequency analysis and ciphertext matching. See this Wikipedia article section: http://en.wikipedia.org/wiki/RSA_(algorithm)#Padding_schemes
I am trying to implement the RSA algorithm and i followed apple reference.
But i am getting problem to convert uint8_t to NSData to NSString.
I have done this so far..These function defined in reference
-(void)test{
[self generateKeyPairPlease];
NSData *data = [self encryptWithPublicKey]; //All goes well until here
[self decryptWithPrivateKey:data];
}
For encryption i did..
- (NSData *)encryptWithPublicKey
{
OSStatus status = noErr;
size_t cipherBufferSize;
uint8_t *cipherBuffer; // 1
// [cipherBufferSize]
const uint8_t dataToEncrypt[] = "the quick brown fox jumps "
"over the lazy dog\0"; // 2
size_t dataLength = sizeof(dataToEncrypt)/sizeof(dataToEncrypt[0]);
SecKeyRef publicKey = NULL; // 3
NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
length:strlen((const char *)publicKeyIdentifier)]; // 4
NSMutableDictionary *queryPublicKey =
[[NSMutableDictionary alloc] init]; // 5
[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];
// 6
status = SecItemCopyMatching
((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey); // 7
// Allocate a buffer
cipherBufferSize = SecKeyGetBlockSize(publicKey);
cipherBuffer = malloc(cipherBufferSize);
// Error handling
if (cipherBufferSize < sizeof(dataToEncrypt)) {
// Ordinarily, you would split the data up into blocks
// equal to cipherBufferSize, with the last block being
// shorter. For simplicity, this example assumes that
// the data is short enough to fit.
printf("Could not decrypt. Packet too large.\n");
return NULL;
}
// Encrypt using the public.
status = SecKeyEncrypt( publicKey,
kSecPaddingPKCS1,
dataToEncrypt,
(size_t) dataLength,
cipherBuffer,
&cipherBufferSize
); // 8
// Error handling
// Store or transmit the encrypted text
if (publicKey) CFRelease(publicKey);
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];
free(cipherBuffer);
return encryptedData;
}
But in decryptWithPrivateKey I can't able to convert uint8_t *plainBuffer (decrypted result) to NSString.First i tried to convert it into NSData on printing with NSLog it show the bytes properly but then NSData is not converting in to string.
- (void)decryptWithPrivateKey: (NSData *)dataToDecrypt
{
OSStatus status = noErr;
size_t cipherBufferSize = [dataToDecrypt length];
uint8_t *cipherBuffer = (uint8_t *)[dataToDecrypt bytes];
size_t plainBufferSize;
uint8_t *plainBuffer;
SecKeyRef privateKey = NULL;
NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
length:strlen((const char *)privateKeyIdentifier)];
NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
// Set the private key query dictionary.
[queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// 1
status = SecItemCopyMatching
((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKey); // 2
// Allocate the buffer
plainBufferSize = SecKeyGetBlockSize(privateKey);
plainBuffer = malloc(plainBufferSize);
if (plainBufferSize < cipherBufferSize) {
// Ordinarily, you would split the data up into blocks
// equal to plainBufferSize, with the last block being
// shorter. For simplicity, this example assumes that
// the data is short enough to fit.
printf("Could not decrypt. Packet too large.\n");
return;
}
// Error handling
status = SecKeyDecrypt( privateKey,
kSecPaddingPKCS1,
cipherBuffer,
cipherBufferSize,
plainBuffer,
&plainBufferSize
); // 3
//*******************************************************************************
// Not able to convert uint8_t *plainBuffer to string
// I also tried to convert it into NSData and then try to convert it into NSString but can't
//What Should i do here to get string back
if(privateKey) CFRelease(privateKey);
}
#end
I want to know how to convert decrypted result uint8_t plainBuffer to NSData to NSString or simply NSString so that i get my string back.For my encryption and key generation code please refer this reference.
Thanks in advance..
i know this question is old and marked as solved but i got here with a similar problem.
what i found out is that there seems to be an error in the encryption method the apple docs in the line
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];
datalength is the wrong variable here. i replaced it with cipherbuffersize to
NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
and everythings works well for me now. i hope it is useful for someone else than me.
The most likely problem is that you're calling +[stringWithCharacters:length:], which expects Unicode characters (more precisely, UTF-16 code points), but you probably have UTF-8 or another 8-bit encoding.
You have to know which encoding you have, so you can call +[stringWithCString:encoding:] (or, if it's UTF-8, +[stringWithUTF8String:] for short).
Note that, unlike stringWithCharacters, these methods don't take a length, and expect your data to be null-terminated. So, unless you're sure the data will always have a terminator, you should either check for it explicitly, or alloc the string and then call -[initWithBytes:length:encoding:].
If you don't know what charset the plaintext was in before it was encrypted, you need to find out. As a human, you can often tell by looking at it, especially if it's mostly ASCII. Create an NSData with the buffer and log that and look at the hex. If it's UTF-16, the ASCII characters will alternate with nulls, so 'Hello' will be 48 00 65 00 6C 00 6C 00 6F 00. But a computer isn't going to do a very good job guessing. (The reason +[stringWithContentsOfFile:usedEncoding:error:] usually gets it right is that it looks at the extended attribute com.apple.TextEncoding, which most Cocoa apps write when they save a file.)
I manage to solve my problem by following this code.It's the same code but encoding of string is different.In encryptWithPublicKey function I have used
uint8_t *dataToEncrypt= (uint8_t *)[#"the quick brown fox jumps over the lazy dog" UTF8String];
instead of
const uint8_t dataToEncrypt[] = "the quick brown fox jumps "
"over the lazy dog\0";
and in decryptWithPrivateKey I have used this to decode
NSData *decryptedData = [NSData dataWithBytes:plainBuffer length:plainBufferSize];
NSString *decryptedString = [[[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding] autorelease];
The Problem: Retrieving a public key
For an iPhone App, I need to create a rsa key pair, store it in the key chain and retrieve the public key.
Fortunately, Apple released a Crypto Exercise Sample where everything i need can be found (class SecKeyWrapper, functions generateKeyPair and getPublicKeyBits).
But after trying to use these functions, I always get the same output for my public key for different key pairs (instead of different public key bits for different key pairs).
Implementation of my generateKeyPair and getPublicKeyBits Function:
I first create a key pair by calling generateKeyPairWithKeySizeInBits (which seems to work fine), afterwards I extract the public key bits with getPublicKeyBits and NSLog them...
- (void) generateKeyPairWithKeySizeInBits:(int)bits withPublicIdentifier:(NSString *)publicIdentifier andPrivateIdentifier:(NSString *)privateIdentifier
{
NSLog(#"begin generating key...");
OSStatus status = noErr;
NSMutableDictionary* privateKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary* publicKeyAttr = [[NSMutableDictionary alloc] init];
NSMutableDictionary* keyPairAttr = [[NSMutableDictionary alloc] init];
NSData* publicTag = [publicIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSData* privateTag = [privateIdentifier dataUsingEncoding:NSUTF8StringEncoding];
SecKeyRef publicKey = NULL;
SecKeyRef privateKey = NULL;
[keyPairAttr setObject:(__bridge id) kSecAttrKeyTypeRSA forKey:(__bridge id) kSecAttrKeyType];
[keyPairAttr setObject:[NSNumber numberWithInt:bits] forKey:(__bridge id) kSecAttrKeySizeInBits];
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id) kSecAttrIsPermanent];
[privateKeyAttr setObject:privateTag forKey:(__bridge id) kSecAttrApplicationTag];
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecAttrIsPermanent];
[publicKeyAttr setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
[keyPairAttr setObject:privateKeyAttr forKey:(__bridge id)kSecPrivateKeyAttrs];
[keyPairAttr setObject:publicKeyAttr forKey:(__bridge id)kSecPublicKeyAttrs];
SecItemDelete((__bridge CFDictionaryRef)keyPairAttr);
status = SecKeyGeneratePair((__bridge CFDictionaryRef) keyPairAttr, &publicKey, &privateKey);
if(status != noErr){
NSLog(#"status = %#",status);
}
if(publicKey){
NSLog(#"public key %#",publicKey);
}
if(privateKey){
NSLog(#"private key %#",privateKey);
}
[self getPublicKeyBits:publicIdentifier];
}
- (NSData *)getPublicKeyBits: (NSString*) publicKeyIdentifier {
OSStatus sanityCheck = noErr;
NSData * publicKeyBits = nil;
CFTypeRef pk;
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init];
NSData* publicTag = [publicKeyIdentifier dataUsingEncoding:NSUTF8StringEncoding];
// Set the public key query dictionary.
[queryPublicKey setObject:(__bridge_transfer id)kSecClassKey forKey:(__bridge_transfer id)kSecClass];
[queryPublicKey setObject:publicTag forKey:(__bridge_transfer id)kSecAttrApplicationTag];
[queryPublicKey setObject:(__bridge_transfer id)kSecAttrKeyTypeRSA forKey:(__bridge_transfer id)kSecAttrKeyType];
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge_transfer id)kSecReturnData];
// Get the key bits.
sanityCheck = SecItemCopyMatching((__bridge_retained CFDictionaryRef)queryPublicKey, &pk);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
publicKeyBits = (__bridge_transfer NSData*)pk;
NSLog(#"public bits %#",publicKeyBits);
return publicKeyBits;
}
Output (the "public bits"-part (last line) is always the same):
2012-07-13 10:39:28.391 [12279:707] begin generating key...
2012-07-13 10:39:39.376 [12279:707] public key <SecKeyRef: 0xeb56e00>
2012-07-13 10:39:39.381 [12279:707] private key <SecKeyRef: 0xeb57800>
2012-07-13 10:39:39.397 [12279:707] public bits <3082010a 02820101 00ea41ec 12780dff 20a55d67 62ec9890 028ed031 eccd1163 5b4bd039 01adffb5 766f37b8 31be8d03 4a41240b 4e127b75 bc4dd8fb 5b404d4d ad18d711 5cf64a04 61c49970 1fef5434 215f80e4 efee8894 1f282f77 8ea5f7e3 35673260 80ee5f80 818d19d3 b7b15e5c f013ad7d 5ff5dd33 b3e57544 de50dc02 f28aa2ae d4b9590a 1e71bd05 79e81fb7 2a9cd592 cf412fe1 db7a89d4 05bd1731 f95f7aae 56ec4171 e9f352ec c26c3c15 05a0e84b 16c5e89d cec8b1a3 24365d4e dcea88a2 92d8c2e6 8f0e9aee e83703bc e66418af aa9dceea 1129f669 cf069b87 edde4cf7 5e313212 80f44e04 d5b5e2db 9e7f26ae 9b8ef8ee 2e177702 18673b1a f125d3c8 d9ddf978 fb020301 0001>
So Your SecItemDelete code does not work as you expect it to. If you want to delete all the items in your keychain (for testing purposes, obviously), you should look at the code in -[Credentials resetCredentials] method in the AdvancedURLConnections sample code.
https://developer.apple.com/library/ios/#samplecode/AdvancedURLConnections/
You might also find the -dumpCredentials method handy during debugging.
So The problem with SecItemDelete means that SecKeyGeneratePair is generating a new set of keys each time you run the app. Each of these keys has the same application tag, making it indeterminate as to which key is returned by your SecItemCopyMatching call in -getPublicKeyBits:. In turns out that on current system software you're always getting back the first key, so you always get the same public key bits.
Public key will be having two components Exponent and Modulus.You can try this
after getting PublickeyBits method
- (NSData *)getPublicKeyExp
{
NSData* pk = [self getPublicKeyBits];
if (pk == NULL) return NULL;
int iterator = 0;
iterator++; // TYPE - bit stream - mod + exp
[self derEncodingGetSizeFrom:pk at:&iterator]; // Total size
iterator++; // TYPE - bit stream mod
int mod_size = [self derEncodingGetSizeFrom:pk at:&iterator];
iterator += mod_size;
iterator++; // TYPE - bit stream exp
int exp_size = [self derEncodingGetSizeFrom:pk at:&iterator];
return [pk subdataWithRange:NSMakeRange(iterator, exp_size)];
return pk;
}
- (NSData *)getPublicKeyMod
{
NSData* pk = [self getPublicKeyBits];
if (pk == NULL) return NULL;
int iterator = 0;
iterator++; // TYPE - bit stream - mod + exp
[self derEncodingGetSizeFrom:pk at:&iterator]; // Total size
iterator++; // TYPE - bit stream mod
int mod_size = [self derEncodingGetSizeFrom:pk at:&iterator];
return [pk subdataWithRange:NSMakeRange(iterator, mod_size)];
return pk;
NSLog(#"public size: %d",pk.length);
}
- (int)derEncodingGetSizeFrom:(NSData*)buf at:(int*)iterator
{
const uint8_t* data = [buf bytes];
int itr = *iterator;
int num_bytes = 1;
int ret = 0;
if (data[itr] > 0x80) {
num_bytes = data[itr] - 0x80;
itr++;
}
for (int i = 0 ; i < num_bytes; i++)
ret = (ret * 0x100) + data[itr + i];
*iterator = itr + num_bytes;
return ret;
}
I need to send my public key that has been generated by SecKeyGeneratePair as a SecKeyRef object.
Now, to send this, i need this KeyRef object to be in a string format.
How do i convert the SecKeyRef object to nsstring object?
// you have SecKeyRef keyref from somewhere
size_t keySize = SecKeyGetBlockSize(keyref);
NSData* keyData = [NSData dataWithBytes:keyref length:keySize];
Then use this NSData category to encode the NSData object with base64 to a NSString.
NSString *keyStringB64 = [keyData base64EncodedString];
[Cleanup of old question]
Saving SecKeyRef device generated public/private key pair on disk
Shows how to create an NSData object. As I'm no iOS developer, I'll leave the conversion to a string (e.g. using base 64 encoding) as an excercise.
+ (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;
}
then, conver the data to base64 string.
it works fine when I run this code as part of an iOS app. 😀
see iOS SecKeyRef to NSData😄
see more https://developer.apple.com/library/archive/samplecode/CryptoExercise/Introduction/Intro.html
I have a RSa publicKey value in base64 , how do i convert to SecKeyRef Object without adding to Keychain
Can i add a RSA Raw value to Keychain which is not in X509 format ???
thanks in advance
The following code comes from Apple's CryptoExercise example, in SecKeyWrapper.m. It assumes the "publicKey" NSData object is the binary DER-encoded ASN.1 object, not base-64 encoded. So you'll have to get a base-64 decoder and apply it first. You might also want to read this post in the Apple Developer Forums.
- (SecKeyRef)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKey {
OSStatus sanityCheck = noErr;
SecKeyRef peerKeyRef = NULL;
CFTypeRef persistPeer = NULL;
LOGGING_FACILITY( peerName != nil, #"Peer name parameter is nil." );
LOGGING_FACILITY( publicKey != nil, #"Public key parameter is nil." );
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]];
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass];
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag];
[peerPublicKeyAttr setObject:publicKey forKey:(id)kSecValueData];
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef];
sanityCheck = SecItemAdd((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer);
// The nice thing about persistent references is that you can write their value out to disk and
// then use them later. I don't do that here but it certainly can make sense for other situations
// where you don't want to have to keep building up dictionaries of attributes to get a reference.
//
// Also take a look at SecKeyWrapper's methods (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)key
// & (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef.
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecDuplicateItem, #"Problem adding the peer public key to the keychain, OSStatus == %d.", sanityCheck );
if (persistPeer) {
peerKeyRef = [self getKeyRefWithPersistentKeyRef:persistPeer];
} else {
[peerPublicKeyAttr removeObjectForKey:(id)kSecValueData];
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef];
// Let's retry a different way.
sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&peerKeyRef);
}
LOGGING_FACILITY1( sanityCheck == noErr && peerKeyRef != NULL, #"Problem acquiring reference to the public key, OSStatus == %d.", sanityCheck );
[peerTag release];
[peerPublicKeyAttr release];
if (persistPeer) CFRelease(persistPeer);
return peerKeyRef;
}
In case someone comes across this question and is looking for the Cocoa answer:
NSData *privateKeyPEMData = [privateKeyPEM dataUsingEncoding:NSUTF8StringEncoding];
CFArrayRef imported = NULL;
SecKeychainRef importedKeychain = NULL;
OSStatus err = 0;
SecExternalFormat format = kSecFormatOpenSSL;
SecKeyRef privateKey = NULL;
[privateKeyString dataUsingEncoding:NSUTF8StringEncoding];
err = SecItemImport((__bridge CFDataRef)(privateKeyPEMData), (CFStringRef)#"pem", &format, NULL, kNilOptions, kNilOptions, importedKeychain, &imported);
NSLog(#"%# ERROR: %#", self.class, [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil]);
assert(err == errSecSuccess);
assert(CFArrayGetCount(imported) == 1);
privateKey = (SecKeyRef)CFArrayGetValueAtIndex(imported, 0);