I'm trying to encrypt some data in node to send to a Java server and keep coming up with this error BadPaddingException. I've seen other posts with an answer to change the Cipher instance on the Java side
Cipher CheckCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
but since this server is used for several different services, this isn't possible for this application.
My current implementation is using node-forge
var publicKey = fs.readFileSync('Public.key', 'utf8');
const key = new nodeRSA([publicKey]); //the file is not pem formatted and nodeRSA formats it to use in forge
var publicKeyForge = forge.pki.publicKeyFromPem(key.exportKey(scheme));
var providerIdBufferForge = forge.util.createBuffer(providerId, 'utf8');
var providerIdBytes = providerIdBufferForge.getBytes();
var providerIdEncrypted = publicKeyForge.encrypt(providerIdBytes, 'RSAES-PKCS1-V1_5');
var encryptedProviderId = forge.util.encode64(providerIdEncrypted);
however this is coming up with the padding error on the Java side.
I've also tried using node-rsa, and crypto.publicEncrypt. I'm pretty certain that this is a mismatch in the algorithm scheme, but after a couple days of trial and error, still coming up empty.
The Java side code is
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] byteDecryptText = org.apache.commons.codec.binary.Base64.decodeBase64(dataToDecrypt);
decryptedText = cipher.doFinal(byteDecryptText);
decryptedData = new String(decryptedText, "UTF-8");
Does anyone have any experience with matching node RSA Encryption with the basic Java RSA Decryption?
Any help is much appreciated!
Thanks
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 have written a little chat and messages are objects like
{type="message",sender="userA",content="plaintextmessage",recipient="userB"}
that are sent to the server who spread it to all enrolled users. I want to encrypt the plaintextmessage-part that the message object looks like
{type="message",sender="userA",content="bHJg67&GghjGZuf/zdu=",recipient="userB"}
I have build my RSA keypair on both - server and client.
KeyPair keyPair = buildKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Then i encode the servers public key to a byte array and this array to a base64 encoded string and send it to the client.
byte[] encodedPublicKey = publicKey.getEncoded();
String b64PublicKey = Base64.getEncoder().encodeToString(encodedPublicKey);
Both, client and server, have implemented the functions
public static byte[] encrypt(PublicKey othersPubKey, String message) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, othersPubKey);
return cipher.doFinal(message.getBytes());
}
public static byte[] decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encrypted);
}
When i try to encrypt a message on the client, send it to the server and decrypt it there i get the error
javax.crypto.IllegalBlockSizeException: Data must not be longer than 512 bytes
Does that means that this encryption method is nout suitable for my messages? I found Java/JCE: Decrypting "long" message encrypted with RSA. Is that my new goal?
Yes, it is called a hybrid cryptosystem. Even then, you may want to understand about the Bleichenbacher attack, the use of authenticated encryption, how to gain trust in a public key etc.
So your goal is either to study the field in much more detail or to learn a lot less about deploying TLS 1.2 or 1.3. Because it takes a lot of details to implement transport mode security.
If you want to continue, at least take a look at RSA in OAEP mode and AES in GCM mode.
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 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.
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.