RSA decryption fails in iOS - ios

I'm trying to do RSA2048 in iOS and am following the example codes from Apple and also this question RSA implementations in Objective C. I have tested on iPhone 5c with iOS 8.4.1, but the sample codes fail at decryption with private key, with error code -9809 (An underlying cryptographic error was encountered), even though encryption with public key. I understand the basic approach is to generate an RSA key pair, secure them in keychain and use public key ref to encrypt and private key to decrypt. I'm completely lost why decryption shall fail, and not always, there are times when decryption succeeded.
Full codes can be found at https://gist.github.com/aceisScope/372e6d6f92650ce03624. The decryption part that throws an error is below, where from time to time status = -9809, and other times it works and returns 0:
status = SecKeyDecrypt(privateKey,
PADDING,
cipherBuffer,
cipherBufferSize,
plainBuffer,
&plainBufferSize
);
I have also set a check that if such key pair has already generated, next time encryption/decryption is called, it will directly using the already-generated-and-stored key pair from key chain without generating a new pair.
Update:
I came across this post iPhone Public-Key Encryption SecKeyEncrypt returns error 9809 (errSSLCrypto) which found out wrong cipher buffer size may cause -9809 error to encryption. Yet even if I make sure both the cipher buffer size in encryption and plain buffer size in decryption is the same as key block size and private key block size, encryption always works but with decryption failing from time to time.

I found the problem. By the end of encryption, when converting cipher buffer to NSData, in the following code
NSMutableData *data=[[NSMutableData alloc] init];
[data appendBytes:cipherBuffer length:strlen( (char*)cipherBuffer ) + 1];
the length is not correct. It should be the size of the cipher buffer, which is the same as key block size.
So after changing it to
NSData *data = [NSData dataWithBytes:cipherBuffer length:cipherBufferSize];
decryption works now.

Related

AES128 Encryption CBC/NoPadding Objective-C

We need to encrypt a request using AES128 in Android and IOS and then send that encrypted message in the backend server written in Java.
Our Android encryption code is like below:
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
where keyspec and ivspec is random bytes generated.
In Objective-C, this is how we do the encryption.
NSString* iv = #"a12bc1256b4de9a0";
NSData* ivData = [iv dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* cipherData = [NSMutableData dataWithLength:data.length+kCCBlockSizeAES128];
CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, keyData.bytes, keyData.length, ivData.bytes, data.bytes, data.length, cipherData.mutableBytes, cipherData
.length, &outLength);
The problem with this is that when we compare the encrypted byte of thee Java program and Objective-C, they are not the same. I understand that the CCOption parameter in Objective-C should be CBC but that is not in the enum list of the CommonCrypto library. When we set it 0, the encrypted byte only return a series of zeros.
Please suggest other alternatives on how to do the AES 128 encryption in Objective-C using AES/CBC/NOPadding Algorithm.
You've requested padding: kCCOptionPKCS7Padding. That's not the same thing as Java's NoPadding. Remove the padding option. (You can use 0 to mean "no options.")
It's also unclear whether every other part of your encryption it the same. You didn't include the key generation or IV in the Java code.
(Note that if you get the exact same bytes out of an encryption algorithm for the same message, then you're using the encryption algorithm in an insecure way. Secure encryption constructions will generate a different cipher text for every encryption. I understand that your server may be using this kind of insecure approach; it's a very common mistake. But it is insecure.)

RSA Key generation with SecKey Xamarin.iOS

I'm trying to generate a public (and private) key pair using the SecKey class from Xamarin.iOS.
The KeySize is defined to 1024 bit and this seems to work (if I change this value, the length of the result array is changing too).
I generate the keys with
SecKey.GenerateKeyPair(CreateRsaParams(), out publicKey, out privateKey);
byte[] key = publicKey.GetExternalRepresentation().ToArray()
(CreateRsaParams() is a function giving back a NSDictionary with the required data)
The problem is: I get a byte array (public key) with 140 Bytes - but depended on the key size it should have only 128 Byte - and I need a 128 Byte public key for data exchange with an other system
(by the way - using PCLCrypto is not an option for me since the project is not allowed to use this 3rd party component)
Does anyone know the problem and know a solution?
Okay, problem solved.
If anyone is facing the same problem, you can find the solution at
https://forums.developer.apple.com/thread/111109
The problem was not the key, but the wrongly formulated requirement.

compare two secKey (public keys) in ios Swift

