I'm interested in Android programming. I'm working with symmetric and asymmetric encryption and I wanted to wrap secret key from Android Key Store by public key but I failed. I'm able to get secret key from Key Store but I can't wrap it.
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE, publicKey);
SecretKey secret = (SecretKey) keyStore.getKey("key",null);
byte[] encrypted = cipher.wrap(secret);
Error:
InvalidKeyException:Cannot wrap key,null encoding
I tried to work with this key pair and secret key in code above - I successfully encrypted and decrypted some String by symmetric and asymmetric encryption.
What's interesting? I tried to generate new secret key by KeyGenerator and wrapping was successful. I'm little bit confused. I can't wrap it by secret key, which is loaded from KeyStore.
Could you help me please?
I believe that you got this error while using BouncyCastle since I also just received this error.
I found that BouncyCastle is calling the following code which throws the error:
byte[] encoded = key.getEncoded();
if (encoded == null) {
throw new InvalidKeyException("Cannot wrap key, null encoding.");
}
The Android KeyStore won't allow you to access the value of the private keys and will instead return null when you try to do so, which is why the above error is thrown.
Related
I am making an server API which will return some confidential keys to my app.
Then the app will use these key to perform a particular action. I would be sending the Keys over SSL so that any Man In the Middle attack could not read them.
To start first I will be first everything the Package name and then I also want to verify the something which assures me that my app has not been decompiled and recompiled and the package is not fake.
Basically I want to avoid these issues:
1) Someone is not creating a fake package name and then sending the request
2) Someone has not recompiled my app and then sending the request
3) Someone if not tracking the response of the server via MIM
Till now I have thought the best way would be to use a HASH key and then compare it within my server to see if the POST key is the same as stored in my server.
But I have not been able to find a key which is attached to the signing key of the app and which cannot be accessed by anyone having the APK of my app.
Any help would be grateful.
You can add extra layer of protection if you create keys in your app using C++ code available on android's NDK libraries. Here's an amazing tutorial for that. Basically, this protects your app from de-compiling tools which commonly de-compiles java files. Also, I recommend adding AES encryption on your keys before sending it through the post request of your SSL server.
On your onCreate() method, get the key from native C++ implementation:
String nativeKey = invokeNativeFunction()
then encrypt it:
byte[] keyStart = nativeKey.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(keyStart);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
// encrypt
byte[] encryptedData = encrypt(key,b);
Encrypt method:
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
Edit for CSRF:
There's an interesting answer from here: Authenticity_token in Rails + Android, also on Wikipedia, there are quite suggestions as to how to counter cross site request forgery. which includes:
Synchronizer token pattern
Cookie-to-header token
to name a few.
Here's a layer of extra security to identify the authenticity of the app request as well.
I'm encrypting some data on a Phoenix webserver:
private_key = ExPublicKey.load!("private.pem")
token = %{username: user.username, mobile_phone: user.mobile_phone, email: user.email}
payload = Poison.encode!(token)
{:ok, signature} = ExPublicKey.encrypt_private(payload, private_key)
And decrypting it on the Java (actually Android) client as follows:
try {
byte[] keyBytes = Base64.decode(Constants.RSA_PUBLIC_KEY.getBytes(), Base64.DEFAULT);
X509EncodedKeySpec encodedKeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(encodedKeySpec) ;
Cipher cipher = Cipher.getInstance("RSA") ;
cipher.init(Cipher.DECRYPT_MODE, publicKey) ;
//
Log.e(DEBUG_TAG, jwt) ; // received token
String payload = new String(Base64.decode(jwt, Base64.DEFAULT), "UTF-8") ; // java does UTF16, elixir does UTF8
Log.e(DEBUG_TAG, payload) ; // base64 decoded token
byte[] cipherText = cipher.doFinal(payload.getBytes("UTF-8")) ; // decrypt
String token = new String(Base64.decode(cipherText, Base64.URL_SAFE), "UTF-8") ; // cipher text is urlencoded
Log.e(DEBUG_TAG, token) ;
return null ;
} catch (Exception e) {
e.printStackTrace();
}
There are no exceptions on the Phoenix side but trying to decrypt the token on java results in the exception:
java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(CipherSpi.java:459)
at javax.crypto.Cipher.doFinal(Cipher.java:1502
If the input is too large for the RSA modulus it should have resulted in error on the webserver. So I'm wondering what is actually wrong.
UPDATE: seems like there was an issue with library. The output produced by signing the SHA256 digest of some data returns 344 bytes, whereas its supposed to be 256 bytes for the key length used. Reverted to using Erlang's public_key module and it works fine now.
Is not clear the real purpose and that makes things difficult, but if you are trying to issue JSON Web Tokens, as it seems, your implementation is completely wrong
JWT is digitally signed, not encrypted
encrypt with private key != Digital signature
you are "decrypting" the entire token instead of verifying the signature, which should be the last part of a JSON Web Token like this hhhh.pppp.ssss.
#zaph described the error, but it would not occur if you use digital signature. It is not possible to fix your code so consider to re-implement it
Signing is not the same as encrypting using a private key. Although both would be using modular exponentiation with the private exponent signing and encryption use different padding methods. More information here. You should basically not see hashing and signing as separate operations: the hashing is part of the signature generation and verification.
The reason why your code failed is however different: the signature is likely encoded using base64. Base64 will generate an output size of ceiling(256/3)×4. This of course equals 344 characters / bytes. So you first would have to decode the result before decrypting it.
The solution to this problem is to use hybrid encryption. Namely, this involves using RSA to asymmetrically encrypt a symmetric key.
Randomly generate a symmetric encryption (say AES) key and encrypt the plaintext message with it. Then, encrypt the symmetric key with RSA. Transmit both the symmetrically encrypted text as well as the asymmetrically encrypted symmetric key.
The receiver can then decrypt the RSA block, which will yield the symmetric key, allowing the symmetrically encrypted text to be decrypted.
This can be shown more formally as the following. Let MM be the plaintext, KAESKAES be the randomly chosen AES key, and KPuKPu be the receiver's public RSA key you already have.
C1=EAES(M,KAES)
C1=EAES(M,KAES)
C2=ERSA(KAES,KPu)
C2=ERSA(KAES,KPu)
Then, send both C1C1 and C2C2.
Let KPrKPr be the receiver's private RSA key. The receiver can then recover MM as
KAES=DRSA(C2,KPr)
KAES=DRSA(C2,KPr)
M=DAES(C1,KAES)
M=DAES(C1,KAES)
(To allow streaming decryption or large messages, you would usually send C2C2 first and then (the much larger) C1C1.)
I'm trying my darnedest to create a program that will generate a private/public RSA key set and use it to send messages that are secure from end to end. I am using the RSA private/public key pair to securely transmit an AES key, that will be used to send the messages.
When I use a 1024-bit key pair, the encrypted session key is 128 bytes. But attempting to decrypt it with the RSA private key, it complains that it can only decrypt 117 bytes or less.
When I use a 2048-bit key pair, the encrypted session key is 256 bytes (but it must be 245 or less), etc.
The result is always the same: I cannot pass a valid passphrase back and forth. :(
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecretKey sessionKey = keyGen.generateKey();
// Encrypt the session key with the RSA public key
Cipher rsaCipher = Cipher.getInstance("RSA");
rsaCipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] encryptedSessionKey = rsaCipher.doFinal(sessionKey.getEncoded());
// Simulating other end user: Receive encrypted session key,
// decrypt it using your private key.
Cipher rsaDecryptCipher = Cipher.getInstance("RSA");
rsaDecryptCipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] decryptedSessionKeyBytes = rsaCipher.doFinal(encryptedSessionKey);
System.out.println("Decrypted session key bytes");
System.out.println(decryptedSessionKeyBytes);
Edit: It looks like the key that is generated is being padded at some time or another, but I don't know how to stop or subvert it. I has teh dumb when it comes to encryption... up until now I was relying on openssl_seal.
You are using rsaDecryptCipher it for everything except the actual decryption.
It would be a good idea to always specify the mode of operation and padding mode, e.g. "RSA/ECB/OAEPWithSHA1AndMGF1Padding" for your Cipher instances.
I have to build a simple authorization server for a project. The server has to distribute AES keys to allow its clients to communicate with each other.
When encrypting the AES key using RSA, I run into this error: "javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes".
Which is weird, since the lenght of my AES key is 128 bits = 16 bytes.
Here is the code generating the error:
private void createAndSendAES() throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, IOException, InvalidKeyException, BadPaddingException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
this.AESBlackboardKey = keyGen.generateKey(); // My AES key
byte[] raw = AESBlackboardKey.getEncoded();
System.out.println(raw.length); // Prints 16
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, this.clientPubKey);
SealedObject encryptedAESBlackboardKey = new SealedObject(this.AESBlackboardKey, cipher); // ERROR HERE
ObjectOutputStream outO = new ObjectOutputStream(this.clientSocket.getOutputStream());
outO.writeObject(encryptedAESBlackboardKey); //Transmitting the key over socket link
outO.flush();
System.out.println("AS: Blackboard AES key sent.");
}
Does someone know how the encryption of a 16 bytes long AES key makes me run into this kind of error and how to avoid it ?
Thanks in advance !
The reason why you are getting the error is that the whole object is saved, not just the 16 bytes that make up the key. So you will e.g. have the full class name in there, the serial number of the class etcetera.
If you want to keep using SealedObject then I would suggest encryption with a new random AES key and "AES/CBC/PKCS5Padding". You can then encrypt that key using the RSA algorithm (be sure to specify it fully, e.g. "RSA/NONE/OAEPPadding" or "RSA/NONE/PKCS1Padding") simply by using Cipher.doFinal().
You can also directly encrypt the generated data that way.
Another method is to simply increase the RSA key size; the RSA key size of 1024 is increasingly under threat, try to use a key size of 2048 at the bare minimum (allowing 256 - 11 = 245 bytes of storage).
Note that you can retrieve the 16 bytes from a previously created AES key by using key.getEncoded().
I have some problem with my android application. I am trying to an app related with RSA encryption/decryption.this is my problem:
I can encrypt short sentences clearly, but when i try to decrypt this message to orginal text I give an error ("too much data for RSA block"). And also if I want to encrypt a long sentences i have same error.I had some search for this problem, and found some solution in this sites:
Site 1
Site 2
Site 3
But i dont understand anything, these solutions are so complicated.How can i fixed this problem, Can anyone give me a more simple solution? Thank you.
EDİT:
These are the code blocks that i use for this project.
public String RSAEncrypt(String plain) throws NoSuchAlgorithmException, NoSuchPaddingException,InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, UnsupportedEncodingException {
publicKey = getPublicKey();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherData = cipher.doFinal(plain.getBytes());
return Base64.encodeToString(cipherData, Base64.DEFAULT);
}
public String RSADecrypt(byte[] encryptedBytes) throws NoSuchAlgorithmException, NoSuchPaddingException,InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, UnsupportedEncodingException {
privateKey = getPrivateKey();
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] cipherData = cipher.doFinal(encryptedBytes);
return Base64.encodeToString(cipherData, Base64.DEFAULT);
}
RSA can only encrypt messages that are several bytes shorter than the modulus of the key pair. The extra bytes are for padding, and the exact number depends on the padding scheme you are using.
RSA is for key transport, not data encryption. If you have a long message, encrypt it with AES, using a random key. Then encrypt the AES key with RSA, using the public key of the message recipient. You should be using the Cipher class's wrap() and unwrap() methods.
This is how PGP, S/MIME, TLS (roughly), and any other correctly designed RSA encryption schemes work.
In my use case, I need to encrypt some request data to server, from my app.
If I encrypt the AES key with RSA, that's kind of meaningless because the point of using RSA instead of AES, is to prevent reverse-engineering to get the AES key (versus RSA needs a private key stored in server for decryption). Since the AES key needs to be stored in the app locally, I cannot use the approach as suggested by erickson.
Instead, I split the long string into List<String> (Each length suggested by server provider); and encrypt each String.
After that, the List<String> is passed to server in a json array, and let server decrypt each and re-join them.
Of course, this approach needs your server's support, and can potentially has performance issue.