“错误的算法”尝试在Java中解密时出错 [英] "Wrong algorithm" Error when trying to Decrypt in Java

查看:500
本文介绍了“错误的算法”尝试在Java中解密时出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我首先要描述我所遇到的问题,然后给我想要做的一些背景。最后我将粘贴一些相关的代码片段。



我正在使用 https://stackoverflow.com/a/992413/171993 。如果我按原样使用这个例子,它可以工作(尽管我注意到我需要重新实例化 Cipher 类,否则解密会产生垃圾)。但是,在我的实现中,我得到以下异常:

  java.security.InvalidKeyException:错误的算法:AES或Rijndael需要
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)
在com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:217)
在javax.crypto.Cipher .implInit(Cipher.java:790)
在javax.crypto.Cipher.chooseProvider(Cipher.java:848)
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 实例化密码进行解密,但这并不奏效。



我想要做的是使用AES / CBC / PKCS5Padding密码保护密钥。我生成一个随机盐和初始化向量。加密密钥后,我将初始化向量(字节数组)附加到加密值(也是一个字节数组,创建一个新数组)。然后,我将此值编码到Base64中,并将其存储在Sqlite数据库中,连同salt(为了简单起见,我以逗号分隔的值存储字符串)。但是当我尝试解密时,我得到以上异常。我可以在我调用加密方法后直接在解密方法之前验证,以下值完全相同(当转换为Base64时,我可以打印出来):



  1. 初始化向量

  2. 加密的密钥(即密文) / li>

我已经尝试过Java 6和7:都提供相同的结果。我也排除了无限的力量管理政策文件作为一个问题。事实上,如果我用另一种算法代替AES并相应地调整盐的长度(例如Blowfish,IV长度为8,产生 java.security.InvalidKeyException:错误的算法:需要Blowfish )。



Google无法帮助我解决这个问题。如果有人可以看出这一点,我将非常感激。



这是一些代码段(我的道歉,这有点粗糙):

  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){

密码加密= 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 {
/ *这是我的失败点。 * /
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 decryptptedKey,byte [] salt,字符串密码){

/ *生成用于加密密钥的新密钥。 * /
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 = decryptptedKey.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);

/ *加密秘密密钥。 * /
IvParameterSpec ivSpec = new IvParameterSpec(initVector);
密码密码= 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() );}

返回新的SecurityData.Secrets(encoder.encode(concatByteArrays(initVector,raw)),joinByteArray(salt));

}

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

/ *获取初始化向量。 * /
byte [] raw = null,decoded = null,initVector = new byte [INIT_VECTOR_LENGTH];
try {
decoding = decoder.decodeBuffer(encryptedKey);
} catch(IOException e){System.err.println(e.getMessage());}
System.arraycopy(解码,0,initVector,0,INIT_VECTOR_LENGTH);
raw = new byte [decoded.length-INIT_VECTOR_LENGTH];
System.arraycopy(decode,INIT_VECTOR_LENGTH,raw,0,decode.length-INIT_VECTOR_LENGTH);
IvParameterSpec ivSpec = new IvParameterSpec(initVector);

/ *生成密钥。 * /
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() );}

密码密码= getCipher(key,Cipher.DECRYPT_MODE);

/ *解密消息。 * /
byte [] stringBytes = null;
try {
stringBytes = cipher.doFinal(raw);
}
catch(IllegalBlockSizeException e){System.err.println(e.getMessage());}
catch(BadPaddingException e){System.err.println(e.getMessage() );}

/ *将解码的消息转换为字符串。 * /
String clear = null;
try {
clear = new String(stringBytes,PLAINTEXT_ENCODING);
}
catch(UnsupportedEncodingException e){System.err.println(e.getMessage());}

返回新的SecretKeySpec(clear.getBytes(),CIPHER_ALGORITHM);

}


解决方案

code> SecretKey 对象需要从其 getAlgorithm()方法返回AES。这就是为什么这个例子有这些步骤:

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


I am first going to describe the problem which I have, and then give some background to what I am trying to do. Finally I shall paste some relevant code snippets.

I am trying to implement secret key encryption/decryption using the method specified in https://stackoverflow.com/a/992413/171993. If I use that example as-is, it works (although I did notice that I need to re-instantiate the Cipher class, otherwise the decryption produces garbage). However, in my implementation I get the following exception:

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)

To beat off the obvious question, yes, I do use the same algorithm: in fact, I assigned AES/CBC/PKCS5Padding to a constant and use that for instantiating both the Cipher class for encryption and decryption. I have also tried using only AES instantiate Cipher for the decryption, but that did not work either.

What I am trying to do is to password-protect a secret key by using AES/CBC/PKCS5Padding. I generate a random salt and initialisation vector. After encrypting the secret key, I append the initialisation vector (an array of bytes) to the encrypted value (also an array of bytes, creating a new array). I then encode this value in Base64 and store it in a Sqlite database, along with the salt (which, for the sake of simplicity, I store as a comma-separated string of values). However when I try to decrypt, I get the above exception. I can verify that directly after my call to the encryption method and directly before the decryption method, the following values are exactly the same (when converted to Base64 so that I can print it out):

  1. The salt
  2. The initialisation vector
  3. The encrypted secret key (i.e. the cipher text)

I have tried both Java 6 and 7: both give the same results. I have also ruled out the unlimited strength jurisdiction policy files as an issue. In fact, I get a similar error if I substitute "AES" with another algorithm and adjust the length of the salt accordingly (for example "Blowfish" with IV length 8, which produces java.security.InvalidKeyException: Wrong algorithm: Blowfish required).

Google has not been able to help me with this problem. If anyone can shed some light on this, I would be very appreciative.

Here are some code snippets (my apologies, it is a little rough):

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);

}

解决方案

The SecretKey object needs to return "AES" from its getAlgorithm() method. That's why the example has these steps:

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

这篇关于“错误的算法”尝试在Java中解密时出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