如何使用 CBC 实现 Java 256 位 AES 加密

2022-09-03 07:19:29

我已经阅读了以下线程,它们有所帮助,但我正在寻找更多信息。

如何为黑莓编写AES / CBC / PKCS5使用初始化矢量参数进行加密和解密

Java 256bit AES 加密

基本上,我正在做的是编写一个程序,该程序将加密通过TCP / IP发送的请求,然后由服务器程序解密。加密需要是AES,做一些研究,我发现我需要使用CBC和PKCS5Padding。所以基本上我需要一个密钥和一个IV。

我正在开发的应用程序是针对手机的,所以我想使用java安全包来减小大小。我已经完成了设计,但不确定IV和共享密钥的实现。

下面是一些代码:

// My user name
byte[] loginId = "login".getBytes();

byte[] preSharedKey128 = "ACME-1234AC".getBytes();
byte[] preSharedKey192 = "ACME-1234ACME-1234A".getBytes();
// 256 bit key
byte[] preSharedKey256 = "ACME-1234ACME-1234ACME-1234".getBytes();
byte[] preSharedKey = preSharedKey256;

// Initialization Vector
// Required for CBC
byte[] iv ={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
IvParameterSpec ips = new IvParameterSpec(iv);


byte[] encodedKey = new byte[loginId.length + preSharedKey.length];

System.arraycopy(loginId, 0, encodedKey, 0, loginId.length);
System.arraycopy(preSharedKey, 0, encodedKey, loginId.length, preSharedKey.length);

// The SecretKeySpec provides a mechanism for application-specific generation
// of cryptography keys for consumption by the Java Crypto classes.

// Create a key specification first, based on our key input.
SecretKey aesKey = new SecretKeySpec(encodedKey, "AES");

// Create a Cipher for encrypting the data using the key we created.
Cipher encryptCipher;

encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize the Cipher with key and parameters
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey, ips);

// Our cleartext
String clearString = "33,8244000,9999,411,5012022517,0.00,0,1,V330";
byte[] cleartext = clearString.getBytes();

// Encrypt the cleartext
byte[] ciphertext = encryptCipher.doFinal(cleartext);

// Now decrypt back again...
// Decryption cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Initialize PBE Cipher with key and parameters
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ips);

// Decrypt the cleartext
byte[] deciphertext = decryptCipher.doFinal(ciphertext);

简而言之,它应该做的是加密一些可以由服务器解密的消息,而无需服务器从手机中获取密钥或IV。有没有办法做到这一点,我可以保护手机上的IV和密钥,并且仍然拥有服务器知道的密钥和IV?如果不是,请随时告诉我要使事情更清楚。


答案 1

代码存在一些问题。首先,您确实应该使用密钥生成器来生成密钥。直接使用一些文本可能适用于某些算法,但其他算法的键较弱,需要测试。

即使您想进行基于密码的加密,密码也应该通过密钥派生算法运行以生成密钥,如我对您已经引用的问题的回答所示。

此外,不应使用 的 no-arg 方法。这取决于平台。如果要编码的所有字符串仅包含 US-ASCII 字符集中的字符,请通过显式指定该编码来明确说明。否则,如果手机和服务器平台使用不同的字符编码,则密钥和 IV 将不相同。getBytes()String

对于 CBC 模式,最好对发送的每条消息使用新的 IV。通常,CBC IV是随机生成的。其他模式(如 CFB 和 OFB)需要每个消息的唯一 IV。IV通常与密文一起发送 - IV不需要保密,但是如果使用可预测的IV,许多算法会中断。

服务器不需要直接从手机获取密钥或IV。您可以使用密钥(或密码,从中获取密钥)配置服务器,但在许多应用程序中,这将是一个糟糕的设计。

例如,如果应用程序要部署到多个人的手机上,那么他们使用相同的密钥不是一个好主意。一个恶意用户可以恢复密钥并为每个人破坏系统。

更好的方法是在手机上生成新的密钥,并使用密钥协议算法与服务器交换密钥。Diffie-Hellman密钥协议可用于此目的,或者可以使用RSA加密密钥并发送到服务器。


更新:

在“临时-静态”模式(以及“静态-静态”模式,尽管这不太理想)下,Diffie-Hellman在没有从服务器到手机的初始消息的情况下是可能的,只要服务器的公钥嵌入在应用程序中。

服务器公钥不会带来与在手机中嵌入通用密钥相同的风险。由于它是公钥,因此威胁将是攻击者获得(或远程入侵)手机,并将真正的公钥替换为假密钥,从而允许他冒充服务器。

可以使用静态 - 静态模式,但它实际上更复杂,安全性更低。每部手机都需要自己唯一的密钥对,或者您又回到了秘密密钥问题。至少服务器不需要跟踪哪个电话具有哪个密钥(假设在应用程序级别存在某种身份验证机制,例如密码)。

我不知道手机有多快。在我的桌面上,生成一个临时密钥对大约需要1/3秒。生成Diffie-Hellman参数非常慢;您肯定希望重用服务器密钥中的参数。


答案 2

之前在中间完成过类似的项目,我有以下建议给你:

  1. 没有安全的方法来存储手机上的共享密钥。您可以使用它,但这属于一个名为“通过晦涩难懂实现安全性”的类别。这就像一种“垫子下的钥匙”的安全保障。
  2. 不要使用 256 位 AES,因为 AES 并不广泛可用。您可能需要安装另一个 JCE。128 位 AES 或 TripleDES 仍被视为安全。考虑到#1,你不应该担心这一点。
  3. 使用密码(每个用户不同)进行加密要安全得多。但是,您不应该像示例中所示的那样使用密码作为密钥。请使用 PBEKeySpec(基于密码的加密)来生成密钥。
  4. 如果您只是担心MITM(中间人)攻击,请使用SSL。

推荐