I am trying to encrypt NSData with the following method:
- (NSData *) encryptWithData:(NSData *)content {
size_t plainLen = [content length];
void *plain = malloc(plainLen);
[content getBytes:plain
length:plainLen];
size_t cipherLen = 256;
void *cipher = malloc(cipherLen);
OSStatus returnCode = SecKeyEncrypt("PUBLIC KEY HERE", kSecPaddingPKCS1, plain,
plainLen, cipher, &cipherLen);
NSData *result = nil;
if (returnCode != 0) {
NSLog(#"SecKeyEncrypt fail. Error Code: %ld", returnCode);
}
else {
result = [NSData dataWithBytes:cipher
length:cipherLen];
}
free(plain);
free(cipher);
return result;
}
Where it is written "PUBLIC KEY HERE" I want to load an existing public key I already copied to my bundle. How can I do that?
For example use a certificate file which contains a public key:
NSData *certificateData = [NSData dataWithContentsOfURL:certificateURL options:0 error:&error];
if (certificateData) {
SecCertificateRef certificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)(certificateData));
// ...
SecKeyRef publicKey;
SecCertificateCopyPublicKey(certificate, &publicKey);
// ...
}
To load data from the bundle:
NSArray *certificateURLs = [[NSBundle mainBundle] URLsForResourcesWithExtension:#"cer" subdirectory:#"myCertificates"];
for (NSURL *certificateURL in certificateURLs) {
NSData *certificateData = [NSData dataWithContentsOfURL:certificateURL options:0 error:&error];
// ...
}
Related
I'm not an expert about this at all, so I've found a wrapper on github called RSA and tried to use it.
What I need to do is simply encrypt a string using my private key.
This is the method used to do the encryption:
- (NSString *)rsaEncryptWithData:(NSData*)data usingPublicKey:(BOOL)yes {
if (yes) {
[self getKeyRefFor:publicTag];
} else {
[self getKeyRefFor:privateTag];
}
}
SecKeyRef key = self.publicKeyRef;
size_t cipherBufferSize = SecKeyGetBlockSize(key);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
memset((void *)cipherBuffer, 0*0, cipherBufferSize);
NSData *plainTextBytes = data;
size_t blockSize = cipherBufferSize - 11;
size_t blockCount = (size_t)ceil([plainTextBytes length] / (double)blockSize);
NSMutableData *encryptedData = [NSMutableData dataWithCapacity:0];
for (int i=0; i<blockCount; i++) {
int bufferSize = (int)MIN(blockSize,[plainTextBytes length] - i * blockSize);
NSData *buffer = [plainTextBytes subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt(key,
kSecPaddingPKCS1,
(const uint8_t *)[buffer bytes],
[buffer length],
cipherBuffer,
&cipherBufferSize);
if (status == noErr){
NSData *encryptedBytes = [NSData dataWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
[encryptedData appendData:encryptedBytes];
}else{
if (cipherBuffer) {
free(cipherBuffer);
}
return nil;
}
}
if (cipherBuffer) free(cipherBuffer);
return [encryptedData base64EncodedStringWithOptions:0];
}
If I use my public key to encrypt, everything is working fine, but if I try to use my private key then the OSStatus variable returns a -4 (Function or operation not implemented.).
Any help would be really appreciated, since I really don't know what to do.
Thanks.
I ended up using the MIHCrypto framework to create a key pair and to sign my messages.
Is this easy:
- (NSData *)signMessageData:(NSData *)data withKey:(MIHRSAPrivateKey *)privKey {
NSError *signingError = nil;
NSData *signatureData = [privKey signWithSHA256:data error:&signingError];
if (signingError != nil) {
NSLog(#"Error: %#", signingError.localizedDescription);
}
return signatureData;
}
I have googling a lot, but any answer help me with this problem:
Code:
MAIN DECRYPT in XRSA.m
- (NSData *) decryptWithString:(NSString *)content {
return [self RSADecryptData:[content dataUsingEncoding:NSUTF8StringEncoding]];
}
LOAD PRIVATE KEY .p12 in XRSA.m
#pragma mark - Private Key (.p12)
-(BOOL)setPrivateKey:(NSString *)privateKeyPath withPassphrase:(NSString *)password{
NSData *pkcs12key = [NSData dataWithContentsOfFile:privateKeyPath];
NSDictionary* options = NULL;
CFArrayRef importedItems = NULL;
if (password) {
options = [NSDictionary dictionaryWithObjectsAndKeys: password, kSecImportExportPassphrase, nil];
}
OSStatus returnCode = SecPKCS12Import((__bridge CFDataRef) pkcs12key,
(__bridge CFDictionaryRef) options,
&importedItems);
if (returnCode != 0) {
NSLog(#"SecPKCS12Import fail");
return FALSE;
}
NSDictionary* item = (NSDictionary*) CFArrayGetValueAtIndex(importedItems, 0);
SecIdentityRef identity = (__bridge SecIdentityRef) [item objectForKey:(__bridge NSString *) kSecImportItemIdentity];
SecIdentityCopyPrivateKey(identity, &privateKey);
if (privateKey == nil) {
NSLog(#"SecIdentityCopyPrivateKey fail");
return FALSE;
}
return TRUE;
}
Decrypt message in XRSA.m
#pragma mark - RSA Decryption
-(NSData *)RSADecryptData:(NSData *)content{
NSAssert(privateKey != nil,#"Private key can not be nil");
size_t cipherLen = content.length;
void *cipher = malloc(cipherLen);
[content getBytes:cipher length:cipherLen];
size_t plainLen = SecKeyGetBlockSize(privateKey) - 12;
void *plain = malloc(plainLen);
//SecKeyDecrypt(<#SecKeyRef key#>, <#SecPadding padding#>, <#const uint8_t *cipherText#>, <#size_t cipherTextLen#>, <#uint8_t *plainText#>, <#size_t *plainTextLen#>)
OSStatus returnCode = SecKeyDecrypt(privateKey, kSecPaddingPKCS1, cipher,cipherLen, plain, &plainLen);
NSData *result = nil;
if (returnCode != 0) {
NSLog(#"SecKeyDecrypt fail. Error Code: %d", (int)returnCode);
}
else {
result = [NSData dataWithBytes:plain
length:plainLen];
}
free(plain);
free(cipher);
return result;
}
in ViewControler.m:
NSString *privatekeyPath = [[NSBundle mainBundle] pathForResource:#"private_key" ofType:#"p12"];
XRSA *rsa2 = [XRSA alloc];
if([rsa2 setPrivateKey:privatekeyPath withPassphrase:#"Xs23tg"]){
NSString *data = #"UKFpmRmyu1TUZLqcgHmCEGnHaT7+0j5fAaf57xzVR2/j/Qe0j+b5Lez7wya3jlARfzRuHSSZctsGs4gK2JX2LEqHmQLX2zRhLSSzyMlLnYPF8X4pjbDY5agjPlWf4FpFJnmwGr2XjdqRJzPZ9NvEJAns5dNKAh0lQ3nc3kDppfg=";
[rsa2 decryptWithString:data];
}
else{
}
In RSADecryptData fuction, OSStaus is always return error code -9809.
Any ideas?
Thanks for your time.
There are a couple of possibilities:
In the line [content getBytes:cipher length:cipherLen]; you are not assigning that result to anything. Perhaps assign it to a const uint8_t * and pass into the SecKeyDecrypt function instead of content.
You should check to ensure that the cipherLen is less than the plainLen value. You didn't mention your key length, but that could be the cause of the failure. If you need to support larger message, you will need to decrypt in smaller chunk and iterate over your cipher.
I'm using this code: https://stackoverflow.com/a/19221754/849616, however not everything is clear for me.
I want to encrypt NSString *msg = "0000" using public key NSString *pubKey = "1111". Because of this, I'm updating constants:
static const UInt8 publicKeyIdentifier[] = 1111;
// i want to encrypt only, so private key doesn't matter and I'm not posting it here
In function testAsymmetricEncryptionAndDecryption I've updated:
const char inputString[] = 0000
However the result is wrong. Is publicKeyIdentifier a right place to put my key string..? How should I do it if my approach is wrong..?
Well the question is wrong. I shouldn't even try to convert it to NSString.
You should put both keys to your project and use something like:
- (SecKeyRef)getPrivateKeyRef {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"rsaPrivate" ofType:#"p12"];
NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath];
NSMutableDictionary *options = [[NSMutableDictionary alloc] init];
SecKeyRef privateKeyRef = NULL;
//change to the actual password you used here
[options setObject:#"!##EWQ" forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef)p12Data, (__bridge CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
CFRelease(items);
return privateKeyRef;
}
- (SecKeyRef)getPublicKeyRef {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"rsaCert" ofType:#"der"];
NSData *certData = [NSData dataWithContentsOfFile:resourcePath];
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
SecKeyRef key = NULL;
SecTrustRef trust = NULL;
SecPolicyRef policy = NULL;
if (cert != NULL) {
policy = SecPolicyCreateBasicX509();
if (policy) {
if (SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust) == noErr) {
SecTrustResultType result;
if (SecTrustEvaluate(trust, &result) == noErr) {
key = SecTrustCopyPublicKey(trust);
}
}
}
}
if (policy) CFRelease(policy);
if (trust) CFRelease(trust);
if (cert) CFRelease(cert);
return key;
}
I didn't write it all by my own (just modified), it's mostly copied but really I have no idea where from - some open source community. Still, many thanks to the person who wrote it.
I implemented a category method on the NSData class which returns a signature of the data using an SHA-1 hash and subsequent encryption with a private key as follows:
- (NSData *)signatureWithKey:(SecKeyRef)keyRef {
if (keyRef == NULL) {
return nil;
}
NSData *sha1Digest = [self dataWithSHA1Digest];
size_t maxLength = SecKeyGetBlockSize(keyRef) - 11;
if ([sha1Digest length] > maxLength) {
NSString *reason = [NSString stringWithFormat:#"Digest is too long to sign with this key, max length is %ld and actual length is %ld", maxLength, (unsigned long)[self length]];
NSException *ex = [NSException exceptionWithName:#"BMInvalidArgumentException" reason:reason userInfo:nil];
#throw ex;
}
#if TARGET_OS_IPHONE
OSStatus status = noErr;
uint8_t *plainBuffer = (uint8_t *)[sha1Digest bytes];
size_t plainBufferSize = [sha1Digest length];
size_t cipherBufferSize = SecKeyGetBlockSize(keyRef);
uint8_t *cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));
status = SecKeyRawSign(keyRef,
kSecPaddingPKCS1SHA1,
plainBuffer,
plainBufferSize,
&cipherBuffer[0],
&cipherBufferSize
);
if (status == noErr) {
return [NSData dataWithBytesNoCopy:cipherBuffer length:cipherBufferSize freeWhenDone:YES];
}
free(cipherBuffer);
return nil;
#else
CFErrorRef error = NULL;
SecTransformRef signer = NULL;
CFTypeRef signature = NULL;
if ((signer = SecSignTransformCreate(keyRef, &error))) {
if (SecTransformSetAttribute(
signer,
kSecTransformInputAttributeName,
(CFDataRef)sha1Digest,
&error)) {
signature = SecTransformExecute(signer, &error);
}
}
if (error) {
LogWarn(#"Could not sign: %#", error);
CFRelease(error);
}
if (signer) {
CFRelease(signer);
}
if (signature) {
NSData *data = [NSData dataWithData:(NSData *)signature];
CFRelease(signature);
return data;
} else {
return nil;
}
#endif
}
Now the strange thing is that with the same private key (loaded from a p12 file) I get two different results for iOS and MacOSX when signing the same data. I am completely puzzled by this. You may notice the method above uses a different implementation for MacOSX using security transforms, but even if I use the iOS implementation on MacOSX (which gives a compile warning but works fine) I get the same result.
The method used for loading the private key from file is below:
+ (SecKeyRef)newPrivateKeyRefWithPassword:(NSString *)password fromData:(NSData *)data {
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
SecKeyRef privateKeyRef = NULL;
// Set the public key query dictionary
//change to your .pfx password here
[options setObject:password forKey:(id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((CFDataRef)data,
(CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp =
(SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
[options release];
if (items) CFRelease(items);
return privateKeyRef;
}
And this is the test case I use. Notice that two different strings are printed on iOS and MacOSX:
NSString *test = #"bla";
NSData *testData = [test dataUsingEncoding:NSUTF8StringEncoding];
NSString *p12Path= [[NSBundle mainBundle] pathForResource:#"private_key" ofType:#"p12"];
NSData *p12Data = [NSData dataWithContentsOfFile:p12Path];
SecKeyRef keyRef = [BMSecurityHelper newPrivateKeyRefWithPassword:#"xxxxxxxx" fromData:p12Data];
NSData *signatureData = [testData signatureWithKey:keyRef];
NSString *signatureString = [BMEncodingHelper base64EncodedStringForData:signatureData withLineLength:0];
if (keyRef) CFRelease(keyRef);
NSLog(#"signatureString: %#", signatureString);
It's always nice if you can answer your own question. I missed the following: under MacOSX the security transform also calculates the SHA-1 hash automatically, in contrast with the iOS implementation.
I fixed the problem by adding the following in the MacOSX implementation:
SecTransformSetAttribute(signer, kSecInputIsAttributeName, kSecInputIsDigest, &error)
I have two scenarios of RSA encryption:
Key pair generated by c# app. Public key sent to iOS. iOS app encrypts data with the key and sends it back. The c# app decrypts using the private key. --- Completely implemented and working.
Key pair generated by ios app. Public key sent to c# app. The c# app encrypts the data, base 64 encodes it and sends it to the ios app. The ios app then decrypts the data using the private key. --- Not working.
If I encrypt using the public and private key pair generated on ios it all works, but as soon as I replace it with the base 64 encoded string from c# it fails. I have tried all combinations of padding.
The code is as follows:
SecPadding padding = kSecPaddingPKCS1;
NSMutableArray *keys = [self generateRSAKeyWithKeySizeInBits:1024 publicKeyTag:#"RSA Public Key" privateKeyTag:#"RSA Private Key"];
SecKeyRef test_publicKey = (__bridge SecKeyRef)(keys[0]);
SecKeyRef test_privateKey = (__bridge SecKeyRef)(keys[1]);
const char *sampled_utf8 = "VnWBW/xRyJB48Uxjdl99apczCuS07zhnLvIjnqyZIQqbI4F7kyAezfD1MNlgeTefkHuCRuzogaQTamk2XRwXoBoGy3Agj4ocPK2Wa7vWNGip8X3FAo1eJL+xKoVoqre/ipDjnZNfEUbX91Ru+IqWkbZXD2POlFfuMaTatCl50+U=";
NSString *sampled = [[NSString alloc] initWithUTF8String:sampled_utf8];
Byte sampled_inputData [[sampled lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[sampled dataUsingEncoding:NSUTF8StringEncoding] getBytes:sampled_inputData];//get the pointer of the data
size_t sampled_inputDataSize = (size_t)[sampled length];
size_t sampled_outputDataSize = EstimateBas64DecodedDataSize(sampled_inputDataSize);//calculate the decoded data size
Byte sampled_outputData[sampled_outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(sampled_inputData, sampled_inputDataSize, sampled_outputData, &sampled_outputDataSize);//decode the data
NSData *sampled_Data = [[NSData alloc] initWithBytes:sampled_outputData length:sampled_outputDataSize];//create a NSData
NSData *test_encrypted_Data = [self encryptString:encoded_toEncrypt RSAPublicKey:test_publicKey padding:padding];
size_t test_encrypted_inputDataSize = (size_t)[test_encrypted_Data length];
Byte *test_encrypted_inputData = (Byte*) malloc(test_encrypted_inputDataSize);
memcpy(test_encrypted_inputData,[test_encrypted_Data bytes],test_encrypted_inputDataSize);
size_t test_encrypted_outputDataSize = EstimateBas64EncodedDataSize(test_encrypted_inputDataSize);//calculate the encoded data size
char* test_encrypted_outputData[test_encrypted_outputDataSize];//prepare a char for the encoded data
Base64EncodeData(test_encrypted_inputData, test_encrypted_inputDataSize, test_encrypted_outputData, &test_encrypted_outputDataSize,false);//encode the data
NSData *test_encrypted_Encoded = [[NSData alloc] initWithBytes:test_encrypted_outputData length:test_encrypted_outputDataSize];//create a NSData object from the decoded data
size_t input_Size = (size_t)[test_encrypted_Encoded length];
Byte *input = (Byte*) malloc(input_Size);
memcpy(input,[test_encrypted_Encoded bytes],input_Size);
size_t output_Size = EstimateBas64DecodedDataSize(input_Size);
char* output[output_Size];
Base64DecodeData(input, input_Size, output, &output_Size);
NSData *res = [[NSData alloc] initWithBytes:output length:output_Size];
NSData *test_decryptedData = [self decryptString:sampled_Data RSAPrivateKey:test_privateKey padding:padding];
-(NSData*)decryptString:(NSData*)original RSAPrivateKey:(SecKeyRef)privateKey padding: (SecPadding)padding
{
#try
{
const unsigned char* original_String = (unsigned char *)[original bytes];
size_t decryptedLength = SecKeyGetBlockSize(privateKey);
uint8_t decrypted[decryptedLength];
OSStatus status = SecKeyDecrypt(privateKey,
padding,
original_String,
[original length],
decrypted,
&decryptedLength);
NSLog(#"result = %#", [self fetchStatus:status]);
if(status == noErr)
{
NSData* decryptedData = [[NSData alloc] initWithBytes:(const void*)decrypted length:decryptedLength];
return decryptedData;
}
else
return nil;
}
#catch (NSException * e)
{
//do nothing
NSLog(#"exception: %#", [e reason]);
}
return nil;
}
- (NSData*)encryptString:(NSString*)original RSAPublicKey:(SecKeyRef)publicKey1 padding:(SecPadding)padding
{
#try
{
Byte encrypt_inputData [[original lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];//prepare a Byte[]
[[original dataUsingEncoding:NSUTF8StringEncoding] getBytes:encrypt_inputData];//get the pointer of the data
size_t encrypt_inputDataSize = (size_t)[original length];
size_t encrypt_outputDataSize = EstimateBas64DecodedDataSize(encrypt_inputDataSize);//calculate the decoded data size
Byte encrypt_outputData[encrypt_outputDataSize];//prepare a Byte[] for the decoded data
Base64DecodeData(encrypt_inputData, encrypt_inputDataSize, encrypt_outputData, &encrypt_outputDataSize);//decode the data
NSData *encryption_Data = [[NSData alloc] initWithBytes:encrypt_outputData length:encrypt_outputDataSize];//create a NSData object from the decoded data
size_t encryptedLength = SecKeyGetBlockSize(publicKey1);
uint8_t* sample = (uint8_t*)[encryption_Data bytes];
size_t text_Size = [encryption_Data length];
uint8_t *encrypted_Data_Bytes;
encrypted_Data_Bytes = malloc(sizeof(uint8_t)*encryptedLength);
memset(encrypted_Data_Bytes,0,encryptedLength);
OSStatus status = SecKeyEncrypt(publicKey1,
padding,
sample,
text_Size,
&encrypted_Data_Bytes[0],
&encryptedLength);
if(status == noErr)
{
NSData* encryptedData = [[NSData alloc] initWithBytes:(const void*)encrypted_Data_Bytes length:encryptedLength];
return encryptedData;
}
else
return nil;
}
#catch (NSException * e)
{
//do nothing
NSLog(#"exception: %#", [e reason]);
}
return nil;
}