尝试在Java中解密时出现“错误的算法”错误。

4
我首先要描述我遇到的问题,然后介绍一下我尝试做什么。最后,我将粘贴一些相关的代码片段。
我正在尝试使用https://dev59.com/xXNA5IYBdhLWcg3wWsiK#992413中指定的方法来实现秘密密钥加密/解密。如果我按原样使用该示例,它可以工作(尽管我注意到我需要重新实例化Cipher类,否则解密会产生垃圾)。然而,在我的实现中,我得到了以下异常:
java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
    at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:77)
    at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
    at com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)
    at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:217)
    at javax.crypto.Cipher.implInit(Cipher.java:790)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:848)
    at javax.crypto.Cipher.init(Cipher.java:1347)
    at javax.crypto.Cipher.init(Cipher.java:1281)
    at securitytest.SecurityManager.getCipher(SecurityManager.java:175)
    at securitytest.SecurityManager.decryptSecretKey(SecurityManager.java:379)
    at securitytest.SecurityManager.<init>(SecurityManager.java:82)
    at securitytest.Test.main(Test.java:44)

为了防止显而易见的问题,是的,我确实使用相同的算法:事实上,我为AES/CBC/PKCS5Padding分配了一个常量,并将其用于实例化加密和解密的Cipher类。 我还尝试仅使用AES来实例化Cipher进行解密,但也没有起作用。
我正在尝试使用AES/CBC/PKCS5Padding密码保护秘密密钥。我生成一个随机盐和初始化向量。在加密密钥后,我将初始化向量(一组字节)附加到加密值(另一组字节,创建一个新数组)上。然后我对该值进行Base64编码,并将其存储在Sqlite数据库中,同时存储盐(出于简单起见,我将其存储为逗号分隔的值字符串)。但是当我尝试解密时,我得到了上面的异常。我可以验证,在调用加密方法之后和解密方法之前,以下值完全相同(转换为Base64以便我可以打印出来):
  • 初始化向量
  • 加密的秘密密钥(即密文)
我已经尝试过Java 6和7:两者都给出了相同的结果。我还排除了无限制强度管辖策略文件的问题。实际上,如果我用另一种算法替换"AES"并相应调整盐的长度(例如IV长度为8的"Blowfish",它会产生java.security.InvalidKeyException: Wrong algorithm: Blowfish required),我会得到类似的错误。
谷歌无法帮助我解决这个问题。如果有人能给我一些提示,我将非常感激。
以下是一些代码片段(抱歉,它有点粗糙):
private static final int INIT_VECTOR_LENGTH = 16;
private static final int PRIVATE_KEY_LENGTH = 128;
private static final int SALT_LENGTH = 16;
private static final int PBE_KEYSPEC_ITERATIONS = 65536;

private static final String CIPHER_ALGORITHM = "AES";
private static final String CIPHER_ALGORITHM_MODE = "CBC";
private static final String CIPHER_ALGORITHM_PADDING = "PKCS5Padding";
private static final String DIGEST = "SHA1";
private static final String PLAINTEXT_ENCODING = "UTF8";
private static final String PRNG = DIGEST + "PRNG";
private static final String SECRET_KEY_FACTORY = "PBKDF2WithHmac" + DIGEST;

private static final String CIPHER = CIPHER_ALGORITHM + "/" + CIPHER_ALGORITHM_MODE + "/" + CIPHER_ALGORITHM_PADDING;

private IvParameterSpec ivSpec;
private final BASE64Encoder encoder = new BASE64Encoder();
private final BASE64Decoder decoder = new BASE64Decoder();

