this code give invalid AES key length error. how can i correct it ? ( i want 128 bit key AES encryption )
package org.temp2.cod1;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.io.*;
public class Code1 {
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
String s = "9882623867";
byte[] plaintext = s.getBytes("UTF-16");
String s2 = "supernova";
byte[] key = s2.getBytes("UTF-16");
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(plaintext);
System.out.println(encryptedData);
}
}
any help appreciated
Use a SecretKeyFactory to derive key bytes from a password.You can see a detailed example here. Note that you'll need to specify a key length of 128 bits key instead of 256 bits as shown in that example.
The next problem that you will run into is that you have not specified a padding scheme. Unless your messages are a multiple of 16 bytes (the AES block size), that will raise an error. Use PKCS5Padding as shown in the example.
Use of CBC mode on the cipher will require a new initialization vector to be chosen for each message. This unique IV must be sent along with the encrypted message to the recipient.
Trying to perform cryptography without a thorough understanding of the concepts raised here (and a lot more) is likely to result in an insecure system.
You can't typically use any arbitrary key length (such as you're doing here with "supernova") for a block cipher like AES. You must use a supported key length (128, 192, 256, etc) appropriate for your algorithm of choice.
One common way to do this is to hash your passphrase (e.g., via SHA) and extract the first N bytes. This is better anyhow, as it allows you to "salt" your password with an initialization value such that no two users' "keys" are identical even if their passphrases are the same. If you're really interested in this stuff, the seminal work is Applied Cryptography by Bruce Schneier.
For practical implementation details, see
You can get this error when the key you're trying to use isn't the right length.
So in psuedocode, you're trying something like this:
String key = "123";
SecretKeySpec k = new SecretKeySpec(key, "AES");
but the key is too short - it needs to be something like, say 31 characters long.
So check your key value -> it's probably stored somewhere incorrectly.
Use a key Value string with 16 bytes for Smooth encryption e.g. The key "thebestsecretkey" will work on base64
Related
I am trying to clear my Secretkey after decrypting.
From what I've read, SecretKeys can be destroyed via the destroy method since Java 8.
I am using Java 14 so it should be possible.
However, whenever I use the destroy method on a key, a DestroyFailedException is thrown.
I've also seen that people ignore that Exception in their code, however, if I were to do that, I am able to print the Key after calling the destroy method on it.
Here my Decryption method:
private byte[] decrypt(byte[] encryptedText, char[] password) throws InvalidKeyException,
InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException, DestroyFailedException {
ByteBuffer bb = ByteBuffer.wrap(encryptedText);
byte[] iv = new byte[ivLengthByte];
bb.get(iv);
byte[] salt = new byte[saltLengthByte];
bb.get(salt);
byte[] cipherText = new byte[bb.remaining()];
bb.get(cipherText);
SecretKey key;
key = crypto.getAESKeyFromPassword(password, salt);
Cipher cipher;
cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(tagLengthBit, iv));
byte[] plainText = cipher.doFinal(cipherText);
Main.clearArray(password, null);
Main.clearArray(null, iv);
Main.clearArray(null, salt);
Main.clearArray(null, cipherText);
key.destroy();
cipher = null;
return plainText;
}
After calling the destroy method, I am, as said, (assuming I ignore the Exception) able to print the key via String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());
EDIT:
After using my Clear method on the array, I can still print it:
byte[] temp = key.getEncoded();
Main.clearArray(null, temp);
Clear Array:
protected static void clearArray(char[] chars, byte[] bytes) {
if (chars != null) {
for (int i = 0; i < chars.length; i++) {
chars[i] = '\0';
}
}
if (bytes != null) {
for (int i = 0; i < bytes.length; i++) {
bytes[i] = 0;
}
}
}
getAESKey:
protected SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return secret;
}
Final Edit:
The best solution was to switch frim PBKDF2 to argon2.
https://github.com/kosprov/jargon2-api
Argon2 allows to use raw Hashes, then you may store that byte array in a SecureKeySpec as mentioned above, since it allows destroying of the Spec, and clear the raw Hash Array.
Actually, there is no easy solution to this. The problem is that the destroy method is an "optional" method. Not all implementations of SecretKey implement it. If you are using a SecretKey type that doesn't implement the method, you get this exception and there is no simple solution.
Unfortunately, you can't just implement the method yourself because (typically) the class it belongs on is provided by the Java SE libraries.
And even if you do figure out how to destroy the key, there is also the problem of the String containing the password1. (And this problem is more of a security risk, since trawling for a String containing a password is likely to be easier than trawling for an unknown byte sequence.)
Options:
Forget the problem. Don't destroy they key / password in memory. (See below for an explanation as to why this is not as bad as it sounds.)
Look for alternative JSSE crypto libraries where the SecretKey implementation for AES secret keys does implement destroy. I am guessing that the Bouncy Castle libraries might. (And if they don't, you always have the option of downloading the source code and patching them.)
Nasty reflection. You could figure out which actual class implements the secret key, and look at its code to work out how it represents the key internally. Then you could use reflection to break the abstraction and access its private state and ... write zeros over the key.
Why is not destroying the key not a disaster?
So some security experts may disagree with this, but I still think it is a valid viewpoint.
When you zero a key or password in memory, you are (ostensibly) protecting against the following kinds of attack:
Attaching a Java debugger to the JVM process and using that to locate and read the key.
Reading the JVM processes memory.
Reading memory pages that have been written to disk.
How easy are these attacks? Well the first two require that the hacker has already gotten into the host and escalated to (probably) root privilege. In the third case you could do it that way, but the hacker could also just steal the hard drive where the swap pages are written.
In all cases, the hacker then has to find the secret key. Unlike (say) a C / C++ program, the key won't be stored at a fixed location. Instead the hacker has to find it by pattern matching, or by finding reference chains. (A Java debugger would make it easier, provided that the key object is still reachable.) And the flip-side is once the key has been garbage collected, the copy in memory will be gone, and the copy in swap will go the next time that the OS writes out the (now) dirty page where the key object once lived. After that ... it is "gone" for all practical purposes.
So rewind a bit. I said that in order to carry off this kind of attack, the hacker already needs root access. (Or the hard drives, which most likely amounts to the same thing.) Now if they have that, there are other ways they can steal the secret key. For example:
Use the debugger to set a breakpoint on (say) the destroy method, and grab the key before it is destroyed.
Use the debugger to capture the password before you create the key.
Steal the private key for the server's SSL cert (or whatever) so that they can pick up the password from network traffic.
Install a software keystroke logger.
Replace your application code with a version that leaks the key or password via some side channel.
And of course they can install backdoors, etc. In short, if the hacker has compromised the system to the degree needed to pull off a "steal stuff out of memory" attack against a JVM, that is probably the least of your worries.
Now a security expert may say that it is "best practice" to have a layered defense against hackers. There is some truth to that. However, if security is that important to you, you should do a proper security analysis (not just a "tick the boxes" audit) and figure out what the real risks are. This will (probably2) tell you that it is better to focus on making the system secure than worrying if someone (with root privilege) can steal keys out of memory.
1 - Though not in your case, because I see that you are using a char[] ... which can be cleared. Except that this still vulnerable to all of the other attacks that I talk about.
2 - Or maybe it won't. But you need to do the analysis!
You must implement the destroy method yourself. The documentation explains this.
https://docs.oracle.com/javase/8/docs/api/javax/crypto/SecretKey.html
I may have found a solution, I tried using this: https://github.com/dbsystel/SecureSecretKeySpec
The only problem is that the key would have to be a byte array, and doing this:
protected SecureSecretKeySpec getAESKeyFromPassword(char[] password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory= SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
byte[] temp = factory.generateSecret(spec).getEncoded();
SecureSecretKeySpec sec= new SecureSecretKeySpec(temp, "AES");
Main.clearArray(null, temp);
return sec;
}
Might not be good, since there is a getEncoded called on the SecretKey, so there might be a SecretKey in the Memory?
I have following code in Java.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(INIT_LENGTH, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
I need to implement it in JavaScript/Node.js
I don't know how to implement it in JavaScript from secretString to key generated by KeyGenerator
from the comment section:
This is my code:
function encodeDesCBC( textToEncode, keyString, ivString ) {
const key = Buffer.from(keyString.substring(0, 8), 'utf8')
const iv = Buffer.from(ivString.substring(0, 8), 'utf8')
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
let c = cipher.update(textToEncode, 'utf8', 'base64')
c += cipher.final('base64')
return base64url.escape(c)
}
My problem is secureRandom and KeyGenerator. I do not know how to implement it in nodejs
I don't know Java, but your usage looks somewhat weak, there are algorithms like PBKDF2 (which is old and discouraged now) and scrypt which do a much better job at turning human passwords into keying material. I'm also not sure where your IV is coming from in your Java code. Exactly replicating the Java code would be somewhat difficult as you'd need to know how your version of Java was implemented, and hence how the bytes passed to setSeed actually get turned into a key.
Node's Crypto module, as far as I can tell, assumes you know how long the keys are supposed to be. In the case of AES 128 in CBC mode, this would be 128 bits (i.e. 16 bytes) for both the key and IV.
Assuming you wanted to use things built into the Crypto module (argon2 would be recommended if you could relax this restriction) then you'd do something like:
const crypto = require('crypto');
const password = 'passw0rd';
const scrypt_salt = crypto.randomBytes(16);
const key = crypto.scryptSync(password, scrypt_salt, 16);
which would leave you with a suitable value in key, then you'd encrypt with:
const plaintext = 'the plain text to encode';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
const ciphertext = Buffer.concat([
cipher.update(plaintext),
cipher.final(),
]);
and you could encode to Base64 with ciphertext.toString('base64').
Note that to be able to decrypt this, you'd need the scrypt_salt, iv, and ciphertext.
When you encrypt something using AES, there is always a "mode" in play. In your Java code you don't specify a mode explicitly - Cipher.getInstance("AES"). When you do not specify a mode, the default mode in Java is "ECB" which is the most insecured mode anyway.
In your NodeJs code, you're using "CBC" mode which is a altogether different mode.
Neither "ECB", nor "CBC" are considered secured enough. As of today, usually, the recommended mode is the GCM mode.
To generate a key from a password, ideally a "key derivation function" should be used. The 4 key derivation functions recommended by OWASP are: PBKDF2, Scrypt, Bcrypt and Argon2.
In your Java code, the password is used as a seed for the pseudo random number generator class SecureRandom. That's a little bit bizarre because even if you give the same password to your function, it will produce different key in different run. Yes, SecureRandom is also used to generate key. But if the requirement is to generate a key from a password, a key derivation function, as mentioned above, shoul be used. Both the approaches are shown in the following StackOverflow answer with detailed explanation. However, it uses "GCM" mode. But as long as you understand the concepts, you can use any mode of your choice.
https://stackoverflow.com/a/53015144/1235935
Similarly, you'll find the same implementation in NodeJs in the following StackOverflow answer:
https://stackoverflow.com/a/53573115/1235935
To further understand AES in general, you may want to go through the following StackOverflow answer:
https://stackoverflow.com/a/43779197/1235935
I have to encode a string using AES/ECB/PKCS5Padding. The encrypted result ( new String(encryptedResult) as they don't want bytes) is then sent to a partner. My partner then decrypt the string using getBytes().
Here is the decrypting method :
public static String decrypter(final String donnees) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(donnees.getBytes()));
}
My problem is that I get this error when I try to decrypt :
Input Length must be multiple of 16 when decrypting with padded cipher.
When I decode bytes directly it works just fine. How can I make the string.getBytes() not loose padding ? Or any other solutions ?
I cannot change the crypting algorythm, and the same can be said about the string and not bytes beeing sent to the partner.
A padding error generally means the decryption failed and failures can include a incorrect key, data and encodings. Incorrect decryption has a side-effect of also producing incorrect padding.
In this case it is an incorrect encoding of the encrypted data. If you need the encrypted data as a string the general method is to use Base64 or hexadecimal encoding.
This code is incorrect: new String(cipher.doFinal(donnees.getBytes()));
Well this is actually a two-parter...
First I need to
read the contents of the file
crypt them into a byte[]
write the byte[] in a file or whatever...
Then the result from #2 or #3 will go into another project. I'm trying to protect our PEM/DER keys.
For decryption, I need to
read the contents of the crypted file as a byte[]
decrypt them into a byte[]
write the decrypted data to a file OR use it instead of a file
Now, I have some basic crypting code
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
// IvParameterSpec iv = new IvParameterSpec(IV); //used for the hardcoded one
byte[] encryptedData = cipher.doFinal(data);
and decrypting one as well
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
System.out.println("decrypted: " + new String(decryptedData));
and the question is:
Given a use-case scenario where one would rarely encrypt something and would distribute crypted keys that are to be decrypted at runtime, what do I need to save apart from the cyphertext?
I know I need to save the IV, but when I did decryption wasn't quite good - which leads me to believe that I need to save the secretKey as well.
Could anyone give me any tips, pointers or general security hints to a better solution? If I need to save the key, the IV and the encrypted data, where should I store them? Maybe hardcode the key and store the IV along the encrypted data? Maybe hardcode both the IV and the key and just store encrypted data in the files?
This isn't about theoretical safety, think of this as the biggest nuissance and inconvenience you can cause to someone that is trying to steal your keys. We all know there's no way I can perfectly hide them.
I pretty much need what this guy started with Decrypting an encrypted file and executing in Java
However if there's a better way of feeding secure data into a PemKeyReader, i'm all ears.
Sharing the key and encrypting something are two completely different things. How to share keys
Having said this, AES with 128bit is fairly strong encryption algorithm than 3DES So what you can do is keep PKI infrastructure in place to exchange AES keys and then Encrypt and Decrypt using them.
Why not RSA? RSA needs to be minimum 512 bit to consider it as strongest and if you increase more bits then it increases time required for encryption and decryption.
SO AES is fast and safe.
Use SecretKeySpec to create key from byte[]
public static void main(String[] args) throws Exception
{
// Initialise secret key with predefined byte array [] like below. I
// have used simple string to array method to generate 16 byte array.
// AES Key must be minimum 16 bytes.
// Now you can put this byte array some where is .SO file.
// Generate new Key using this byte []
// Then you can generate a key using device specific information at
// first boot up.
// Use second key to encrypt data and first key to encrypt the second
// key
// I Hope it clears all the doubts
SecretKey key = new SecretKeySpec("ABCDEFGHIJKLMNOP".getBytes(), "AES");
System.out.println(Arrays.toString(key.getEncoded()));
// Initialise Cipher with AES Algorithm
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Set The Encrypt Mode
cipher.init(Cipher.ENCRYPT_MODE, key);
// Encrypt some bytes
byte[] encrypted = cipher.doFinal("ABCDEFGH".getBytes());
// Print it to vefiry
System.out.println(Arrays.toString(encrypted));
// Get The IV
byte[] iv = cipher.getIV();
System.out.println(iv.length);
// Now why storing you can create structure like [16 IV][Encrypted Data]
// And while decrypting you can read first [16] bytes IV and then
// decrypt remaining bytes
//byte[] iv = new byte[16];
// System.arraycopy(encrypted, 0, iv, 0, 16)
//Copy remaining bytes to decrypt
// set cipher to decrypt mode
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(iv));
// decrypt it
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println(new String(decrypted));
}
Now write an algorithm which will generate byte[] from some random data like device name, user name, random seed etc.
You can add more protection to algorithm source code by writing that algorithm in C and create.SO file and get byte [] using Native calls.
What are the advantages of doing all this?
Event if your so is hacked it will need real time environment to run create key out of it.
Even if some one does crack it the damage will be limited i.e. 1 device
Hacker will have to repeat same with each device which is highly impossible to do.
The I/O aspect of your question is best addressed by reading the "Byte Streams" and "Buffered Streams" sections of the Oracle Java tutorial. You can accumulate the bytes in memory by writing them to a ByteArrayOutputStream, and then using the toByteArray() method to get the bytes as a byte[].
I am facing a very peculiar problem when using RSA encryption/decryption in Java.
Example code:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.genKeyPair();
Cipher enc = Cipher.getInstance("RSA");
enc.init(Cipher.ENCRYPT_MODE, kp.getPublic());
String CipherText = new String(enc.doFinal(PlainText.getBytes()));
System.out.println("CipherText: ") + CipherText);
Cipher dec = Cipher.getInstance("RSA");
dec.init(Cipher.DECRYPT_MODE, kp.getPrivate());
PlainText = new String(dec.doFinal(CipherText.getBytes()));
System.out.println("PlainText: " + PlainText);
As everyone can plainly see: I encrypt the plaintext using the public key, after which I decrypt the ciphertext using the private key.
This code crashes with the following message:
Exception in thread "main" javax.crypto.BadPaddingException: Data must start with zero
I also tried to explicitly use "RSA/ECB/NoPadding", and this fails on decoding period. (Eg the decoded ciphertext doesn't match the original plaintext).
Last but not least, I have tried to perform this when using my own PKCS1.5 padding function ala the PKCS1.5 specs:
EMB = 00 || 02 || RD || 00 || MD
EMB is encoded messageblock of length k
Where RD are 8 random nonzero bytes
MD is max length k = 11, and optionally padded with zero bytes to make EMB length k.
After two days of testing I can only conclude that the RSA algo in Java is flawed or simply not performing what I expect it to perform.
Any suggestions or fixes to the above code are very welcome, as I am completely stumped on why the above code will not simply work as expected.
Don't do this:
String CipherText = new String(enc.doFinal(PlainText.getBytes()));
Two reasons:
It's almost never a good idea to call String.getBytes() without specifying an encoding. Do you really want the result to depend on the system default encoding?
It's definitely never a good idea to treat the result of a binary encryption operation (i.e. opaque binary data) as an encoded string. Encode it in Base64 or hex instead.
You can use Apache Commons Codec to perform the base64 encode/decode operations, or this standalone public domain encoder/decoder.