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];
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 need to perform AES 256 decryption in my iphone app. The scenario is that the plain text is base64 encoded and encrypted using AES256 bit (OFB mode) in an online webpage.
In my app, I retrieve the encrypted text from that webpage as a query string. Here I have done base64 decoding and AES256 Decryption.
But I am getting -4304 status.
I have used kCCOptionPKCS7Padding. Even if I change the padding also, I am not getting proper Decrypted Plain text. Only unreadable text is displayed.
I have used http://isv.appspot.com/app/enc for checking the AES256 encryption with base64 by setting OFB mode.
Following code has two methods which I used for base64 decoding and AES256 decryption
+ (NSString*)decryptBase64String:(NSString*)encryptedBase64String keyString:(NSString*)keyString
{
NSData* encryptedData = [NSData dataFromBase64String:encryptedBase64String];
NSLog(#"encryptedData %#",encryptedData);
// NSData *strData = [encryptedData subdataWithRange:NSMakeRange(0, [encryptedData length] - 2)];
// NSString* newStr = nil;
NSData* keyData = [keyString dataUsingEncoding:NSUTF8StringEncoding];
NSData* data = [self decryptData:encryptedData
key:keyData
iv:nil];
// newStr = [NSString stringWithCString:[strData bytes] encoding:NSUTF8StringEncoding];
if (data) {
return [[[NSString alloc] initWithData:data
encoding:NSASCIIStringEncoding] autorelease];
} else {
return nil;
}
}
+ (NSData*)decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv;
{
NSData* result = nil;
// setup key
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:FBENCRYPT_KEY_SIZE];
// setup iv
char cIv[FBENCRYPT_BLOCK_SIZE];
bzero(cIv, FBENCRYPT_BLOCK_SIZE);
if (iv) {
[iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
}
// setup output buffer
size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
void *buffer = malloc(bufferSize);
// do decrypt
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
FBENCRYPT_ALGORITHM,
kCCOptionPKCS7Padding,
cKey,
FBENCRYPT_KEY_SIZE,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&decryptedSize);
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(#"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus);
}
return result;
}
Could anyone help me out to get the plain text by using this method?
Based on the facts in your question, the base64 encoded data is encrypted, not the decoded variation of that data. Because of this, you need to decrypt the base64 encoded data and then base64 decode it.
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
I've created a pair of keys using SecKeyGeneratePair. I'd now like to pass the public key to a server, but I'm not really sure how to proceed.
I have a function getPublicKeyBits (taken from Apple's CryptoExercise), but I don't really know what to do with the raw NSData. Here is the function:
- (NSData *)getPublicKeyBits {
OSStatus sanityCheck = noErr;
NSData* publicKeyBits = nil;
NSData* publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
CFDataRef cfresult = NULL;
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*)&cfresult);
if (sanityCheck != noErr)
{
publicKeyBits = nil;
}
else
{
publicKeyBits = (__bridge_transfer NSData *)cfresult;
}
return publicKeyBits;
}
How do I take this raw byte data and turn it into something like PEM or some other format that a crypto library understands? Should I base64 encode it? Are there other things I need to do as well?
If it helps, I'm trying to use the public key with the M2Crypto library available for Python.
I think you will want to look at http://www.openssl.org/docs/crypto/pem.html#
maybe:
int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
unsigned char *kstr, int klen,
pem_password_cb *cb, void *u);
This page has some great tips and sample code for packaging the data you have into the PEM format so you can send it to a server:
http://blog.wingsofhermes.org/?p=42
You don't need the whole openssl library compiled from source and statically linked to do it. I'm using just this technique, wrapping the base 64 key in "-----BEGIN PUBLIC KEY-----" and it can be read and used by a Rails application using the standard ruby openssl classes.
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