private Cipher getCipher(SecretKey key, int mode) {

    Cipher cipher = null;

    try {
        cipher = Cipher.getInstance(CIPHER);
    }
    catch (NoSuchAlgorithmException e) {System.err.println(System.err.println(e.getMessage());}
    catch (NoSuchPaddingException e) {System.err.println(e.getMessage());}

    try {
        if (mode == Cipher.ENCRYPT_MODE) {
            cipher.init(mode, key);
            AlgorithmParameters params = cipher.getParameters();
            ivSpec = params.getParameterSpec(IvParameterSpec.class);
        }
        else {
            /* This is my point-of-failure. */
            cipher.init(mode, key, ivSpec);
        }
    }
    catch (InvalidKeyException e) {System.err.println(e.getMessage());}
    catch (InvalidAlgorithmParameterException e) {System.err.println(e.getMessage());}
    catch (InvalidParameterSpecException e) {System.err.println(e.getMessage());}

    return cipher;

}

private SecurityData.Secrets generateSecrets(SecretKey decryptedKey, byte[] salt, String passphrase) {

    /* Generate a new key for encrypting the secret key. */
    byte[] raw = null;
    PBEKey key = null;
    PBEKeySpec password = new PBEKeySpec(passphrase.toCharArray(), salt, PBE_KEYSPEC_ITERATIONS, PRIVATE_KEY_LENGTH);
    SecretKeyFactory factory = null;
    byte[] initVector = null;
    byte[] secretKeyBytes = decryptedKey.getEncoded();

    try {
        factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY);
        key = (PBEKey) factory.generateSecret(password);
    }
    catch (NoSuchAlgorithmException e) {System.err.println(e.getMessage());}
    catch (InvalidKeySpecException e) {System.err.println(e.getMessage());}

    SecretKeySpec newKey = new SecretKeySpec(key.getEncoded(), CIPHER_ALGORITHM);

    /* Encrypt the secret key. */
    IvParameterSpec ivSpec = new IvParameterSpec(initVector);
    Cipher cipher = getCipher(newKey, ivSpec, Cipher.ENCRYPT_MODE);

    try {
        raw = cipher.doFinal(secretKeyBytes);
    }
    catch (IllegalBlockSizeException e) {System.err.println(e.getMessage());}
    catch (BadPaddingException e) {System.err.println(e.getMessage());}

    return new SecurityData.Secrets(encoder.encode(concatByteArrays(initVector, raw)), joinByteArray(salt));

}

private SecretKey decryptSecretKey(String encryptedKey, String salt, String passphrase) {

    /* Get initialisation vector. */
    byte[] raw = null, decoded = null, initVector = new byte[INIT_VECTOR_LENGTH];
    try {
        decoded = decoder.decodeBuffer(encryptedKey);
    } catch (IOException e) {System.err.println(e.getMessage());}
    System.arraycopy(decoded, 0, initVector, 0, INIT_VECTOR_LENGTH);
    raw = new byte[decoded.length-INIT_VECTOR_LENGTH];
    System.arraycopy(decoded, INIT_VECTOR_LENGTH, raw, 0, decoded.length-INIT_VECTOR_LENGTH);
    IvParameterSpec ivSpec = new IvParameterSpec(initVector);

    /* Generate the key. */
    byte[] rawSalt = splitByteArrayString(salt);
    PBEKeySpec password = new PBEKeySpec(passphrase.toCharArray(), rawSalt, PBE_KEYSPEC_ITERATIONS, PRIVATE_KEY_LENGTH);
    SecretKeyFactory factory = null;
    PBEKey key = null;
    try {
        factory = SecretKeyFactory.getInstance(SECRET_KEY_FACTORY);
        key = (PBEKey) factory.generateSecret(password);
    }
    catch (NoSuchAlgorithmException e) {System.err.println(e.getMessage());}
    catch (InvalidKeySpecException e) {System.err.println(e.getMessage());}

    Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);

    /* Decrypt the message. */
    byte[] stringBytes = null;
    try {
        stringBytes = cipher.doFinal(raw);
    }
    catch (IllegalBlockSizeException e) {System.err.println(e.getMessage());}
    catch (BadPaddingException e) {System.err.println(e.getMessage());}

    /* Converts the decoded message to a String. */
    String clear = null;
    try {
        clear = new String(stringBytes, PLAINTEXT_ENCODING);
    }
    catch (UnsupportedEncodingException e) {System.err.println(e.getMessage());}

    return new SecretKeySpec(clear.getBytes(), CIPHER_ALGORITHM);

}

你引用的我的例子确实会重新实例化和重新初始化“Cipher”,并且是“原样”的。 - erickson
1个回答

6
< p > SecretKey 对象需要在其 getAlgorithm() 方法中返回 "AES"。这就是为令示例包含以下步骤的原因:

SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

有趣的是,SecretKey 对象报告其算法为 SecretKeyFactory 算法的 "PBKDF2WithHmacSHA1"。 - phantom-99w
1
好的。如果您没有提供密钥工厂所需的信息,它怎么能够生成具有正确算法的密钥呢?而且也没有API可以让您这样做。这就是为什么需要创建“SecretKeySpec”的原因。 - erickson
啊,是的,我现在明白你的意思了!非常感谢! - phantom-99w

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接