如何在java中用CBC(32位IV和密钥)实现AES 128位?
我正在尝试用java实现AES 256位CBC算法。我想做这样的东西 下面是示例运行的图片。 我正在使用来自多个SO线程的以下程序,下面是我用来加密/解密的代码。按照@dave_thompson的建议更新了代码,但对于IV的长度仍然存在相同的错误如何在java中用CBC(32位IV和密钥)实现AES 128位?,java,encryption,cryptography,aes,Java,Encryption,Cryptography,Aes,我正在尝试用java实现AES 256位CBC算法。我想做这样的东西 下面是示例运行的图片。 我正在使用来自多个SO线程的以下程序,下面是我用来加密/解密的代码。按照@dave_thompson的建议更新了代码,但对于IV的长度仍然存在相同的错误 import java.security.AlgorithmParameters; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; i
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线程
在加密中定义的IV实际上不会传递给密码。。因此,密码会为您生成一个(即16字节)。CBC模式加密的IV大小与块大小相同。AES是Rijndael的一个子集,具有某些限制。这些限制之一是块大小始终为128位 Rijndael在AES参数之外使用时未标准化。这意味着它通常没有实现。Oracle的Java不在AES边界之外实现Rijndael。蹦蹦跳跳的城堡有,但是 因此,您唯一能做的就是使用Bouncy Castle提供程序的所谓轻量级API中的Rijndael。基本上,您可以通过Bouncy的专有接口直接调用Bouncy Castle提供程序的底层实现 警告:下面的代码使用静态(归零)密钥和IV。它仅用于演示块大小为256位的Rijndael密码。它不遵循(加密)最佳实践
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.RijndaelEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
public class RijndaelTestJava {
private static final boolean FOR_ENCRYPTION = true;
public static void main(String[] args) throws Exception {
rijndael256BouncyLW();
rijndael256BouncyProvider();
}
private static void rijndael256BouncyLW() throws InvalidCipherTextException {
{
RijndaelEngine rijndael256 = new RijndaelEngine(256);
BufferedBlockCipher rijndael256CBC =
new BufferedBlockCipher(
new CBCBlockCipher(rijndael256));
KeyParameter key = new KeyParameter(new byte[256 / Byte.SIZE]);
rijndael256CBC.init(FOR_ENCRYPTION, new ParametersWithIV(key,
new byte[256 / Byte.SIZE]));
byte[] in = new byte[64]; // two blocks
byte[] out = new byte[64]; // two blocks
int off = rijndael256CBC.processBytes(in, 0, in.length, out, 0);
off += rijndael256CBC.doFinal(out, off);
System.out.println(Hex.toHexString(out));
}
}
private static void rijndael256BouncyProvider() throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
InvalidAlgorithmParameterException, IllegalBlockSizeException,
BadPaddingException {
{
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7Padding");
SecretKeySpec key = new SecretKeySpec(new byte[256 / Byte.SIZE],
"Rijndael");
IvParameterSpec iv = new IvParameterSpec(new byte[256 / Byte.SIZE]);
// throws an InvalidAlgorithmParameterException: IV must be 16 bytes long.
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] out = cipher.doFinal("owlsteead"
.getBytes(StandardCharsets.US_ASCII));
System.out.println(Hex.toHexString(out));
}
}
}
好的,现在我们有一个特定的目标:AES-CBC(尽管您的示例数据只有一个块,所以CBC并不重要),128位密钥以十六进制表示,(128位)IV全部为零,零填充(如果是精确的块,则省略) 结果是什么
encrypt hex->e6b426aca323815fd6583cbcc4293c8d
decrypt chars->Hello world
感谢回复@Ebbe,我如何使我的算法与32字节IV一起工作。如何使其与256字节一起工作。有什么想法吗?AES总是使用16字节的IV。。这与您在我的问题中看到的关键尺寸无关,客户要求使用32位iv。如何根据此行为进行更改?您能建议一些更改吗。如果您能对此提供一些指导,那就太好了。一些链接会很有用。@KaranMer 32位IV for AES是不可能的,32字节也是不可能的。另外,使用值为48的字节,就像你正在做的那样,虽然合法,但会很奇怪。在密码学(以及其他计算领域)中,以十六进制写入二进制值是很常见的,并且零的32个十六进制数字(有时称为Nybles)是16字节或128位,这对于AES来说完全正确;我敢打赌这就是你的客户想要的,而你只是误解了。由于IV是一个字节数组,Java将原始数组默认为零,因此您可以使用新字节[16]。请不要混淆位和字节。如果CBC-mode.OMG的16字节设置为0,或者16对十六进制字符,那么16位IV基本上是无用的。Sheesh.(调整)该网站正在以十六进制输入密钥和IV,因此您的示例具有128位密钥而不是256,128位(零)作为IV而不是256。它直接使用密钥,不进行PBKDF2或任何其他密钥派生,它进行零填充而不是PKCS#5填充。简言之,如果你想做网站正在做的事情,那么代码中的几乎所有内容都是错误的,如果你想做一些接近代码中内容的事情,那么网站是完全错误的。挑一个。但请注意,零填充并不常见,而且通常是个坏主意。@dave_thompson_085非常感谢您让我意识到这一点。我需要让它像那个网站一样工作。那我该怎么做呢?任何对特定想法的推动都会有帮助。对不起,太懒了,无法处理异常。确保区分编程异常和与输入相关的异常。(已修复)这是Rijndael的一个很好的答案,只是您将密钥和IV保持为常量全零,这不是很安全,但我怀疑这是问题所在;任何知道非AES分析Rijndael是可能的人都知道指定“Rijndael”而不是“AES”。@dave_thompson_085有一些API使用Rijndael类实现AES功能。对于那些类(例如.NET中的
RijndaelManaged
,当然还有PHP中的mcrypt),使用更大的块大小和更大的键大小是一个很容易的错误。mcrypt在其原始API示例代码中甚至有256位的块大小(但同样,很少有mcrypt不严重缺乏的地方)。
encrypt hex->e6b426aca323815fd6583cbcc4293c8d
decrypt chars->Hello world