OSX Generated key can't encrypt (SecKeyCreateRandomKey & SecKeyCreateEncryptedData) - ios

I am basically follow this guide to generated a private key, copy the public key, and then encrypt a message. However, it gives me the error (OSStatus error -67712 - CSSM Exception: -2147415791 CSSMERR_CSP_INVALID_KEY_REFERENCE).
Initially, I thought I set the attributes incorrectly. However, if I create the public key (with the same attributes) by the SecKeyGeneratePair() function, everything works perfectly. Is it weird?
void TestEncryptDecrpt() {
OSStatus status;
NSData* tag = [#"com.example.keys.mykey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #1024,
(id)kSecPrivateKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
// *** it will work if I generate the key by SecKeyGeneratePair ***
// status = SecKeyGeneratePair( (__bridge CFDictionaryRef)attributes, &publicKey, &privateKey );
// start encrypt and decrypt a message
static char const kMessage[] = "This is a secret!\n";
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionRaw;
BOOL canEncrypt = SecKeyIsAlgorithmSupported(publicKey, kSecKeyOperationTypeEncrypt, algorithm);
NSData* plainData = [NSData dataWithBytes:kMessage length:sizeof(kMessage)];
canEncrypt &= ([plainData length] < (SecKeyGetBlockSize(publicKey)-130));
NSData* cipherText = nil;
if (canEncrypt) {
CFErrorRef error = NULL;
cipherText = (NSData*)CFBridgingRelease( SecKeyCreateEncryptedData(publicKey, algorithm, (__bridge CFDataRef)plainData, &error));
if (!cipherText) {
NSError *err = CFBridgingRelease(error); // ARC takes ownership
// Handle the error. . .
NSLog(#"error = %#, %#", [err userInfo], [err localizedDescription]);
}
}
}

Problem solved. You need the "kSecAttrIsPermanent" property as well in the public key setting.
Not sure why this is not mentioned in the example.

Related

Objective C- RSA digital signature from objective c sent to JAVA server can't be verified

Im generating and signing data with RSA private key from objective C,
and sending signed data with string RSA public key to JAVA server,
then the server must be verifying the signed data with the public key I provided,
but it always fails.
Can anyone tell me what is wrong with the codes on Objective C side.
Here's how I'm generating RSA key pairs
KeyChainWrapper* keychainTemp = [[KeyChainWrapper alloc] initWithService:[NSString stringWithFormat:#"SS%#TEMP", KEYCHAINSERVICEID] withGroup:nil];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #2048
};
CFErrorRef cfError = NULL;
SecKeyRef privateKeySec = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &cfError);
if (!privateKeySec) {
CFBridgingRelease(cfError);
return;
}
SecKeyRef publicKeySec = SecKeyCopyPublicKey(privateKeySec);
NSData* dataPublicKey = (__bridge_transfer NSData *)SecKeyCopyExternalRepresentation(publicKeySec, &cfError);
NSData* dataPrivateKey = (__bridge_transfer NSData *)SecKeyCopyExternalRepresentation(privateKeySec, &cfError);
if (dataPublicKey && dataPrivateKey) {
[keychainTemp insert:[NSString stringWithFormat:#"%#RSAPUBKEYTEMP", strType] DATA:dataPublicKey];
[keychainTemp insert:[NSString stringWithFormat:#"%#RSAPRIKEYTEMP", strType] DATA:dataPrivateKey];
}
if (publicKeySec) { CFRelease(publicKeySec); }
if (privateKeySec) { CFRelease(privateKeySec); }
then convert dataPublicKey to strPublicKey to send to the server,
and here's how I sign the data.
strRsaSign = [self sha256HashFor:[NSString stringWithFormat:#"%#%#%#%#", userId, systemId, [self SSGET_UNIQUE_DEVICE], strPublicKey]];
NSData* dataStrRSASign = [strRsaSign dataUsingEncoding:NSUTF8StringEncoding];
size_t signatureBytesSize = SecKeyGetBlockSize(privateKey);
uint8_t *signatureBytes = (uint8_t *)malloc(signatureBytesSize * sizeof(uint8_t));
memset((void *)signatureBytes, 0x0, signatureBytesSize);
OSStatus status = SecKeyRawSign(privateKey,
kSecPaddingPKCS1,
(unsigned char *)dataStrRSASign.bytes,
dataStrRSASign.length,
signatureBytes,
&signatureBytesSize
);
base64StrRSASign = [[NSData dataWithBytes:signatureBytes length:signatureBytesSize] base64EncodedStringWithOptions:0];
And then, I send the base64StrRSASign and strPublicKey to the server,
to let server verifies the base64StrRSASign with strPublicKey.
What can I possibly be missing here if anyone can tell me...

Why SecItemAdd return me -50 (invalid params)

I want to store in the keychain the value "MyKeyValue" and I do like this :
NSData* key = [#"MyKeyValue" dataUsingEncoding:NSUTF8StringEncoding];
NSData* tag = [#"com.example.MyKey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* addquery = #{ (id)kSecValueRef: key,
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrApplicationTag: tag,
};
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
but this failed with error -50 (Invalid params)
What i did wrong ?
I would like to store in the keychain a string that can be retrieved if the user uninstall and reinstall my app.
The error is occurring because of kSecValueRef, as per Apple's guideline kSecValueRef accepts a cryptographic key which can be generated through SecKeyRef, Please find below,
NSData* tag = [#"com.example.keys.mykey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #2048,
(id)kSecPrivateKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes,
&error);
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
NSDictionary* addquery = #{ (id)kSecValueRef: (__bridge id)publicKey,
(id)kSecClass: (id)kSecClassKey,
(id)kSecAttrApplicationTag: tag,
};
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)addquery, NULL);
For more info please refer Storing Keys in the Keychain

Printing SecKeyRef reference using NSLog in Objective-C

I am retrieving public key from a certificate with the following code.
- (SecKeyRef) extractPublicKeyFromCertificate: (SecIdentityRef) identity {
// Get the certificate from the identity.
SecCertificateRef myReturnedCertificate = NULL;
OSStatus status = SecIdentityCopyCertificate (identity,
&myReturnedCertificate);
if (status) {
NSLog(#"SecIdentityCopyCertificate failed.\n");
return NULL;
}
SecKeyRef publickey;
SecCertificateCopyPublicKey(myReturnedCertificate, &publickey);
NSLog(#"%#", publickey);
return publickey;
}
I am trying to print the "publickey" variable to see the contents. I am getting runtime error. I would like to know how to print the contents of the "publickey" variable?

RSA/ECB/PKCS1Padding iOS encryption

I am currently stuck at a problem which involves encryption in iOS.
My client has given me the public key,
"-----BEGIN PUBLIC KEY-----
xxxx
-----END PUBLIC KEY-----"
The padding strategy that needs to be used is RSA/ECB/PKCS1Padding.
With android, it seems pretty straight forward
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plain.getBytes());
return encryptedBytes;
I dont see any direct methods to do this in iOS. Any of the common pods used like Commoncrypto doesnt allow me to force PKCS1 padding scheme. Being a pretty inexperienced guy with RSA and encryption, it would be very much appreciated if you could help me understand on how to approach this and guide me through this.
Using standard Security Framework - SecKeyEncrypt with kSecPaddingPKCS1 parameter
My issue was solved using non padding :
kSecPaddingNone
-(SecKeyRef)getPublicKeyForEncryption
{
NSString *thePath = [MAuthBundle pathForResource:#"certificate" ofType:#"der"];
//2. Get the contents of the certificate and load to NSData
NSData *certData = [[NSData alloc]
initWithContentsOfFile:thePath];
//3. Get CFDataRef of the certificate data
CFDataRef myCertData = (__bridge CFDataRef)certData;
SecCertificateRef myCert;
SecKeyRef aPublicKeyRef = NULL;
SecTrustRef aTrustRef = NULL;
//4. Create certificate with the data
myCert = SecCertificateCreateWithData(NULL, myCertData);
//5. Returns a policy object for the default X.509 policy
SecPolicyRef aPolicyRef = SecPolicyCreateBasicX509();
if (aPolicyRef) {
if (SecTrustCreateWithCertificates((CFTypeRef)myCert, aPolicyRef, &aTrustRef) == noErr) {
SecTrustResultType result;
if (SecTrustEvaluate(aTrustRef, &result) == noErr) {
//6. Returns the public key for a leaf certificate after it has been evaluated.
aPublicKeyRef = SecTrustCopyPublicKey(aTrustRef);
}
}
}
return aPublicKeyRef;
}
-(NSString*) rsaEncryptString:(NSString*) string
{
SecKeyRef publicKey = [self getPublicKeyForEncryption];
NSData* strData = [string dataUsingEncoding:NSUTF8StringEncoding];
CFErrorRef err ;
NSData * data = CFBridgingRelease(SecKeyCreateEncryptedData(publicKey, kSecKeyAlgorithmRSAEncryptionPKCS1, ( __bridge CFDataRef)strData, &err));
NSString *base64EncodedString = [data base64EncodedStringWithOptions:0];
return base64EncodedString;
}

sign data using rsa private key

i know is christmas but i have a huge problem that i need to solve, and i'm looking for my christmas miracle here...
I have read apples documentation, and there were only guides how to create RSA public and private keys from certificates. In my case, i have only RSA private key in .pem file. So my question is his: how should i sign data, using that key? I dont want to use openssl. i have tried it with no luck, and i think it's possible to sign data with RSA, by using apples API's.
This is how my key looks like:
-----BEGIN RSA PRIVATE KEY-----
..............................
-----END RSA PRIVATE KEY-----
This is what i have done so far:
-(NSString *)signing:(NSString *)dataString {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"PrestaMobilekey" ofType:#"pem"];
NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];
SecKeyRef privateKey = (__bridge SecKeyRef)(data);
uint8_t *signedHashBytes = NULL;
// calculate private key size
size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);
// create space to put signature
signedHashBytes = (uint8_t *)malloc(signedHashBytesSize * sizeof(uint8_t));
memset((void *)signedHashBytes, 0x0, signedHashBytesSize);
OSStatus status = NULL;
// sign data
status = SecKeyRawSign(privateKey,
kSecPaddingPKCS1SHA1,
[[[dataString dataUsingEncoding:NSUTF8StringEncoding] SHA1] bytes],
CC_SHA1_DIGEST_LENGTH,
signedHashBytes,
&signedHashBytesSize);
if (privateKey) {
CFRelease(privateKey);
}
// get signature hash
NSData *signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];
// release created space
if (signedHashBytes) {
free(signedHashBytes);
}
if (status != errSecSuccess) {
return #"";
}
// return Base64 encoded signature string
return [Base64 encode:signedHash];
}
I really hope that someone will help me, with some good information and answer.
Thank you.
You don't need to use OpenSSL. You can sign your data using your method with a few tweaks. I don't think you can simply bridge and cast an NSData object to a SecKeyRef. You most likely need to save it to the keychain first.
You can do so with this method:
- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *saveDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecValueData : key,
(__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
(__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
(__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
(__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
};
SecKeyRef savedKey = NULL;
sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey);
if (sanityCheck != errSecSuccess) {
LOGGING_FACILITY1(sanityCheck != noErr, #"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
}
return savedKey;
}
If you don't want to get the reference immediately, you can change the method type to void and remove the return statement. Change (CFTypeRef *)&savedKey to NULL.
You can then retrieve the saved key like so:
- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
OSStatus sanityCheck = noErr;
NSData *tag;
id keyClass;
if (isPrivate) {
if (privateKeyRef != NULL) {
// already exists in memory, return
return privateKeyRef;
}
tag = privateTag;
keyClass = (__bridge id) kSecAttrKeyClassPrivate;
}
else {
if (publicKeyRef != NULL) {
// already exists in memory, return
return publicKeyRef;
}
tag = publicTag;
keyClass = (__bridge id) kSecAttrKeyClassPublic;
}
NSDictionary *queryDict = #{
(__bridge id) kSecClass : (__bridge id) kSecClassKey,
(__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
(__bridge id) kSecAttrApplicationTag : tag,
(__bridge id) kSecAttrKeyClass : keyClass,
(__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
};
SecKeyRef keyReference = NULL;
sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
if (sanityCheck != errSecSuccess) {
NSLog(#"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
}
if (isPrivate) {
privateKeyRef = keyReference;
}
else {
publicKeyRef = keyReference;
}
return keyReference;
}
Also, an easier way to return a base64 encoded string is to do this:
NSString *signatureString = [signedHash base64EncodedStringWithOptions:nil];
About privateTag and publicTag
privateTag and publicTag are used to mark the kSecAttrApplicationTag which defines the application that uses this key. You want to have a separate privateTag and publicTag to differentiate between your private key and public key.
It's a bit convoluted because I followed the sample code, but I defined my privateTag and publicTag this way:
SecKeyWrapper.h
#define kPublicKeyTag "com.sample.app.publickey"
#define kPrivateKeyTag "com.sample.app.privatekey"
SecKeyWrapper.m
// just under #implementation or #synthesize lines
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;
- (id)init {
if (self = [super init]) {
// Tag data to search for keys.
privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
}
return self;
}
Then use the privateTag and publicTag as you would in the code samples I provided above.
Ok, o found a solution for this problem my self. I hope that this will help to others... this is what helped me. I thought that i could do this without openssl, but i was wrong. But still, by doing like in that post, you won't need an extra library in you project. Use terminal

Resources