I am developing a BlackBerry application where the server is in Java. Whenever AES encrypted data is sent to server along with PKCS5 Formatting,The server gets bad padding exception while decrypting it.
Is that the Blackberry or the server code? Have you tried using the standard JCE classes? something like this:
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec key = new SecretKeySpec(yourKeyBytes, "AES");
aes.init(Cipher.DECRYPT_MODE, key);
byte[] cleartext = aes.update(ciphertext, 0, ciphertext.length);
Make sure you're doing the padding etc in the right order first: pad then encrypt, decrypt then unpad.
Check the transmitted data length and make sure it's a multiple of blocksize.
Make sure blocksize is consistent in all the calls.
Make sure your crypto provider settings are exactly matching.
Related
We have a client server system where client(Android phone) and server(spring ) both are using java.security.KeyFactory to get an instance of java.security.KeyFactory as shown below:
KeyFactory factory = KeyFactory.getInstance("RSA");
But if we do that, when we use this factory to encrypt data, the server gives a different output and the client gives different output. When we checked providers, it was coming to SunRsaSign for server and was OpenSSLRSA for the client. So we tried to set the same on the client using the following:
KeyFactory factory = KeyFactory.getInstance("RSA", "SunRsaSign");
But we get java.security.NoSuchProviderException error. Similarly when we try to set OpenSSLRSA on server, they also face the same error.
Complete code to encrypt is same on server and client is following:
String pubKey = "<key here>"
byte[] keyData = DatatypeConverter.parseHexBinary(pubKey);
System.out.println("key data" + Arrays.toString(keyData));
KeyFactory factory = KeyFactory.getInstance("RSA");
//System.out.println("provide = " + factory.getProvider());
PublicKey pub = factory.generatePublic(new X509EncodedKeySpec(keyData));
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, pub);
byte[] secretMessageBytes = msg.getBytes(StandardCharsets.UTF_8);
System.out.println("secret msg" +Arrays.toString(secretMessageBytes));
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
System.out.println("enc data" +Arrays.toString(encryptedMessageBytes));
encryptedMessageBytes generated are different. Can that be a problem? I think it is because of different Providers being used by different platform.
Can somebody pls help me on how to set the provider for KeyFactory or how to remove the decryption error(javax.crypto.BadPaddingException)?
The posted code, used on both the Android and Java/Spring sides, only specifies the algorithm and not the padding when instantiating the cipher:
Cipher.getInstance("RSA")
Without explicit specification of the padding, the default padding of the providers of both sides is used. However, different providers generally define different default paddings (e.g. NoPadding, PKCS1Padding or OAEPPadding). The different paddings cause the decryption to fail, because a prerequisite for successful decryption is that the same padding is used as for encryption.
To avoid such things, the padding should always be specified when instantiating the cipher, especially in a cross-platform environment, e.g.:
Cipher.getInstance("RSA/ECB/PKCS1Padding")
On the left is the algorithm and on the right the padding. Note that the middle part (ECB) has no meaning for RSA (it is an artifact of using the scheme of symmetric encryption specifying the operation mode in the middle, which is not defined for asymmetric encryption).
Fixing the issue with the explicit specification of the padding proves that the padding was indeed the problem.
I can only speculate about the default paddings used in your environment.
I could not test the OpenSSLRSA provider, as it is not available in my environment. On my machine Android (API Level 28, P) applies the AndroidOpenSSL provider (aka Conscrypt). This defaults to NoPadding, while Java's SunJCE provider defaults to PKCS1Padding. Encryption on Android and decryption on Java would result in an exception (or non-removed padding for the other direction).
You can determine the default padding of your environment as follows: Encrypt with the default padding (by specifying only the algorithm) and vary the padding on decryption until decryption is successful and the original plaintext is decrypted.
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 need to use AEAD to share information between two users, part of which must be encrypted and part of which should be kept in cleartext.
Is there an API to check the ciphertext tag and access the associated data once a message has been encrypted with AES/GCM ?
In more detail:
I'm using Java 7 with bouncycastle as a provider and I have managed to encrypt and decrypt my data successfully, using the corresponding API:
private byte[] encrypt(SecretKey key, byte[] nonce, byte[] message, byte[] associatedData) throws ... {
Cipher aeadCipher = Cipher.getInstance(AES_GCM_NOPADDING);
aeadCipher.init(Cipher.ENCRYPT_MODE, kint, new GCMParameterSpec(GCM_MAC_SIZE, nonce);
aeadCipher.updateAAD(associatedData);
return aeadCipher.doFinal(message);
}
private byte[] decrypt(SecretKey key, byte[] nonce, byte[] cipherText, byte[] associatedData) throws ... {
Cipher aeadCipher = Cipher.getInstance(AES_GCM_NOPADDING);
aeadCipher.init(Cipher.DECRYPT_MODE, kint, new GCMParameterSpec(GCM_MAC_SIZE, nonce);
aeadCipher.updateAAD(associatedData);
return aeadCipher.doFinal(cipherText);
}
However, it is my understanding that AES/GCM ciphertexts should already contain the parameters that could affect decryption (nonce and associatedData).
Therefore, I would like to be able to retrieve them from the ciphertext, rather than having to store them alongside the ciphertext and pass them along to the decryption function. Furthermore, I'd like to be able to run integrity checks (computing the tag) and run some checks on the associated data without having to completely decrypt the message.
Is there an API that would allow this and that I might have missed ?
So far, I've checked:
The Cipher API: http://docs.oracle.com/javase/7/docs/api/javax/crypto/Cipher.html
This very informative blog post on AES/GCM implementations in Java: http://blog.philippheckel.com/2014/03/01/cipherinputstream-for-aead-modes-is-broken-in-jdk7-gcm/
Since the Java API automatically places the tag at the end, you only have to extract this tag from the your encryption result as follow:
private byte[] getTag(SecretKey key, byte[] nonce, byte[] message, byte[] associatedData) throws ... {
Cipher aeadCipher = Cipher.getInstance(AES_GCM_NOPADDING);
aeadCipher.init(Cipher.ENCRYPT_MODE, kint, new GCMParameterSpec(GCM_MAC_SIZE, nonce);
aeadCipher.updateAAD(associatedData);
byte[] encrypted = aeadCipher.doFinal(message);
// Assuming you have an AAD_SIZE = 128 bits (16 bytes)
return Arrays.copyOfRange (encrypted, encrypted.length-16, encrypted.length)
}
There is no implicit format that stores all the input data of GCM in a specific location. The Java API is already a bit strange in
the sense that it automatically places the tag at the end. This makes the
algorithm more compatible with the Cipher class, but in principle the tag just
needs to be kept with the ciphertext - where does not matter. Now you have the
issue that you don't know where the AAD ends and the ciphertext starts for instance.
So what you can do is either to create your own format (maybe your AAD has a
static size, so you can just concatenate) or you can use a predefined container format.
There is an internet draft
that specifies how to use both modes in the Cryptographic Message Syntax (CMS).
The AAD can then be stored in authenticated atributes, which should also include the required
parameters (including the IV comprising of the nonce).
If you feel masochistic you could also try and use XML-encryption with GCM mode, but beware of the many pitfalls with regard to verifying XML authenticity (e.g. beware that you are actually verifying the data you are going to use).
Bouncy Castle seems to offer support for CMS using GCM.
I have this code to send encryted data over a network:
s = new Socket(serverAddress, serverPort);
is = s.getInputStream();
os = s.getOutputStream();
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, ClientSocket.clientPrivateKey);
cis = new CipherInputStream(is,decryptCipher);
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, this.serverPublicKey);
cos = new CipherOutputStream(os,encryptCipher);
This code works, but when I try to use CipherOutputStream to send encrypted data over the network, the data is not sent until I call cos.close(), but if I close the stream I close the network connection. What is the proper process for sending encrypted data with CipherOutputStream?
The way I interpret the code is that the Cipher is initialized to encrypt one message with RSAES-PKCS1-v1_5, because according to http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher "RSA" refers to "The RSA encryption algorithm as defined in PKCS #1" which I guess refers the oldest implementation with a padding scheme and that should be RSAES-PKCS1-v1_5. If that is correct, there is no way for the stream to produce partial results before the whole message (the whole stream) is read. Also you should not be able to send long messages with the cipher (with a 2048 bit RSA key that should be less than 256 bytes).
I assume what you are trying to accomplish is to create a secure connection between two endpoints? If so then you should not bother with all that low level cryptography and create a TLS connection. Even though it not trivial to set up it still is much more easier than to build a secure encrypted communication channel from scratch.