AES密钥字符串抛出异常

时间:2014-01-15 13:33:05

标签: java cryptography

我正在尝试编写一个使用AES加密/解密文本的类。

我想生成一个密钥,将密钥存储在数据库列中,并使用该密钥加密/解密包含密钥的数据库行中的相应文本。

以下是我编写的用于生成密钥并执行加密和解密任务的类。

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class StringDecryptor {

    public static String encrypt(String text, String key) {
        Key aesKey = null;
        Cipher cipher = null;
        byte[] encrypted = null;
        try {
            aesKey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, aesKey);
            encrypted = cipher.doFinal(text.getBytes());
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new String(encrypted);
    }

    public static String decrypt(String text, String key) {
        Key aesKey = null;
        Cipher cipher;
        String decrypted = null;
        try {
            aesKey  = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, aesKey);
            decrypted = new String(cipher.doFinal(text.getBytes()));
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchPaddingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvalidKeyException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalBlockSizeException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (BadPaddingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return decrypted;
    }

    public static String generateKey() {
        SecretKey secretKey = null;
        try {
            secretKey = KeyGenerator.getInstance("AES").generateKey();
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        }
        String keyString = bytesToString(secretKey.getEncoded());
        return keyString;
    }

    public static String bytesToString(byte[] b) {
        String decoded = null;
        try {
            decoded = new String(b, "UTF-8");            
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(StringDecryptor.class.getName()).log(Level.SEVERE, null, ex);
        }
        return decoded;
    }

    public static void main(String args[]) {
        String key = generateKey();
        System.out.println("key: " + key);
        String str = "This is the original string...";
        String enc = encrypt(str, key);
        System.out.println("enc: " + enc);
        String dec = decrypt(enc, key);
        System.out.println("dec: " + dec);
    }
}

此代码抛出以下异常。

SEVERE: null
java.security.InvalidKeyException: Invalid AES key length: 26 bytes
    at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
    at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
    at javax.crypto.Cipher.implInit(Cipher.java:786)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
    at javax.crypto.Cipher.init(Cipher.java:1213)
    at javax.crypto.Cipher.init(Cipher.java:1153)
    at com.innolabmm.software.luckydraw.utils.StringDecryptor.encrypt(StringDecryptor.java:27)
    at com.innolabmm.software.luckydraw.utils.StringDecryptor.main(StringDecryptor.java:95)

Exception in thread "main" java.lang.NullPointerException
    at java.lang.String.<init>(String.java:556)
    at com.innolabmm.software.luckydraw.utils.StringDecryptor.encrypt(StringDecryptor.java:42)
    at com.innolabmm.software.luckydraw.utils.StringDecryptor.main(StringDecryptor.java:95)
Java Result: 1

有没有办法生成一个不会导致AES密钥转换抛出异常的密钥字符串?

4 个答案:

答案 0 :(得分:1)

为了能够使用AES算法加密,您需要将密钥基于至少128位字符串。 (其他价值观也是合法的,但我没有这些价值)

要将其转换为您提供的键字符串中所需的字符数,您可以将128除以8

128 / 8 = 16 alpha-numeric characters

这可以解决您的问题。

修改

对BASE64的评论回答: BASE64是一种不同的字符串编码方式。 BASE64编码的结果可能是128位字符串,但默认情况下不是。实际上没有BASE64编码结果的默认长度。结果可能是8个字符或512,或者其他符合BASE64编码规则的输出,这一切都取决于您编码的字符串。

答案 1 :(得分:0)

AES需要一个长度为128,192或256位的密钥。尝试使用更长的密钥。

答案 2 :(得分:0)

以下是生成AES密钥的工作示例:

private static SecretKeySpec key;
private static final int ENCRYPTION_KEY_SIZE = 128;

private static SecretKey generateKey() throws NoSuchAlgorithmException {

KeyGenerator keyGenerator = null;
keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(ENCRYPTION_KEY_SIZE);

SecretKey key = keyGenerator.generateKey();
return key;
}

public void createNewEncrytionKey() throws NoSuchAlgorithmException, Exception {
SecretKey newKey = generateKey();
saveKey(newKey);
LogUtil.logMessage("createNewEncrytionKey", "New random encryption key was created.", logger);
key = (SecretKeySpec) newKey;
}

然后您可以使用该密钥加密\解密您的代码(在示例中,我将加密值转换为十六进制格式:

/**
     * Encrypt a given text
     * 
     * @param value String to encrypt
     * @return encrypted String value
     */
    public static String encrypt(String value) {

    if (null == key) {
        throw new RuntimeException("Secret encryption key not found!");
    }

    Cipher c;
    String result = null;
    try {
        c = Cipher.getInstance(ENCRYPTION_ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);
        // 1. convert value to byte array
        byte[] bvalue = value.getBytes();
        // 2. convert to encrypted byte array
        byte[] encrypted = c.doFinal(bvalue);
        // 3. convert to hexadecimal representation
        result = toHexString(encrypted);
    } catch (Exception e) {
        LogUtil.logError("encrypt", e, logger);
    }

    return result;
    }


/**
     * Decrypt a given text
     * 
     * @param value encrypted String value
     * @return Decrypted value
     */
    public static String decrypt(String value) {

    if (null == key) {
        throw new RuntimeException("Secret encryption key not found!");
    }

    Cipher c;
    String result = null;
    try {
        // 1. convert hex to encrypted byte array
        byte[] encrypted = hexToByteArray(value);
        // 2. convert to decrypted byte array
        c = Cipher.getInstance(ENCRYPTION_ALGORITHM);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decrypted = c.doFinal(encrypted);
        // 3. convert to plain string
        result = bytesToString(decrypted);
    } catch (Exception e) {
        LogUtil.logError("decrypt", e, logger);
    }

    return result;
    }

答案 3 :(得分:0)

您的问题与AES或加密无关。但是,您尝试将随机字节序列解释为UTF-8编码字符(b是AES密钥):

String decoded = new String(b, "UTF-8");

这不会起作用,因为你的字节数组很可能包含几个字节序列,这些字节序列对UTF-8无效。如果需要字节数组内容的字符串表示,则必须使用例如数据编码数据。 base64或十六进制编码。