我有一个使用AES的Java(v8)加密文本,我正在尝试使用相同的SecretKey,Salt在python中解密,但是在取消索引超出范围时遇到问题。当我做相反的操作时,即在python中加密并在java中解密,那么我能够获取文本,但带有一些不需要的前缀。
以下是我尝试过的java和python代码。
Java代码(来自org.apache.commons.codec.binary.Base64的Base64)
public static String encrypt(String secretKey, String salt, String value) throws Exception {
Cipher cipher = initCipher(secretKey, salt, Cipher.ENCRYPT_MODE);
byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.encodeBase64String(encrypted);
}
public static String decrypt(String secretKey, String salt, String encrypted) throws Exception {
Cipher cipher = initCipher(secretKey, salt, Cipher.DECRYPT_MODE);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
}
private static Cipher initCipher(String secretKey, String salt, int mode) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec skeySpec = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(mode, skeySpec, new IvParameterSpec(new byte[16]));
return cipher;
}
public static void main(String[] args) throws Exception {
String secretKey = "Secret";
String fSalt = "tJHnN5b1i6wvXMwzYMRk";
String plainText = "England";
String cipherText = encrypt(secretKey, fSalt, plainText);
System.out.println("Cipher: " + cipherText);
// cipherText = "6peDTxE1xgLE4hTGg0PKTnuuhFC1Vftsd7NH9DF/7WM="; // Cipher from python
String dcrCipherText = decrypt(secretKey, fSalt, cipherText);
System.out.println(dcrCipherText);
}
Python代码(版本3.6)和Pycrypto V2.6
import base64
import hashlib
import os
from Crypto.Cipher import AES
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
# unpad = lambda s: s[:-ord(s[len(s) - 1:])]
unpad = lambda s: s[0:-s[-1]]
def get_private_key(secretKey, salt):
key = hashlib.pbkdf2_hmac('SHA256', secretKey.encode(), salt.encode(), 65536, 32)
return key
def encrypt(message, salt, secretKey):
private_key = get_private_key(secretKey, salt)
message = pad(message)
iv = os.urandom(BS) # 128-bit IV
cipher = AES.new(private_key, AES.MODE_CBC, iv, segment_size=256)
return base64.b64encode(iv + cipher.encrypt(message))
def decrypt(enc, salt, secretKey):
private_key = get_private_key(secretKey, salt)
enc = base64.b64decode(enc)
iv = enc[:BS]
cipher = AES.new(private_key, AES.MODE_CBC, iv, segment_size=256)
return unpad(cipher.decrypt(enc[BS:]))
secretKey = "Secret"
salt = "tJHnN5b1i6wvXMwzYMRk"
plainText = "England"
cipher = encrypt(plainText, salt, secretKey)
print("Cipher: " + bytes.decode(cipher))
# cipher = "0JrZdg9YBRshfTdr1d4zwQ==" # Cipher from java
decrypted = decrypt(cipher, salt, secretKey)
print("Decrypted " + bytes.decode(decrypted))
当我通过python密码时,Java解密输出:�U�����or���England
,应该是:England
Python解密输出:unpad = lambda s : s[0:-s[-1]]
IndexError: index out of range
,预期值:England
关于此问题,我也在堆栈上浏览过其他帖子,但是由于他们使用了不同的模式,因此无法解决问题。
答案 0 :(得分:1)
在python中,您将iv(初始化向量)存储在加密消息的前16个字节中。
在Java中,您没有做任何事情-您传递了一个空的IV,并将包括前16个字节的整个消息视为密文。
您需要确保Java和Python相匹配。
两者都不使用IV,在这种情况下,您要在Python中删除该部分。
或者两者都使用IV,在这种情况下,您需要更新Java代码以生成关于加密的随机IV,并将其添加到加密结果中。解密时,Java代码需要将前16个字节作为IV,并将其传递给Cipher
。
答案 1 :(得分:1)
使用 python 3
的正确 pycryptodome
代码将匹配 Java 代码如下:
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad,pad
import hashlib
def get_private_key(secretKey, salt):
# _prf = lambda p,s: HMAC.new(p, s, SHA256).digest()
# private_key = PBKDF2(secretKey, salt.encode(), dkLen=32,count=65536, prf=_prf )
# above code is equivalent but slow
key = hashlib.pbkdf2_hmac('SHA256', secretKey.encode(), salt.encode(), 65536, 32)
# KeySpec spec = new PBEKeySpec(secretKey.toCharArray(), salt.getBytes(), 65536, 256);
return key
def encrypt(message, salt, secretKey):
private_key = get_private_key(secretKey, salt)
message = pad(message.encode(), AES.block_size)
iv = "\x00"*AES.block_size # 128-bit IV
cipher = AES.new(private_key, AES.MODE_CBC, iv.encode())
return base64.b64encode(cipher.encrypt(message))
def decrypt(enc, salt, secretKey):
# _prf = lambda p,s: HMAC.new(p, s, SHA256).digest()
# private_key = PBKDF2(secretKey, salt.encode(), dkLen=32,count=65536, prf=_prf )
private_key = get_private_key(secretKey, salt)
enc = base64.b64decode(enc)
iv = "\x00"*AES.block_size
cipher = AES.new(private_key, AES.MODE_CBC, iv.encode())
return unpad(cipher.decrypt(enc), AES.block_size).decode('utf-8')
secretKey = "Secret"
salt = "tJHnN5b1i6wvXMwzYMRk"
plainText = "England"
enc_datta = encrypt(plainText, salt, secretKey)
print(f"Encrypted: {enc_datta}")
# Encrypted: 0JrZdg9YBRshfTdr1d4zwQ==
cipher = "0JrZdg9YBRshfTdr1d4zwQ==" # Cipher from java
decrypted = decrypt(cipher, salt, secretKey)
print(f"Decrypted: {decrypted}" )
# Decrypted: England