【发布时间】:2016-05-16 07:14:19
【问题描述】:
我正在尝试在 java 中实现 AES 256 位 CBC 算法。我想做这样的东西。 Click here
我正在使用来自多个 SO 线程的以下程序,以下是我用来加密/解密的代码。根据@dave_thompson 的建议更新了代码,但对于 IV 的长度仍然存在相同的错误。
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Scanner;
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 = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,};
private static SecretKey secretKey;
public static void main(String []args) throws Exception {
Scanner in = new Scanner(System.in);
salt = getSalt();
String s = in.nextLine();
char[] message = s.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();
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);
}
}
当前代码的问题向我显示以下错误,但如果我将 IV 改回 16 位,它可以工作。
以下是我所指的 SO 线程。
【问题讨论】:
-
请不要混淆位和字节。如果 CBC 模式有这样的东西,16 位 IV 基本上是没用的。
-
OMG 将 16 个字节设置为 0,或十六个 对 十六进制字符。嘘。
-
(tweaked) 该网站正在以十六进制输入密钥和 IV,因此您的示例有 128 位密钥而不是 256,以及 128 位(零)作为 IV 而不是 256。它直接使用密钥,不执行 PBKDF2 或任何其他密钥派生,并且它执行零填充而不是 PKCS#5 填充。简而言之,如果您想做该网站正在做的事情,那么您的代码中的几乎所有内容都是错误的,并且如果您想做与您代码中的内容相近的事情,那么该网站就完全错误了。 选择一个。但请注意,零填充并不常见,通常是个坏主意。
-
@dave_thompson_085 非常感谢您让我意识到这一点。我需要让它像那个网站一样工作。那我该怎么做呢?对特定想法的任何推动都会有所帮助。
标签: java encryption cryptography aes