I want to ssl public key pinning in swift, I read lot of examples how to do that, last think who I can't find is How to compare two public keys in SecKey object format.
Example:
let serverPublicKey = SecTrustCopyPublicKey(secTrust) /*return SecKey object from actual SecTrust*/
let clientPublicKey = getLocalPublicKeyFromDer() /*return SecKey from .der local*/
how to compare them? At now I do that and it works:
if(serverPublicKey! as AnyObject).isEqual(clientPublicKey){
/*Key is the same, pinning OK!*/
}
find it way on gitHub: https://github.com/teamcarma/IOS-AlamofireDomain/blob/master/Source/ServerTrustPolicy.swift
but is cast to AnyObject a good idea? How to work isEqual on casted SecKey? Can any explain me?
ps.
Another idea is getting base64 from SecKey - I try and it also works, but it require a KeyChain temp operations and look no profesional.
Cited from the headers:
"Most SecKeychainItem functions will work on an SecKeyRef."*
You may cast SecKeyRef to a SecKeychainItem. If this is a valid operation (that is, the key is a keychain item), you may apply function
SecKeychainItemCreatePersistentReference
and get a CFData object, filled with attributes and data. Then check with memcpyon the bytes or cast it to a NSData object and check with isEqualToData. Don't forget to release the CFData object.
Edit
On iOS, as far as I known, the only reliable approach is to copy the data (or secret) into the keychain, using a temporary key, so that you can find it again, and then extract the data. It's cumbersome, but if you just implement it in a minimalistic way, it should not take more than 30 lines of code. I have a working example.
I The usual disclaimer: Use this at your own risk, and always be careful with security stuff.
iOS10 added:
CFDataRef _Nullable SecKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error)
so you can now create two Data (NSData) objects, then compare those.
Have a look at this answer for just getting the NSData: Can I get the modulus or exponent from a SecKeyRef object in Swift?
You can then compare the two NSData instances using isEqualToData:
I don't have expereince in the domain, but if they are two strings (irrespectiveof their content), you would basically do a simple check:
if(string1 == string2)
{
//condition logic
}

Data encryption with AES

