使用我自己的密钥

我想用我自己的密钥使用 AES 加密一个字符串。但是钥匙的位长有点问题。你能看一下我的代码,看看我需要修改什么吗。

public static void main(String[] args) throws Exception {
String username = "bob@google.org";
String password = "Password1";
String secretID = "BlahBlahBlah";
String SALT2 = "deliciously salty";


// Get the Key
byte[] key = (SALT2 + username + password).getBytes();
System.out.println((SALT2 + username + password).getBytes().length);


// Need to pad key for AES
// TODO: Best way?


// Generate the secret key specs.
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");


// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);


byte[] encrypted = cipher.doFinal((secrectID).getBytes());
System.out.println("encrypted string: " + asHex(encrypted));


cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] original = cipher.doFinal(encrypted);
String originalString = new String(original);
System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " + asHex(original));
}

现在我得到一个异常“ AES 密钥长度无效: 86字节”。我需要垫我的钥匙吗? 我应该怎么做?

我还需要为 ECB 或 CBC 设置什么吗?

谢谢

227395 次浏览

You should use a KeyGenerator to generate the Key,

AES key lengths are 128, 192, and 256 bit depending on the cipher you want to use.

Take a look at the tutorial here

Here is the code for Password Based Encryption, this has the password being entered through System.in you can change that to use a stored password if you want.

        PBEKeySpec pbeKeySpec;
PBEParameterSpec pbeParamSpec;
SecretKeyFactory keyFac;


// Salt
byte[] salt = {
(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
(byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
};


// Iteration count
int count = 20;


// Create PBE parameter set
pbeParamSpec = new PBEParameterSpec(salt, count);


// Prompt user for encryption password.
// Collect user password as char array (using the
// "readPassword" method from above), and convert
// it into a SecretKey object, using a PBE key
// factory.
System.out.print("Enter encryption password:  ");
System.out.flush();
pbeKeySpec = new PBEKeySpec(readPassword(System.in));
keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);


// Create PBE Cipher
Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");


// Initialize PBE Cipher with key and parameters
pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);


// Our cleartext
byte[] cleartext = "This is another example".getBytes();


// Encrypt the cleartext
byte[] ciphertext = pbeCipher.doFinal(cleartext);

Edit:

As written in the comments the old code is not "best practice". You should use a keygeneration algorithm like PBKDF2 with a high iteration count. You also should use at least partly a non static (meaning for each "identity" exclusive) salt. If possible randomly generated and stored together with the ciphertext.

    SecureRandom sr = SecureRandom.getInstanceStrong();
byte[] salt = new byte[16];
sr.nextBytes(salt);


PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8);
SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);
Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.ENCRYPT_MODE, key);

===========

Old Answer

You should use SHA-1 to generate a hash from your key and trim the result to 128 bit (16 bytes).

Additionally don't generate byte arrays from Strings through getBytes() it uses the platform default Charset. So the password "blaöä" results in different byte array on different platforms.

byte[] key = (SALT2 + username + password).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit


SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

Edit: If you need 256 bit as key sizes you need to download the "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" Oracle download link, use SHA-256 as hash and remove the Arrays.copyOf line. "ECB" is the default Cipher Mode and "PKCS5Padding" the default padding. You could use different Cipher Modes and Padding Modes through the Cipher.getInstance string using following format: "Cipher/Mode/Padding"

For AES using CTS and PKCS5Padding the string is: "AES/CTS/PKCS5Padding"

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;
import java.io.BufferedReader;
import java.io.FileReader;


public class AESFile
{
private static String algorithm = "AES";
private static byte[] keyValue=new byte[] {'0','2','3','4','5','6','7','8','9','1','2','3','4','5','6','7'};// your key


// Performs Encryption
public static String encrypt(String plainText) throws Exception
{
Key key = generateKey();
Cipher chiper = Cipher.getInstance(algorithm);
chiper.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = chiper.doFinal(plainText.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}


// Performs decryption
public static String decrypt(String encryptedText) throws Exception
{
// generate key
Key key = generateKey();
Cipher chiper = Cipher.getInstance(algorithm);
chiper.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
byte[] decValue = chiper.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}


//generateKey() is used to generate a secret key for AES algorithm
private static Key generateKey() throws Exception
{
Key key = new SecretKeySpec(keyValue, algorithm);
return key;
}


// performs encryption & decryption
public static void main(String[] args) throws Exception
{
FileReader file = new FileReader("C://myprograms//plaintext.txt");
BufferedReader reader = new BufferedReader(file);
String text = "";
String line = reader.readLine();
while(line!= null)
{
text += line;
line = reader.readLine();
}
reader.close();
System.out.println(text);


String plainText = text;
String encryptedText = AESFile.encrypt(plainText);
String decryptedText = AESFile.decrypt(encryptedText);


System.out.println("Plain Text : " + plainText);
System.out.println("Encrypted Text : " + encryptedText);
System.out.println("Decrypted Text : " + decryptedText);
}
}

This wll work.

public class CryptoUtils {


private  final String TRANSFORMATION = "AES";
private  final String encodekey = "1234543444555666";
public  String encrypt(String inputFile)
throws CryptoException {
return doEncrypt(encodekey, inputFile);
}




public  String decrypt(String input)
throws CryptoException {
// return  doCrypto(Cipher.DECRYPT_MODE, key, inputFile);
return doDecrypt(encodekey,input);
}


private  String doEncrypt(String encodekey, String inputStr)   throws CryptoException {
try {


Cipher cipher = Cipher.getInstance(TRANSFORMATION);


byte[] key = encodekey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit


SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");


cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);


byte[] inputBytes = inputStr.getBytes();
byte[] outputBytes = cipher.doFinal(inputBytes);


return Base64Utils.encodeToString(outputBytes);


} catch (NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | BadPaddingException
| IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file", ex);
}
}




public  String doDecrypt(String encodekey,String encrptedStr) {
try {


Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
dcipher = Cipher.getInstance("AES");
byte[] key = encodekey.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit


SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");


dcipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
// decode with base64 to get bytes


byte[] dec = Base64Utils.decode(encrptedStr.getBytes());
byte[] utf8 = dcipher.doFinal(dec);


// create new string based on the specified charset
return new String(utf8, "UTF8");


} catch (Exception e) {


e.printStackTrace();


}
return null;
}
}
    byte[] seed = (SALT2 + username + password).getBytes();
SecureRandom random = new SecureRandom(seed);
KeyGenerator generator;
generator = KeyGenerator.getInstance("AES");
generator.init(random);
generator.init(256);
Key keyObj = generator.generateKey();

MD5, AES, no padding

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.io.Charsets.UTF_8;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;


public class PasswordUtils {


private PasswordUtils() {}


public static String encrypt(String text, String pass) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(ENCRYPT_MODE, key);


byte[] encrypted = cipher.doFinal(text.getBytes(UTF_8));
byte[] encoded = Base64.getEncoder().encode(encrypted);
return new String(encoded, UTF_8);


} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException("Cannot encrypt", e);
}
}


public static String decrypt(String text, String pass) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(DECRYPT_MODE, key);


byte[] decoded = Base64.getDecoder().decode(text.getBytes(UTF_8));
byte[] decrypted = cipher.doFinal(decoded);
return new String(decrypted, UTF_8);


} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
throw new RuntimeException("Cannot decrypt", e);
}
}
}