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.
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 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
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 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.