I found a guide for implementing AES encryption/decryption in Java and tried to understand each line as I put it into my own solution. However, I don't fully understand it and am having issues as a result. The end goal is to have passphrase based encryption/decryption. I've read other articles/stackoverflow posts about this, but most do not provide enough explanation (I am very new to crypto in Java)
My main issues right now are that even when I set byte[] saltBytes = "Hello".getBytes();
I still get a different Base64 result in the end (char[] password is random each time, but I read that it is safer to leave passwords in char[] form. My other problem is that when the program gets to decrypt(), I get a NullPointerException at
byte[] saltBytes = salt.getBytes("UTF-8");
Thank you in advance for any help/advice you can give me.
The code in question:
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class EncryptionDecryption {
private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;
public static void main(String []args) throws Exception {
char[] message = "PasswordToEncrypt".toCharArray();
System.out.println("Message: " + message.toString());
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}
public static String encrypt(char[] plaintext) throws Exception {
salt = getSalt();
byte[] saltBytes = salt.getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
SecretKey secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(plaintext.toString().getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrypt(char[] encryptedText) throws Exception {
byte[] saltBytes = salt.getBytes("UTF-8");
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(encryptedText.toString());
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(encryptedText, saltBytes, iterations, keySize);
SecretKey secretkey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretkey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return decryptedTextBytes.toString();
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return salt.toString();
}
}
I think that you are making two mistakes :)
I've corrected your sample code to make it work :
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class EncryptionDecryption {
private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;
private static SecretKey secretKey;
public static void main(String []args) throws Exception {
salt = getSalt();
char[] message = "PasswordToEncrypt".toCharArray();
System.out.println("Message: " + String.valueOf(message));
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}
public static String encrypt(char[] plaintext) throws Exception {
byte[] saltBytes = salt.getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrypt(char[] encryptedText) throws Exception {
System.out.println(encryptedText);
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return new String(salt);
}
}
The first mistake is that you generate 2 different salts (when using the encrypt method), so encrypted/decrypted logs were differents (logical, but the decryption would still work because you are calling the decryption directly after encryption).
The second mistake was for the secret key. You need to generate a secret key when you are encrypting, but not decrypting. To put it more simply, it is as if i was encrypting with the password "encrypt" and that you are trying to decrypt it with the password "decrypt".
I would advise you to generate every random stuff (such as private key, salt etc on startup). But beware that when you'll stop your app, you won't be able to decrypt old stuff unless getting the exact same random stuff.
Hope I helped :)
Regards,
Related
This question already has answers here:
What's the simplest way to print a Java array?
(37 answers)
Closed 1 year ago.
The way i understand it, a key encoding should return a sequence of bytes in some specific encoding such as UTF-8 for example.
However, logging the following:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair kp = kpg.generateKeyPair();
Log("TEST : ${kp.public.encoded}")
Log("Test : ${kp.public.encoded}")
Equivalently, in Java:
kp.getPublic().getEncoded();
Is giving me 2 different byte arrays! What am i missing here?
It seems that every time i call the encoded method, different pairs are generated.
Try in this way
import android.util.Base64;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AESEncyption {
private static final int pswdIterations = 10;
private static final int keySize = 128;
private static final String cypherInstance = "AES/CBC/PKCS5Padding";
private static final String secretKeyInstance = "PBKDF2WithHmacSHA1";
private static final String plainText = "sampleText";
private static final String AESSalt = "exampleSalt";
private static final String initializationVector = "8119745113154120";
public static String encrypt(String textToEncrypt) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(getRaw(plainText, AESSalt), "AES");
Cipher cipher = Cipher.getInstance(cypherInstance);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(initializationVector.getBytes()));
byte[] encrypted = cipher.doFinal(textToEncrypt.getBytes());
return Base64.encodeToString(encrypted, Base64.DEFAULT);
}
public static String decrypt(String textToDecrypt) throws Exception {
byte[] encryted_bytes = Base64.decode(textToDecrypt, Base64.DEFAULT);
SecretKeySpec skeySpec = new SecretKeySpec(getRaw(plainText, AESSalt), "AES");
Cipher cipher = Cipher.getInstance(cypherInstance);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(initializationVector.getBytes()));
byte[] decrypted = cipher.doFinal(encryted_bytes);
return new String(decrypted, "UTF-8");
}
private static byte[] getRaw(String plainText, String salt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKeyInstance);
KeySpec spec = new PBEKeySpec(plainText.toCharArray(), salt.getBytes(), pswdIterations, keySize);
return factory.generateSecret(spec).getEncoded();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return new byte[0];
}
}
I have this code in my rails app:
require 'openssl'
require 'digest/sha1'
require 'base64'
KEY="secret_key"
data = "secret message"
def encrypt_value(data)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.encrypt
cipher.key = Digest::SHA256.digest(KEY)
encrypted = cipher.update(data)+cipher.final
return encrypted
end
def decrypt_value1(data)
cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
cipher.decrypt
cipher.key = Digest::SHA256.digest(KEY)
decrypted = cipher.update(data)+cipher.final
data = Base64.decode64(decrypted)
return data
end
And java code:
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class EncryptionDecryption {
private static String salt;
private static int iterations = 65536 ;
private static int keySize = 256;
private static byte[] ivBytes;
// private static SecretKey secretKey;
private static final byte[] secretKey = "secret_key".getBytes();
public static void main(String []args) throws Exception {
salt = getSalt();
char[] message = "secret message".toCharArray();
System.out.println("Message: " + String.valueOf(message));
System.out.println("Encrypted: " + encrypt(message));
System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
}
public static String encrypt(char[] plaintext) throws Exception {
byte[] saltBytes = salt.getBytes();
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
//secretKey = skf.generateSecret(spec);
SecretKeySpec secretSpec = new SecretKeySpec(secretKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
AlgorithmParameters params = cipher.getParameters();
ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));
return DatatypeConverter.printBase64Binary(encryptedTextBytes);
}
public static String decrypt(char[] encryptedText) throws Exception {
System.out.println(encryptedText);
byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
SecretKeySpec secretSpec = new SecretKeySpec(secretKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));
byte[] decryptedTextBytes = null;
try {
decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return new String(decryptedTextBytes);
}
public static String getSalt() throws Exception {
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] salt = new byte[20];
sr.nextBytes(salt);
return new String(salt);
}
}
How can I have both of them work with each other, for example if I send an encrypted data to java app from rails app it should be able to decode it and vice-versa.
As far as you are using the same cypher algorithms and message padding characters, they should work just fine.
Also, it is better to use some key exchange protocol, like Diffie-Hellman. Both Ruby's OpenSSL wrapper and Java's Crypto lib support it. It is a safer way to generate per session keys.
PS. Your code as you have it, seems to be written for a standalone execution. For example, your Java code for decoding assumes that your string was encoded with the same instance's encode method. Because, your decode method is using IV that was initialized in encode method (ivBytes).
I'm currently working on an Android project for developing a Hybrid File Encryption. From this site, I've already created a hybrid encryption which only able to encrypt text/string. While all the keys are automatically generated within the code. Any idea on how to make the keys to be user input password based and able to encrypt files?
Here is the code:
// Some codes here
// Create key pair (public and private key) for RSA encryption and decryption
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
} catch (Exception e) {
Log.e(TAG, "RSA key pair error");
}
}
private void encHybrid() throws GeneralSecurityException {
// Create random secret key with AES algorithm
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, sr) ;
SecretKey cipherKey = kg.generateKey() ;
// Encrypt secret key asymmetry algorithm (RSA)
encryptedSecretKey = encrypt(cipherKey.getEncoded(), kp.getPublic());
textHybrid = etHybrid.getText().toString();
// Encrypt inputted text/string with symmetry algorithm using encrypted secret key
encryptedData = encrypt(textHybrid.getBytes(), cipherKey);
}
// Method to encrypt the text/string
public static byte[] encrypt(byte[] toEncrypt, SecretKey key)
throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES") ;
cipher.init(Cipher.ENCRYPT_MODE, key) ;
return cipher.doFinal(toEncrypt);
}
// Method to encrypt the secret key
public static byte[] encrypt(byte[] toEncrypt, PublicKey key)
throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA") ;
cipher.init(Cipher.ENCRYPT_MODE, key) ;
return cipher.doFinal(toEncrypt);
}
private void decryptHybrid() throws GeneralSecurityException {
// Decrypt secret key with private key
byte[] decryptedSecretKey = decrypt(encryptedSecretKey, kp.getPrivate()) ;
// Decrypted secret key will be stored in sKey
SecretKey sKey = new SecretKeySpec(decryptedSecretKey, "AES") ;
textHybrid = etHybrid.getText().toString();
// Decrypt encrypted text/string with decrypted secret key
byte[] decryptedData = decrypt(encryptedData, sKey) ;
}
// Method to decrypt the text/string
public static byte[] decrypt(byte[] toDecrypt, SecretKey key)
throws GeneralSecurityException {
Cipher deCipher = Cipher.getInstance("AES") ;
deCipher.init(Cipher.DECRYPT_MODE, key) ;
return deCipher.doFinal(toDecrypt);
}
// Method to decrypt the secret key
public static byte[] decrypt(byte[] toDecrypt, PrivateKey key)
throws GeneralSecurityException {
Cipher deCipher = Cipher.getInstance("RSA") ;
deCipher.init(Cipher.DECRYPT_MODE, key) ;
return deCipher.doFinal(toDecrypt);
}
You don't really want to generate the key pair from the password. The problem with that scheme is that there is no way to trust the public key. What is usually done is to encrypt the private key with a secret (symmetric) key that has been generated from a password.
So, in addition to the hybrid encryption you would have a scheme that does it entirely the other way around. It's a bit of code, but it should be readable enough. Or you could use PGP, which essentially performs the same kind of operations.
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class PassphraseWrapRSA {
private static KeyPair generateRSAKeyPair(final int size) {
KeyPairGenerator kpgen;
try {
kpgen = KeyPairGenerator.getInstance("RSA");
} catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException();
}
kpgen.initialize(size);
return kpgen.generateKeyPair();
}
public static byte[] generateSalt() {
final SecureRandom rng = new SecureRandom();
final byte[] salt = new byte[16];
rng.nextBytes(salt);
return salt;
}
private static SecretKey deriveAESKey(final byte[] salt,
final char[] password) {
try {
final SecretKeyFactory factory = SecretKeyFactory
.getInstance("PBKDF2WithHmacSHA1");
final KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
final SecretKey keyWrapKey = factory.generateSecret(spec);
final SecretKey secret = new SecretKeySpec(keyWrapKey.getEncoded(),
"AES");
return secret;
} catch (final Exception e) {
throw new IllegalStateException(e);
}
}
private static byte[] encryptRSAPrivateKey(final RSAPrivateKey rsaPrivateKey,
final SecretKey aesKey) {
try {
final Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
final SecureRandom ivGen = new SecureRandom();
final byte[] iv = new byte[c.getBlockSize()];
ivGen.nextBytes(iv);
c.init(Cipher.WRAP_MODE, aesKey, new IvParameterSpec(iv));
final byte[] wrappedKey = c.wrap(rsaPrivateKey);
return concat(iv, wrappedKey);
} catch (final GeneralSecurityException e) {
throw new IllegalStateException(e);
}
}
public static byte[] wrapRSAPrivateKey(final String passphrase,
final RSAPrivateKey rsaPrivateKey) {
// --- generate salt
final byte[] newSalt = generateSalt();
// --- derive symmetric key from salt and password
final SecretKey aesKey = deriveAESKey(newSalt,
passphrase.toCharArray());
final byte[] encryptedPrivate = encryptRSAPrivateKey(rsaPrivateKey, aesKey);
final byte[] saltedAndEncryptedPrivate = concat(newSalt,
encryptedPrivate);
return saltedAndEncryptedPrivate;
}
private static RSAPrivateKey decryptRSAPrivateKey(final byte[] encryptedRSAPrivateKey,
final SecretKey aesKey) throws InvalidKeyException {
try {
final Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
int offset = 0;
final byte[] iv = Arrays.copyOfRange(encryptedRSAPrivateKey, 0,
c.getBlockSize());
offset += c.getBlockSize();
c.init(Cipher.UNWRAP_MODE, aesKey, new IvParameterSpec(iv));
final Key key = c.unwrap(Arrays.copyOfRange(encryptedRSAPrivateKey, offset,
encryptedRSAPrivateKey.length), "RSA", Cipher.PRIVATE_KEY);
return (RSAPrivateKey) key;
} catch (final InvalidKeyException e) {
throw e;
} catch (final GeneralSecurityException e) {
throw new IllegalStateException(e);
}
}
public static RSAPrivateKey unwrapRSAPrivateKey(final String passphrase,
final byte[] saltedAndEncryptedPrivate) throws InvalidKeyException {
int offset = 0;
final byte[] backSalt = Arrays.copyOfRange(saltedAndEncryptedPrivate,
offset, 16);
offset += 16;
final SecretKey backAESKey = deriveAESKey(backSalt,
passphrase.toCharArray());
final byte[] backEncryptedPrivateKey = Arrays.copyOfRange(
saltedAndEncryptedPrivate, offset,
saltedAndEncryptedPrivate.length);
final RSAPrivateKey decryptedPrivate = decryptRSAPrivateKey(
backEncryptedPrivateKey, backAESKey);
return decryptedPrivate;
}
public static RSAPublicKey decodeRSAPublicKey(
final byte[] x509EncodedPUblicKey) throws InvalidKeySpecException {
try {
final KeyFactory rsaPublicKeyFactory = KeyFactory.getInstance("RSA");
final PublicKey pubKey = rsaPublicKeyFactory
.generatePublic(new X509EncodedKeySpec(x509EncodedPUblicKey));
return (RSAPublicKey) pubKey;
} catch (final InvalidKeySpecException e) {
throw e;
} catch (final GeneralSecurityException e) {
throw new IllegalStateException(e);
}
}
public static byte[] encodeRSAPublicKey(final RSAPublicKey rsaPublicKey) {
return rsaPublicKey.getEncoded();
}
private static byte[] concat(final byte[] a, final byte[] a2) {
final byte[] result = new byte[a.length + a2.length];
System.arraycopy(a, 0, result, 0, a.length);
System.arraycopy(a2, 0, result, a.length, a2.length);
return result;
}
public static void main(final String[] args) throws Exception {
// --- not required for Java 8
Security.addProvider(new BouncyCastleProvider());
// --- setup key pair (generated in advance)
final String passphrase = "owlstead";
final KeyPair kp = generateRSAKeyPair(1024);
final RSAPublicKey rsaPublicKey = (RSAPublicKey) kp.getPublic();
final RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) kp.getPrivate();
// --- encode and wrap
byte[] x509EncodedRSAPublicKey = encodeRSAPublicKey(rsaPublicKey);
final byte[] saltedAndEncryptedPrivate = wrapRSAPrivateKey(
passphrase, rsaPrivateKey);
// --- decode and unwrap
final RSAPublicKey retrievedRSAPublicKey = decodeRSAPublicKey(x509EncodedRSAPublicKey);
final RSAPrivateKey retrievedRSAPrivateKey = unwrapRSAPrivateKey(passphrase,
saltedAndEncryptedPrivate);
// --- check result
System.out.println(retrievedRSAPublicKey);
System.out.println(retrievedRSAPrivateKey);
}
}
WARNING: for demonstration purposes only, please implement using classes and a more flexible method of handling the protocol (include e.g. a version number) etc.
I have a working code example that uses DES (see below), but I want to specify the key data to use. How can I edit the code sample to do this?
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
public class DESEncryptionDecryption {
private static Cipher encryptCipher;
private static Cipher decryptCipher;
public static void main(String[] args) {
try {
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey secretKey = keygenerator.generateKey();
encryptCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedData = encryptData("Classified Information!");
decryptCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey);
decryptData(encryptedData);
}}}
Do not use a KeyGenerator, use a SecretKeyFactory:
String desKey = "0123456789abcdef"; // value from user
byte[] keyBytes = DatatypeConverter.parseHexBinary(desKey);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
SecretKey key = factory.generateSecret(new DESKeySpec(keyBytes));
Note that DES is not a secure algorithm. Consider using AES or Triple DES (DESede).
For DESede:
String desKey = "0123456789abcdef0123456789abcdef0123456789abcdef"; // user value (24 bytes)
byte[] keyBytes = DatatypeConverter.parseHexBinary(desKey);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(new DESedeKeySpec(keyBytes));
For AES, just do:
String aesKey = "0123456789abcdef0123456789abcdef"; // user value (16/24/32 bytes)
byte[] keyBytes = DatatypeConverter.parseHexBinary(aesKey);
SecretKey key = new SecretKeySpec(keyBytes, "AES");
I am new to cryptography, so I have a question:
How can I create my own key (let`s say like a string "1234"). Because I need to encrypt a string with a key (defined by me), save the encrypted string in a database and when I want to use it, take it from the database and decrypt it with the key known by me.
I have this code :
import java.security.InvalidKeyException;
import java.security.*;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
public class LocalEncrypter {
private static String algorithm = "PBEWithMD5AndDES";
//private static Key key = null;
private static Cipher cipher = null;
private static SecretKey key;
private static void setUp() throws Exception {
///key = KeyGenerator.getInstance(algorithm).generateKey();
SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
String pass1 = "thisIsTheSecretKeyProvidedByMe";
byte[] pass = pass1.getBytes();
SecretKey key = factory.generateSecret(new DESedeKeySpec(pass));
cipher = Cipher.getInstance(algorithm);
}
public static void main(String[] args)
throws Exception {
setUp();
byte[] encryptionBytes = null;
String input = "1234";
System.out.println("Entered: " + input);
encryptionBytes = encrypt(input);
System.out.println(
"Recovered: " + decrypt(encryptionBytes));
}
private static byte[] encrypt(String input)
throws InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] inputBytes = input.getBytes();
return cipher.doFinal(inputBytes);
}
private static String decrypt(byte[] encryptionBytes)
throws InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] recoveredBytes =
cipher.doFinal(encryptionBytes);
String recovered =
new String(recoveredBytes);
return recovered;
}
}
Exception in thread "main" java.security.spec.InvalidKeySpecException: Invalid key spec
at com.sun.crypto.provider.PBEKeyFactory.engineGenerateSecret(PBEKeyFactory.java:114)
at javax.crypto.SecretKeyFactory.generateSecret(SecretKeyFactory.java:335)
at LocalEncrypter.setUp(LocalEncrypter.java:22)
at LocalEncrypter.main(LocalEncrypter.java:28)
A KeyGenerator generates random keys. Since you know the secret key, what you need is a SecretKeyFactory. Get an instance for your algorithm (DESede), and then call its generateSecret méthode with an instance of DESedeKeySpec as argument:
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(new DESedeKeySpec(someByteArrayContainingAtLeast24Bytes));
Here is a complete example that works. As I said, DESedeKeySpec must be used with the DESede algorithm. Using a DESede key with PBEWithMD5AndDES makes no sense.
public class EncryptionTest {
public static void main(String[] args) throws Exception {
byte[] keyBytes = "1234567890azertyuiopqsdf".getBytes("ASCII");
DESedeKeySpec keySpec = new DESedeKeySpec(keyBytes);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(keySpec);
byte[] text = "Hello world".getBytes("ASCII");
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(text);
cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println(new String(decrypted, "ASCII"));
}
}
Well, I found the solution after combining from here and there. The encrypted result will be formatted as Base64 String for safe saving as xml file.
package cmdCrypto;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.xml.bind.DatatypeConverter;
public class CmdCrypto {
public static void main(String[] args) {
try{
final String strPassPhrase = "123456789012345678901234"; //min 24 chars
String param = "No body can see me";
System.out.println("Text : " + param);
SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede");
SecretKey key = factory.generateSecret(new DESedeKeySpec(strPassPhrase.getBytes()));
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
String str = DatatypeConverter.printBase64Binary(cipher.doFinal(param.getBytes()));
System.out.println("Text Encryted : " + str);
cipher.init(Cipher.DECRYPT_MODE, key);
String str2 = new String(cipher.doFinal(DatatypeConverter.parseBase64Binary(str)));
System.out.println("Text Decryted : " + str2);
} catch(Exception e) {
e.printStackTrace();
}
}
}