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
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'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 have a NSString which is supposed to be a public key. I want to store it in the keychain and then get a SecKeyRef of it, in order to use it in other security related functions like SecKeyEncrypt, etc.
For storing I use the SecItemAdd supposing that I also have an identifier for the public key. I tried to get a persistent ref and then with this get a SecKeyRef with SecItemCopyMatching. I use the below two functions. Before I pass the string of key to putKey I converted it to NSData.
-(SecKeyRef)putKey:(NSData *)key withIdentifier:(NSString *)identifier
{
OSStatus status = noErr;
SecKeyRef keyRef = nil;
CFTypeRef persKey = nil;
NSData * identifierTag = [[NSData alloc] initWithBytes:(const void *)[identifier UTF8String] length:[identifier length]];
NSMutableDictionary *queryKey = [[NSMutableDictionary alloc] init];
[queryKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
[queryKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
[queryKey setObject:identifierTag forKey:(__bridge id)kSecAttrApplicationTag];
[queryKey setObject:key forKey:(__bridge id)kSecValueData];
[queryKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnPersistentRef];
status = SecItemAdd((__bridge CFDictionaryRef)queryKey, (CFTypeRef *)&persKey);
if (status == errSecDuplicateItem) NSLog(#"Key %# already exists in the KeyStore, OSStatus = %ld.", identifier, status);
else if (status != noErr) NSLog(#"Error putting key %# in KeyStore, OSStatus = %ld.", identifier, status);
keyRef = [self getKeyRefWithPersistentKeyRef:persKey];
return keyRef;
}
- (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef
{
OSStatus sanityCheck = noErr;
SecKeyRef keyRef = NULL;
if (persistentRef == NULL) NSLog(#"persistentRef object cannot be NULL.");
NSMutableDictionary * queryKey = [[NSMutableDictionary alloc] init];
// Set the SecKeyRef query dictionary.
[queryKey setObject:(__bridge id)persistentRef forKey:(__bridge id)kSecValuePersistentRef];
[queryKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
// Get the key reference.
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef)queryKey, (CFTypeRef *)&keyRef);
return keyRef;
}
The SecItemAdd returns successfully a persistent key ref.
But SecItemCopyMatching returns 0x0. Anyone knows why?
Checkout Apple's Crypto Exercise. In order to add a public key to the KeyChain, it is necessary to strip off the header that is attached to it. Apple's example shows doing just that, and the code is easy to reuse.
How can i create RSA encryption public key from 'Modulus' and 'Exponent' in iOS.?
I have created public key from keychain. is it possible from string 'Modulus' and 'Exponent' values?
See this answer over here
https://stackoverflow.com/a/10643894/584616
https://github.com/StCredZero/SCZ-BasicEncodingRules-iOS
SCZ-BasicEncodingRules-iOS
Implementation of Basic Encoding Rules to enable import of RSA keys to iOS
KeyChain using exponent. Code targets iOS 5 with ARC.
Let's say you already have a modulus and exponent from
an RSA public key as an NSData in variables named pubKeyModData and
pubKeyModData. Then the following code will create an NSData containing that RSA
public key, which you can then insert into the iOS or OS X Keychain.
NSMutableArray *testArray = [[NSMutableArray alloc] init];
[testArray addObject:pubKeyModData];
[testArray addObject:pubKeyExpData];
NSData *testPubKey = [testArray berData];
This would allow you to store the key using the addPeerPublicKey:keyBits: method from SecKeyWrapper in the Apple CryptoExercise example. Or, from the perspective of the low-level API, you can use SecItemAdd().
NSString * peerName = #"Test Public Key";
NSData * peerTag =
[[NSData alloc]
initWithBytes:(const void *)[peerName UTF8String]
length:[peerName length]];
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init];
[peerPublicKeyAttr
setObject:(__bridge id)kSecClassKey
forKey:(__bridge id)kSecClass];
[peerPublicKeyAttr
setObject:(__bridge id)kSecAttrKeyTypeRSA
forKey:(__bridge id)kSecAttrKeyType];
[peerPublicKeyAttr
setObject:peerTag
forKey:(__bridge id)kSecAttrApplicationTag];
[peerPublicKeyAttr
setObject:testPubKey
forKey:(__bridge id)kSecValueData];
[peerPublicKeyAttr
setObject:[NSNumber numberWithBool:YES]
forKey:(__bridge id)kSecReturnPersistentRef];
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer);
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);