I'm trying to generate a secret key using PBE but the secret key generated by the SecretKeyFactory is exactly the same as the input password. I've tried different algorithms, iteration counts etc. and it is still the same so I feel I'm missing a step here.
public SecretKey generateKey(String password, String salt) {
char[] passChars = password.toCharArray();
byte[] saltBytes = salt.getBytes();
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
PBEKeySpec keySpec = new PBEKeySpec(passChars, saltBytes, 2048, 128);
SecretKey secretKey = keyFactory.generateSecret(keySpec);
byte[] encodedKey = secretKey.getEncoded();
System.out.println("key: " + new String(encodedKey));
return new SecretKeySpec(encodedKey, "AES");
}
EDIT: if I use the algorithm "PBKDF2WithHmacSHA1" then the key generated is different from the password, but how come the algorithm I'm using is generating a key that is exactly the same as the input password?
When you generate a SecretKey using the SecretKeyFactory PBEWithHmacSHA256AndAES_128 you will get an instance of com.sun.crypto.provider.PBEKey and this class has the "special feature" that it returns the original "key" (aka password) when calling getEncoded() and not the cryptographic key material. If I understand it correctly the key derivation will not be made by the KeyFactory but by the Cipher itself.
Therefore you should not try to convert the SecretKey instance into a SecretKeySpec instance; instead just use the generated SecretKey instance in the correct cipher instance:
Cipher c = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
c.init(Cipher.ENCRYPT_MODE, secretKey);
Related
I want to convert a string of length 32 to 256-bit secret key of AES/CBC/PKCS5Padding in java.
I found two answers:
By encoding and decoding -
String password = "12345678910111211234567891011121"; // length-32
byte[] decodedKey = Base64.getDecoder().decode(password); // gives byte[] of size 24
SecretKey key = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
But byte[] of size 24 gives me for 192-bit key. I want 256-bit key for that exact string.
By using salt -
public static SecretKey getKey(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
}
But I don't want to use salt as I need to generate it again.
Is there any other way to convert string of length 32 to 256-bit secret key? I will be using IV anyway. Can I use it as a salt? If not, what can I give for salt value and what is the standard way to generate salt?.
I've been working on an encryption/decryption program. I am using AES.
What I am struggling with is, removing the string password and create a AES session key. This AES session key is what I actually want to use to encrypt and decrypt the data with. Then use the private or public key to encrypt this AES session key and stored locally (for example purposes).
Below is the coding which is currently fully working to encrypt and decrypt data with a plain text string:
Coding so far:
// password to encrypt the file
String password = "p#sswordDataNoW";
// salt is used for encoding
byte[] salt = new byte[8];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(salt);
FileOutputStream saltOutFile = new FileOutputStream("salt.enc");
saltOutFile.write(salt);
saltOutFile.close();
SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 65536,
128);
SecretKey secretKey = factory.generateSecret(keySpec);
SecretKey secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
//padding AES encryption
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
// iv adds randomness to the text and just makes the mechanism more
// secure
// used while initializing the cipher
// file to store the iv
FileOutputStream ivOutFile = new FileOutputStream("iv.enc");
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
ivOutFile.write(iv);
ivOutFile.close();
//file encryption
byte[] input = new byte[64];
int bytesRead;
while ((bytesRead = inFile.read(input)) != -1) {
byte[] output = cipher.update(input, 0, bytesRead);
if (output != null)
outFile.write(output);
}
byte[] output = cipher.doFinal();
if (output != null)
outFile.write(output);
inFile.close();
outFile.flush();
outFile.close();
Please could someone help me out, in terms of changing the string passwordto a session key that is generated. Then encrypt this session key with either the public or private key and stored on the local system.
I have tried many different ways and every time it has no worked out.
An AES key consists of either 16, 24 or 32 bytes and is supposed to look like random noise. The user is never going to see it. So, you can generate it securely:
SecureRandom r = new SecureRandom();
byte[] aesKey = new byte[16];
r.nextBytes(aesKey);
Here I generated a 16 byte key. You should use 24 or 32 byte for higher security, but only if you installed the Unlimited Strength policy files for your JRE/JDK.
Now, the remaining encryption is easy:
SecretKey secret = new SecretKeySpec(aesKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
// everything else as before
Separately from that you can encrypt aesKey with your public key. If you "encrypt" with a private key, you're not protecting the AES key from spying on. "Encrypting with a private key" is called signing and that data is not confidential.
I have written a code to encrypt a aes key and decrypt it but it dosent seem to be happening.Why is this so?
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
PublicKey pubKey= pair.getPublic();
PrivateKey privateKey = pair.getPrivate();
Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c1.init(Cipher.ENCRYPT_MODE, pubKey);
KeyGenerator aesKeyGenerator = KeyGenerator.getInstance("AES");
aesKeyGenerator.init(256);
Key aesKey = rijndaelKeyGenerator.generateKey();
Cipher symmetricCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] encodedKeyBytes = c1.doFinal(aeslKey.getEncoded());
SecretKey aesKey1 = new SecretKeySpec(encodedKeyBytes, "aes1");
Cipher dec = Cipher.getInstance("RSA/ECB/PKCS1Padding");
dec.init(Cipher.DECRYPT_MODE, privateKey);
symmetricCipher.init(Cipher.DECRYPT_MODE, aesKey1, spec);
if(aesKey.getEncoded() == dec.doFinal(c1.doFinal(aesKey.getEncoded())) )
{
// Not reaching here but is supposed to
}
On the line:
SecretKey aesKey1 = new SecretKeySpec(encodedKeyBytes, "aes1");
You are converting the still (RSA) encrypted aesKey to a SecretKey. At that spot you should have decrypted the key first. "aes1" is not any known type of key either.
Please try and separate the various wrapping (key encryption) and encryption statements into methods, and make separate methods for the unwrapping and decryption. Just throwing statements around is not going to get you anywhere. Try to make a methodical attempt to solve the problem at hand.
I am writing a simple app to encrypt my message using AES / CBC (mode). As my understanding CBC mode requires IV parameter but I don't know why my code work without IV parameter used. Anyone can explain why? Thanks.
The encrypted message printed: T9KdWxVZ5xStaisXn6llfg== without exception.
public class TestAES {
public static void main(String[] args) {
try {
byte[] salt = new byte[8];
new SecureRandom().nextBytes(salt);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec keySpec = new PBEKeySpec("myPassword".toCharArray(), salt, 100, 128);
SecretKey tmp = keyFactory.generateSecret(keySpec);
SecretKeySpec key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher enCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
enCipher.init(Cipher.ENCRYPT_MODE, key);
// enCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] cipherBytes = enCipher.doFinal("myMessage".getBytes());
String cipherMsg = BaseEncoding.base64().encode(cipherBytes);
System.out.println("Encrypted message: " + cipherMsg);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
When it is used without an IV, for certain types of ciphers including AES, it implicitly uses 0 IV. See Cipher class documentation.
The disadvantage of a null IV (or a deterministic IV) is that it is vulnerable to dictionary attacks. The requirement for IV is to prevent the same plain text block producing the same cipher text every time.
Like other users have said, it depends on the JCE provider. Java SE generates a random IV for you if you specify none.
Only Android1 and Javacard API use a blank IV, which is non-conforming to the Java Crypto spec, which states:
If this cipher requires any algorithm parameters that cannot be derived from the given key, the underlying cipher implementation is supposed to generate the required parameters itself (using provider-specific default or random values) if it is being initialized for encryption or key wrapping, and raise an InvalidKeyException if it is being initialized for decryption or key unwrapping. The generated parameters can be retrieved using getParameters or getIV (if the parameter is an IV).
If you do not specify the IV, in Java SE you get a random one, and will need to retrieve it with cipher.getIV() and store it, as it will be needed for decryption.
But better yet, generate a random IV yourself and provide it via IvParameterSpec.
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[cipher.getBlockSize()];
rnd.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), ivParams);
byte[] ciphertext = cipher.doFinal(input.getBytes());
1 That could be because Android is Java-esque, like the Eminem-esque ad. Just guessing, that's all.
I want to generate a privatekey PKCS8 format encrypted with password, and I try with this code:
String password = "123456";
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(2048);
KeyPair key = gen.generateKeyPair();
PrivateKey privateKey = key.getPrivate();
PublicKey publicKey = key.getPublic();
FileOutputStream pvt = new FileOutputStream("d:\\pvt123456.der");
try {
pvt.write(privateKey.getEncoded());
pvt.flush();
} finally {
pvt.close();
}
FileOutputStream pub = new FileOutputStream("d:\\pub123456.der");
try {
pub.write(publicKey.getEncoded());
pub.flush();
} finally {
pub.close();
}
But I donĀ“t know how to encrypt a password with 3des to be compatible with openssl format.
I know it's a little bit late but I also have been looking for a way to do this and while i was searching I found your question, now that I have found a way to do this I decided to come back and share this:
// generate key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// extract the encoded private key, this is an unencrypted PKCS#8 private key
byte[] encodedprivkey = keyPair.getPrivate().getEncoded();
// We must use a PasswordBasedEncryption algorithm in order to encrypt the private key, you may use any common algorithm supported by openssl, you can check them in the openssl documentation http://www.openssl.org/docs/apps/pkcs8.html
String MYPBEALG = "PBEWithSHA1AndDESede";
String password = "pleaseChangeit!";
int count = 20;// hash iteration count
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
// Create PBE parameter set
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory keyFac = SecretKeyFactory.getInstance(MYPBEALG);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher pbeCipher = Cipher.getInstance(MYPBEALG);
// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
// Encrypt the encoded Private Key with the PBE key
byte[] ciphertext = pbeCipher.doFinal(encodedprivkey);
// Now construct PKCS #8 EncryptedPrivateKeyInfo object
AlgorithmParameters algparms = AlgorithmParameters.getInstance(MYPBEALG);
algparms.init(pbeParamSpec);
EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, ciphertext);
// and here we have it! a DER encoded PKCS#8 encrypted key!
byte[] encryptedPkcs8 = encinfo.getEncoded();
This example code is based on the folowing code I found: http://www.jensign.com/JavaScience/PEM/EncPrivKeyInfo/EncPrivKeyInfo.java
but the folowing resource also helped me to understand a little bit better: http://java.sun.com/j2se/1.4.2/docs/guide/security/jce/JCERefGuide.html