I'm building an app that will communicate with a server (php), and this communication (probably will be with json) i want to encrypt. After a lot of searching and reading i found the AESCrypt-Objc project.
While testing the encryption (i'm using a web tool AES Encryption test) i found that in the encryption result i'm missing 16 byte of data.
Here's the example i'm using
In the AES project:
String to be encrypted: "The quick brown fox jumped over the lazy dog".
Password: "12345678901234561234567890123456"
The result:
<7eda336b 82f3e279 ae7638fe cccfffc6 5fbef8da 6df76d97 67d8cfa8 5bce2ae9>
My Code:
self.strnToBeEnc = #"The quick brown fox jumped over the lazy dog";
self.appKey = #"12345678901234561234567890123456";
NSData *data2 = [self.strnToBeEnc dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%#", data2);
NSData *s2 = [data2 AES256EncryptedDataUsingKey:self.appKey error:nil];
NSLog(#"%#", s2);
WEB Tool:
Same string and password
The result:
<7eda336b 82f3e279 ae7638fe cccfffc6 5fbef8da 6df76d97 67d8cfa8 5bce2ae9 ca2ed34a 48f85af2 909654d5 b0de0fb7>
As you can see i'm missing some bytes...:)
I've tried adding to the buffer in the algorithm, but with no success.
Any advice?
Thanks
(if the question is not Detailed enough please let me know)
I know you were trying to avoid this but I think you might need to spend some time in the source code of AESCrypt-Objc as I suspect it is somehow not encrypting the last block.
Step into the code and see if you actually get to the CCCryptorFinal call, and note its results. This can be found in the AESCrypt-ObjC/NSData+CommonCrypto.m _runCryptor:result: . Another thing to look into is the default padding type they are using which appears to be kCCOptionPKCS7Padding this will also have an effect on your ending bytes.
Do your testing first with non-arbitrary length bytes that are multiples of the AES block size, then once you have validated that move on to the variable length ones you have here.

PGP decryption on iOS

I'm trying to implement decryption of a PGP file on an iPad. I set up some test .txt files which I then encrypted via PGP desktop.
I've imported the private key of the certificate used to encrypt the document, using SecPKCS12Import, then SecIdentityCopyPrivateKey() from the resulting SecIdentityRef.
If I test encrypting and decrypting a simple string in Objective C, using the public and private key of the cert, that works perfectly.
Now that I'm trialling the actual PGP decryption, I'm a bit stumped... Reading the text from the .pgp file, I get:
-----BEGIN PGP MESSAGE-----
Version: 10.1.1.10
qANQR1DBwEwDraQm2Kxa5GkBB/4yLebeLk10C2DVvHpQL20E0DThhgQlTasXo+YJ
pLp5Ig2hHu4Xx0m74D3vfyWpA2XQA02TMAHO9lhNfkE234c/ds05D1UyQkJEoqW+
joEcbRT5rlGN3qrMf1FXv8/01EHH0dgeD6mAkkDeDEorIirYHCF6+QVkedaphZLs
c63GmcikzkWZT/vv20ICL3Ys0DaC3P9zu0T1GtjkmQ062kaTab/VBJnQrsY/y1JU
ypmbW9bbFeZMcAqXHMqpjw49K5UluIJaDbRNAjIvHTFLNuOYWVJM6FcMs5p6xqvZ
ltizeKAjr1B1h4DvbQaqdO6/OAb+dGr7fJoIHEszDsJbW1cc0lUBitrxKHrPGovF
1uEW+3glA3SopveWB4GkKzcYlbqT5y1p/gQNwY8yuZr/6iF1hyF9mx/hU/+xjOMB
og3sGX4npcQegsAMw2y+zz9kJ9a6jlteNufi
=d2Fq
-----END PGP MESSAGE-----
I know that I need to get the random one-time key, that PGP used to encrypt the file, from the data in the file. I know that to do that, I need to use SecKeyDecrypt with the private key, to obtain the one-time AES key. Once I have that key, I can then decrypt the rest of the data.
The part I'm having problems with is which part of the data to feed into SecKeyDecrypt. How is the PGP file setup - is the first 128 chars the AES key? Unless my understanding is wrong, I need to get that out separately from the data.
If I run, say, the first 128 chars as a void through the SecKeyDecrypt function: (after stripping the BEGIN PGP MESSAGE lines)
size_t dataLength = [theKey length];
size_t outputLength = MAX(128, SecKeyGetBlockSize(privateKeyRef));
void *outputBuf = malloc(outputLength);
OSStatus err;
err = SecKeyDecrypt(privateKeyRef, kSecPaddingNone,//PKCS1,
(uint8_t *)theKey, dataLength,
outputBuf, &outputLength);
if (err) {
NSLog(#"something went wrong...err = %ld", err);
}
I get this:
MRªh6∞bJ˘e£t*˝ã=ŒA¢Òt‘ŸY±éÿAÃîâG
Îfi≠$b≈tâç`yxk=uHªqu-,–dïn^™È\›5±tb.‡€Kñ⁄≤sΩw–ïʃkafS˘À*Æô竡rAyv)fi]wOrµKz^ªq“à∑öΓı*r<+l˝Äo∑›g≠¶/÷eÔ&€PÒRåêM¶Ñ|Q$á6În^võ¬∏·h(ƒß•R≤(flò(*•Aa
I don't know what encoding this is, but trying to get it from the outputBuf into a string never works 100%. It seems to get modified no matter what encoding I pass it. If I pass it to NSData first, I can get the original string back.
NSData *keyData = [NSData dataWithBytesNoCopy:outputBuf length:outputLength];
NSString *keyFromData = [[NSString alloc] initWithBytes:[keyData bytes] length:[keyData length] encoding:NSASCIIStringEncoding];
I then try to pass that key to an AES256DecryptWithKey class, providing it with the remaining data from the PGP file, after the first 128 chars.
NSData *cipherText = [[NSData alloc]initWithData:[[bodyPart objectAtIndex:1] dataUsingEncoding:NSUTF8StringEncoding]];
NSData *plain = [[NSData alloc] initWithData:[cipherText AES256DecryptWithKey:keyFromData]];
NSLog(#"after decrypting = %#", [[NSString alloc] initWithData:plain encoding:NSUTF8StringEncoding]);
Problem:
The resulting data 'plain' prints as <> i.e. empty. My problem is I don't even think I know how to grab the key from the PGP file.
Can anyone explain to me the PGP file setup? What part is the 'key' if it is in fact separate from the data at all? Is it always the same length/ same position? If it's NOT separate then I don't know how I'd be able to grab it at all. I think the rest would work fine. I'm not getting any errors or crashes, it's just NOT the right key and/or data I'm passing for AES decryption, I suspect probably a combination of string encoding error and not grabbing the right amount for the AES key/ right combination.
Note -
I created 3 different text files and ran them through the PGP process. Inspecting them, they all started with the same 24 characters (qANQR1DBwEwDraQm2Kxa5GkB). Even if I pass these 24 through the decryption, it doesn't work, and I was under the impression that the AES key PGP used was different for every single document. Did I get that wrong?
Thanks for any step in the right direction!
Edited to add:
Just noticed partly my mistake - AES of 128 requires 16 bits, so either way I am taking the wrong amount by using 128 characters, stupid mistake, think I've been looking at this too long... Swapped it out and didn't work. Any decryption I do is resulting in the '⁄Ĉ¢ï¡0M¶È2Cˆ¿©gUú¨6iîΩ`&<%Jœv£¯nRb∆:(–%' type result, which to me implies I've done something wrong OR need to do something further with encoding.
Read RFC 4880. That file is an ASCII-Armored collection of PGP packets. There are 1 or more packets that contain the symmetric key needed to decrypt the actual message, each of the symmetric key packets is encrypted with the public key of a recipient. Only people who possess the right private key can decrypt the symmetric key packet and then use that symmetric key to decrypt the rest of the message.
The AES key is indeed different.
It is randomly selected, and the encrypted with the public key system (RSA, typically).
Pub key has costs and limitations that make it unattractive to use for bulk.
You might want to look at the NetPGP, which is C code under the
BSD license, which means you can incorporate it or modify it
without encumbering your app or upsetting Apple in any way.
(Of course, contributions of source code or money would be
appreciated by the project. I'm not affiliated with them.)
The OpenPGP standard is a lot of work to implement.
Even once an implementation works, there are countless
ways in which it can be insecure.

Resources