在 python 中实施 PBEWITHHMACSHA512ANDAES_256 的 java jasypt
Implement PBEWITHHMACSHA512ANDAES_256 of java jasypt in python
我正在尝试使用 jasypt library through jasypt plugin.
在 python 中加密密码并在 java springboot 应用程序中解密它
到目前为止我做了什么
- 为简单起见,我使用了零盐和固定 iv
- 我已经编写了 python 脚本来使用 hselvarajan 的 pkcs12kdf 执行加密
import sys
import math
import base64
import hashlib
from Crypto.Cipher import AES
from Crypto.Hash import SHA512
from binascii import hexlify
from binascii import unhexlify
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY2:
str_encode = lambda s: str(s)
elif PY3:
str_encode = lambda s: str(s, 'utf-8')
iterations = 10000
salt_block_size = AES.block_size
key_size = 256
password = "test1"
plaintext_to_encrypt = "password1"
salt = "0000000000000000"
iv = "0000000000000000"
# -----------------------------------------------------------------------------
# This is a pure copy paste of
# https://github.com/hselvarajan/pkcs12kdf/blob/master/pkcs12kdf.py
# -----------------------------------------------------------------------------
class PKCS12KDF:
"""This class generates keys and initialization vectors from passwords as specified in RFC 7292"""
#
# IDs for Key and IV material as in RFC
#
KEY_MATERIAL = 1
IV_MATERIAL = 2
def __init__(self, password, salt, iteration_count, hash_algorithm, key_length_bits):
self._password = password
self._salt = salt
self._iteration_count = iteration_count
self._block_size_bits = None
self._hash_length_bits = None
self._key_length_bytes = key_length_bits/8
self._key = None
self._iv = None
self._hash_algorithm = hash_algorithm
#
# Turns a byte array into a long
#
@staticmethod
def byte_array_to_long(byte_array, nbytes=None):
#
# If nbytes is not present
#
if nbytes is None:
#
# Convert byte -> hex -> int/long
#
return int(hexlify(byte_array), 16)
else:
#
# Convert byte -> hex -> int/long
#
return int(hexlify(byte_array[-nbytes:]), 16)
#
# Turn a long into a byte array
#
@staticmethod
def long_to_byte_array(val, nbytes=None):
hexval = hex(val)[2:-1] if type(val) is long else hex(val)[2:]
if nbytes is None:
return unhexlify('0' * (len(hexval) & 1) + hexval)
else:
return unhexlify('0' * (nbytes * 2 - len(hexval)) + hexval[-nbytes * 2:])
#
# Run the PKCS12 algorithm for either the key or the IV, specified by id
#
def generate_derived_parameters(self, id):
#
# Let r be the iteration count
#
r = self._iteration_count
if self._hash_algorithm not in hashlib.algorithms_available:
raise NotImplementedError("Hash function: "+self._hash_algorithm+" not available")
hash_function = hashlib.new(self._hash_algorithm)
#
# Block size, bytes
#
#v = self._block_size_bits / 8
v = hash_function.block_size
#
# Hash function output length, bits
#
#u = self._hash_length_bits / 8
u = hash_function.digest_size
# In this specification however, all passwords are created from BMPStrings with a NULL
# terminator. This means that each character in the original BMPString is encoded in 2
# bytes in big-endian format (most-significant byte first). There are no Unicode byte order
# marks. The 2 bytes produced from the last character in the BMPString are followed by
# two additional bytes with the value 0x00.
password = (unicode(self._password) + u'[=13=]').encode('utf-16-be') if self._password is not None else b''
#
# Length of password string, p
#
p = len(password)
#
# Length of salt, s
#
s = len(self._salt)
#
# Step 1: Construct a string, D (the "diversifier"), by concatenating v copies of ID.
#
D = chr(id) * v
#
# Step 2: Concatenate copies of the salt, s, together to create a string S of length v * [s/v] bits (the
# final copy of the salt may be truncated to create S). Note that if the salt is the empty
# string, then so is S
#
S = b''
if self._salt is not None:
limit = int(float(v) * math.ceil((float(s)/float(v))))
for i in range(0, limit):
S += (self._salt[i % s])
else:
S += '0'
#
# Step 3: Concatenate copies of the password, p, together to create a string P of length v * [p/v] bits
# (the final copy of the password may be truncated to create P). Note that if the
# password is the empty string, then so is P.
#
P = b''
if password is not None:
limit = int(float(v) * math.ceil((float(p)/float(v))))
for i in range(0, limit):
P += password[i % p]
else:
P += '0'
#
# Step 4: Set I=S||P to be the concatenation of S and P.[=13=][=13=]
#
I = bytearray(S) + bytearray(P)
#
# 5. Set c=[n/u]. (n = length of key/IV required)
#
n = self._key_length_bytes
c = int(math.ceil(float(n)/float(u)))
#
# Step 6 For i=1, 2,..., c, do the following:
#
Ai = bytearray()
for i in range(0, c):
#
# Step 6a.Set Ai=Hr(D||I). (i.e. the rth hash of D||I, H(H(H(...H(D||I))))
#
hash_function = hashlib.new(self._hash_algorithm)
hash_function.update(bytearray(D))
hash_function.update(bytearray(I))
Ai = hash_function.digest()
for j in range(1, r):
hash_function = hashlib.sha256()
hash_function.update(Ai)
Ai = hash_function.digest()
#
# Step 6b: Concatenate copies of Ai to create a string B of length v bits (the final copy of Ai
# may be truncated to create B).
#
B = b''
for j in range(0, v):
B += Ai[j % len(Ai)]
#
# Step 6c: Treating I as a concatenation I0, I1,..., Ik-1 of v-bit blocks, where k=[s/v]+[p/v],
# modify I by setting Ij=(Ij+B+1) mod 2v for each j.
#
k = int(math.ceil(float(s)/float(v)) + math.ceil((float(p)/float(v))))
for j in range(0, k-1):
I = ''.join([
self.long_to_byte_array(
self.byte_array_to_long(I[j:j + v]) + self.byte_array_to_long(bytearray(B)), v
)
])
return Ai[:self._key_length_bytes]
#
# Generate the key and IV
#
def generate_key_and_iv(self):
self._key = self.generate_derived_parameters(self.KEY_MATERIAL)
self._iv = self.generate_derived_parameters(self.IV_MATERIAL)
return self._key, self._iv
# -----------------------------------------------------------------------------
# Main execution
# -----------------------------------------------------------------------------
kdf = PKCS12KDF(
password = password,
salt = salt,
iteration_count = iterations,
hash_algorithm = "sha512",
key_length_bits = key_size
)
(key, iv_tmp) = kdf.generate_key_and_iv()
aes_key = key[:32]
pad = salt_block_size - len(plaintext_to_encrypt) % salt_block_size
plaintext_to_encrypt = plaintext_to_encrypt + pad * chr(pad)
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(plaintext_to_encrypt)
# Since we selt the salt to be zero's,
# jasypt needs only the iv + encrypted value,
# not the salt + iv + encrypted
result = str_encode(base64.b64encode(iv + encrypted))
# Python output : MDAwMDAwMDAwMDAwMDAwMKWsWH+Ku37n7ddfj0ayxp8=
# Java output : MDAwMDAwMDAwMDAwMDAwMAtqAfBtuxf+F5qqzC8QiFc=
print(result)
运行 作为
python2.7 test-PBEWITHHMACSHA512ANDAES_256.py
paxYf4q7fuft11+PRrLGnw==
- 我在jasypt仓库写了一个单元测试来解密
参见 PBEWITHHMACSHA512ANDAES_256EncryptorTest。
运行 作为
$ cd jasypt
$ mvn clean test -Dtest=org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest
问题:上述设置在 python 和 java
中产生不同的结果
- Python 输出:MDAwMDAwMDAwMDAwMDAwMKWsWH+Ku37n7ddfj0ayxp8=
- Java 输出:MDAwMDAwMDAwMDAwMDAwMAtqAfBtuxf+F5qqzC8QiFc=
我所知道的
- 失败是因为没有使用python中的正确密钥。添加额外的日志,错误是
EncryptionOperationNotPossibleException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
- PBEWITHHMACSHA512ANDAES_256使用pkcs12密钥推导函数。
我不明白 HMAC 在哪里使用。
- 我也尝试过使用下面的实现,但没有用。我在所有这些中都收到“”错误。
- oscrypto
- python-hkdf
- Cryptodome.Protocol.KDF香港自卫队
我不明白这里在哪里使用迭代。
self.aes_key = HKDF(master = self.password, key_len = 32, salt = self.salt, hashmod = SHA512, num_keys = 1)
我想要一些关于我做错了什么的指导。任何帮助,任何指示将不胜感激。
在 Cryptodome 的 PBKDF2 和 AES 之后更新
这是 python 脚本
import sys
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Hash import SHA512
from Cryptodome.Protocol.KDF import PBKDF2
from Cryptodome.Util.Padding import pad
iterations = 10000
password = b'test1'
plaintext_to_encrypt = b'password1'
salt = b'0000000000000000'
iv = b'0000000000000000'
# -----------------------------------------------------------------------------
# Main execution
# -----------------------------------------------------------------------------
keys = PBKDF2(password, salt, 64, count=iterations, hmac_hash_module=SHA512)
aes_key = keys[:32]
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
ct_bytes = cipher.encrypt(pad(plaintext_to_encrypt, AES.block_size))
encrypted = base64.b64encode(ct_bytes).decode('utf-8')
# Since we selt the salt to be zero's,
# jasypt needs only the iv + encrypted value,
# not the salt + iv + encrypted
result = encrypted
# Python output : 6tCAZbswCh9DZ1EK8utRuA==
# Java output : C2oB8G27F/4XmqrMLxCIVw==
print(result)
及其输出
python2.7 test-PBEWITHHMACSHA512ANDAES_256-2.py
6tCAZbswCh9DZ1EK8utRuA==
我尝试使用 test
在 java 中解密它并出现以下错误
mvn clean test -Dtest=org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest
[...]
Running org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest
Test encr: C2oB8G27F/4XmqrMLxCIVw==
Error: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.524 sec <<< FAILURE!
test1(org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest) Time elapsed: 0.522 sec <<< ERROR!
org.jasypt.exceptions.EncryptionOperationNotPossibleException
at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.decrypt(StandardPBEByteEncryptor.java:1173)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.decrypt(StandardPBEStringEncryptor.java:738)
at org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest.test1(PBEWITHHMACSHA512ANDAES_256EncryptorTest.java:27)
Results :
Tests in error:
test1(org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest)
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.648 s
[INFO] Finished at: 2020-06-24T17:40:04+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project jasypt: There are test failures.
[ERROR]
[ERROR] Please refer to /space/openbet/git/github-jasypt-jasypt/jasypt/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
PBEWITHHMACSHA512ANDAES_256
应用 PBKDF2 生成密钥。使用 AES-256、CBC 进行加密。
应用了使用的(最初)发布的 Jasypt 测试函数 RandomIvGenerator
, which creates a random IV. For the salt, ZeroSaltGenerator
,它生成了一个由 16 个零字节组成的盐。
要实现你要找的Python函数,最好使用固定的IV,例如with StringFixedIvGenerator
. StringFixedSaltGenerator
provides a corresponding functionality for the salt (FixedStringSaltGenerator
具有相同的功能,但自 1.9.2 起已弃用)。 StringFixedSaltGenerator
和 StringFixedIvGenerator
默认使用 UTF-8 对传递的字符串进行编码(但可以指定其他编码),这样盐(或 IV)0000000000000000
就是十六进制编码的 0x30303030303030303030303030303030
.
请注意,固定盐和 IV 只能用于测试。实际上,每次加密都必须使用新的随机盐和新的随机 IV。由于 salt 和 IV 不是秘密的,它们通常在字节级别与密文连接(例如,按 salt、iv、密文的顺序)并发送给接收方,接收方将这些部分分开并使用它们进行解密。
如果双方使用相同的参数(尤其是相同的 salt 和 IV),则可以使用 Python 加密和使用 Java 解密。
使用 Python (PyCryptodome) 加密:
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Hash import SHA512
from Cryptodome.Protocol.KDF import PBKDF2
from Cryptodome.Util.Padding import pad
# Key generation (PBKDF2)
iterations = 10000
password = b'test1'
plaintext_to_encrypt = b'password1'
salt = b'5432109876543210'
iv = b'0123456789012345'
key = PBKDF2(password, salt, 32, count=iterations, hmac_hash_module=SHA512)
# Encryption (AES-256, CBC)
cipher = AES.new(key, AES.MODE_CBC, iv)
ct_bytes = cipher.encrypt(pad(plaintext_to_encrypt, AES.block_size))
encrypted = base64.b64encode(ct_bytes).decode('utf-8')
print(encrypted) # Output: kzLd5qPlCLnHq5sT7LOXzQ==
使用 Java (Jasypt) 解密:
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("test1");
encryptor.setSaltGenerator(new StringFixedSaltGenerator("5432109876543210"));
encryptor.setIvGenerator(new StringFixedIvGenerator("0123456789012345"));
encryptor.setKeyObtentionIterations(10000);
encryptor.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
String decryptedMsg = encryptor.decrypt("kzLd5qPlCLnHq5sT7LOXzQ==");
System.out.println("Test decr: " + decryptedMsg); // Output: Test decr: password1
顺便说一句,如果有人仍在寻找这个答案,但使用随机盐和 IV,它们似乎已按顺序附加到密文中。这是与 PBEWithHMACSHA512AndAES_256 兼容的 encryption/decryption 解决方案:
from base64 import b64decode, b64encode
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
KEY = b'my awesome key'
def decrypt_pbe_with_hmac_sha512_aes_256(obj: str) -> str:
# re-generate key from
encrypted_obj = b64decode(obj)
salt = encrypted_obj[0:16]
iv = encrypted_obj[16:32]
cypher_text = encrypted_obj[32:]
kdf = PBKDF2HMAC(hashes.SHA512(), 32, salt, 1000, backend=default_backend())
key = kdf.derive(KEY)
# decrypt
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_text = decryptor.update(cypher_text) + decryptor.finalize()
# remove padding
unpadder = PKCS7(128).unpadder()
clear_text = unpadder.update(padded_text) + unpadder.finalize()
return clear_text.decode()
def encrypt_pbe_with_hmac_sha512_aes_256(obj: str, salt: bytes = None, iv: bytes = None) -> str:
# generate key
salt = salt or os.urandom(16)
iv = iv or os.urandom(16)
kdf = PBKDF2HMAC(hashes.SHA512(), 32, salt, 1000, backend=default_backend())
key = kdf.derive(KEY)
# pad data
padder = PKCS7(128).padder()
data = padder.update(obj.encode()) + padder.finalize()
# encrypt
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
cypher_text = encryptor.update(data) + encryptor.finalize()
return b64encode(salt + iv + cypher_text).decode()
然后你可以直接使用Jasypt的base64输出:
>>> decrypt_pbe_with_hmac_sha512_aes_256(encrypt_pbe_with_hmac_sha512_aes_256('hello world'))
'hello world'
我正在尝试使用 jasypt library through jasypt plugin.
在 python 中加密密码并在 java springboot 应用程序中解密它到目前为止我做了什么
- 为简单起见,我使用了零盐和固定 iv
- 我已经编写了 python 脚本来使用 hselvarajan 的 pkcs12kdf 执行加密
运行 作为import sys import math import base64 import hashlib from Crypto.Cipher import AES from Crypto.Hash import SHA512 from binascii import hexlify from binascii import unhexlify PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 if PY2: str_encode = lambda s: str(s) elif PY3: str_encode = lambda s: str(s, 'utf-8') iterations = 10000 salt_block_size = AES.block_size key_size = 256 password = "test1" plaintext_to_encrypt = "password1" salt = "0000000000000000" iv = "0000000000000000" # ----------------------------------------------------------------------------- # This is a pure copy paste of # https://github.com/hselvarajan/pkcs12kdf/blob/master/pkcs12kdf.py # ----------------------------------------------------------------------------- class PKCS12KDF: """This class generates keys and initialization vectors from passwords as specified in RFC 7292""" # # IDs for Key and IV material as in RFC # KEY_MATERIAL = 1 IV_MATERIAL = 2 def __init__(self, password, salt, iteration_count, hash_algorithm, key_length_bits): self._password = password self._salt = salt self._iteration_count = iteration_count self._block_size_bits = None self._hash_length_bits = None self._key_length_bytes = key_length_bits/8 self._key = None self._iv = None self._hash_algorithm = hash_algorithm # # Turns a byte array into a long # @staticmethod def byte_array_to_long(byte_array, nbytes=None): # # If nbytes is not present # if nbytes is None: # # Convert byte -> hex -> int/long # return int(hexlify(byte_array), 16) else: # # Convert byte -> hex -> int/long # return int(hexlify(byte_array[-nbytes:]), 16) # # Turn a long into a byte array # @staticmethod def long_to_byte_array(val, nbytes=None): hexval = hex(val)[2:-1] if type(val) is long else hex(val)[2:] if nbytes is None: return unhexlify('0' * (len(hexval) & 1) + hexval) else: return unhexlify('0' * (nbytes * 2 - len(hexval)) + hexval[-nbytes * 2:]) # # Run the PKCS12 algorithm for either the key or the IV, specified by id # def generate_derived_parameters(self, id): # # Let r be the iteration count # r = self._iteration_count if self._hash_algorithm not in hashlib.algorithms_available: raise NotImplementedError("Hash function: "+self._hash_algorithm+" not available") hash_function = hashlib.new(self._hash_algorithm) # # Block size, bytes # #v = self._block_size_bits / 8 v = hash_function.block_size # # Hash function output length, bits # #u = self._hash_length_bits / 8 u = hash_function.digest_size # In this specification however, all passwords are created from BMPStrings with a NULL # terminator. This means that each character in the original BMPString is encoded in 2 # bytes in big-endian format (most-significant byte first). There are no Unicode byte order # marks. The 2 bytes produced from the last character in the BMPString are followed by # two additional bytes with the value 0x00. password = (unicode(self._password) + u'[=13=]').encode('utf-16-be') if self._password is not None else b'' # # Length of password string, p # p = len(password) # # Length of salt, s # s = len(self._salt) # # Step 1: Construct a string, D (the "diversifier"), by concatenating v copies of ID. # D = chr(id) * v # # Step 2: Concatenate copies of the salt, s, together to create a string S of length v * [s/v] bits (the # final copy of the salt may be truncated to create S). Note that if the salt is the empty # string, then so is S # S = b'' if self._salt is not None: limit = int(float(v) * math.ceil((float(s)/float(v)))) for i in range(0, limit): S += (self._salt[i % s]) else: S += '0' # # Step 3: Concatenate copies of the password, p, together to create a string P of length v * [p/v] bits # (the final copy of the password may be truncated to create P). Note that if the # password is the empty string, then so is P. # P = b'' if password is not None: limit = int(float(v) * math.ceil((float(p)/float(v)))) for i in range(0, limit): P += password[i % p] else: P += '0' # # Step 4: Set I=S||P to be the concatenation of S and P.[=13=][=13=] # I = bytearray(S) + bytearray(P) # # 5. Set c=[n/u]. (n = length of key/IV required) # n = self._key_length_bytes c = int(math.ceil(float(n)/float(u))) # # Step 6 For i=1, 2,..., c, do the following: # Ai = bytearray() for i in range(0, c): # # Step 6a.Set Ai=Hr(D||I). (i.e. the rth hash of D||I, H(H(H(...H(D||I)))) # hash_function = hashlib.new(self._hash_algorithm) hash_function.update(bytearray(D)) hash_function.update(bytearray(I)) Ai = hash_function.digest() for j in range(1, r): hash_function = hashlib.sha256() hash_function.update(Ai) Ai = hash_function.digest() # # Step 6b: Concatenate copies of Ai to create a string B of length v bits (the final copy of Ai # may be truncated to create B). # B = b'' for j in range(0, v): B += Ai[j % len(Ai)] # # Step 6c: Treating I as a concatenation I0, I1,..., Ik-1 of v-bit blocks, where k=[s/v]+[p/v], # modify I by setting Ij=(Ij+B+1) mod 2v for each j. # k = int(math.ceil(float(s)/float(v)) + math.ceil((float(p)/float(v)))) for j in range(0, k-1): I = ''.join([ self.long_to_byte_array( self.byte_array_to_long(I[j:j + v]) + self.byte_array_to_long(bytearray(B)), v ) ]) return Ai[:self._key_length_bytes] # # Generate the key and IV # def generate_key_and_iv(self): self._key = self.generate_derived_parameters(self.KEY_MATERIAL) self._iv = self.generate_derived_parameters(self.IV_MATERIAL) return self._key, self._iv # ----------------------------------------------------------------------------- # Main execution # ----------------------------------------------------------------------------- kdf = PKCS12KDF( password = password, salt = salt, iteration_count = iterations, hash_algorithm = "sha512", key_length_bits = key_size ) (key, iv_tmp) = kdf.generate_key_and_iv() aes_key = key[:32] pad = salt_block_size - len(plaintext_to_encrypt) % salt_block_size plaintext_to_encrypt = plaintext_to_encrypt + pad * chr(pad) cipher = AES.new(aes_key, AES.MODE_CBC, iv) encrypted = cipher.encrypt(plaintext_to_encrypt) # Since we selt the salt to be zero's, # jasypt needs only the iv + encrypted value, # not the salt + iv + encrypted result = str_encode(base64.b64encode(iv + encrypted)) # Python output : MDAwMDAwMDAwMDAwMDAwMKWsWH+Ku37n7ddfj0ayxp8= # Java output : MDAwMDAwMDAwMDAwMDAwMAtqAfBtuxf+F5qqzC8QiFc= print(result)
python2.7 test-PBEWITHHMACSHA512ANDAES_256.py paxYf4q7fuft11+PRrLGnw==
- 我在jasypt仓库写了一个单元测试来解密
参见 PBEWITHHMACSHA512ANDAES_256EncryptorTest。
运行 作为
$ cd jasypt $ mvn clean test -Dtest=org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest
问题:上述设置在 python 和 java
中产生不同的结果- Python 输出:MDAwMDAwMDAwMDAwMDAwMKWsWH+Ku37n7ddfj0ayxp8=
- Java 输出:MDAwMDAwMDAwMDAwMDAwMAtqAfBtuxf+F5qqzC8QiFc=
我所知道的
- 失败是因为没有使用python中的正确密钥。添加额外的日志,错误是
EncryptionOperationNotPossibleException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
- PBEWITHHMACSHA512ANDAES_256使用pkcs12密钥推导函数。 我不明白 HMAC 在哪里使用。
- 我也尝试过使用下面的实现,但没有用。我在所有这些中都收到“”错误。
- oscrypto
- python-hkdf
- Cryptodome.Protocol.KDF香港自卫队 我不明白这里在哪里使用迭代。
self.aes_key = HKDF(master = self.password, key_len = 32, salt = self.salt, hashmod = SHA512, num_keys = 1)
我想要一些关于我做错了什么的指导。任何帮助,任何指示将不胜感激。
在 Cryptodome 的 PBKDF2 和 AES 之后更新 这是 python 脚本
import sys
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Hash import SHA512
from Cryptodome.Protocol.KDF import PBKDF2
from Cryptodome.Util.Padding import pad
iterations = 10000
password = b'test1'
plaintext_to_encrypt = b'password1'
salt = b'0000000000000000'
iv = b'0000000000000000'
# -----------------------------------------------------------------------------
# Main execution
# -----------------------------------------------------------------------------
keys = PBKDF2(password, salt, 64, count=iterations, hmac_hash_module=SHA512)
aes_key = keys[:32]
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
ct_bytes = cipher.encrypt(pad(plaintext_to_encrypt, AES.block_size))
encrypted = base64.b64encode(ct_bytes).decode('utf-8')
# Since we selt the salt to be zero's,
# jasypt needs only the iv + encrypted value,
# not the salt + iv + encrypted
result = encrypted
# Python output : 6tCAZbswCh9DZ1EK8utRuA==
# Java output : C2oB8G27F/4XmqrMLxCIVw==
print(result)
及其输出
python2.7 test-PBEWITHHMACSHA512ANDAES_256-2.py
6tCAZbswCh9DZ1EK8utRuA==
我尝试使用 test
在 java 中解密它并出现以下错误mvn clean test -Dtest=org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest
[...]
Running org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest
Test encr: C2oB8G27F/4XmqrMLxCIVw==
Error: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.524 sec <<< FAILURE!
test1(org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest) Time elapsed: 0.522 sec <<< ERROR!
org.jasypt.exceptions.EncryptionOperationNotPossibleException
at org.jasypt.encryption.pbe.StandardPBEByteEncryptor.decrypt(StandardPBEByteEncryptor.java:1173)
at org.jasypt.encryption.pbe.StandardPBEStringEncryptor.decrypt(StandardPBEStringEncryptor.java:738)
at org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest.test1(PBEWITHHMACSHA512ANDAES_256EncryptorTest.java:27)
Results :
Tests in error:
test1(org.jasypt.encryption.pbe.PBEWITHHMACSHA512ANDAES_256EncryptorTest)
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.648 s
[INFO] Finished at: 2020-06-24T17:40:04+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project jasypt: There are test failures.
[ERROR]
[ERROR] Please refer to /space/openbet/git/github-jasypt-jasypt/jasypt/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
PBEWITHHMACSHA512ANDAES_256
应用 PBKDF2 生成密钥。使用 AES-256、CBC 进行加密。
应用了使用的(最初)发布的 Jasypt 测试函数 RandomIvGenerator
, which creates a random IV. For the salt, ZeroSaltGenerator
,它生成了一个由 16 个零字节组成的盐。
要实现你要找的Python函数,最好使用固定的IV,例如with StringFixedIvGenerator
. StringFixedSaltGenerator
provides a corresponding functionality for the salt (FixedStringSaltGenerator
具有相同的功能,但自 1.9.2 起已弃用)。 StringFixedSaltGenerator
和 StringFixedIvGenerator
默认使用 UTF-8 对传递的字符串进行编码(但可以指定其他编码),这样盐(或 IV)0000000000000000
就是十六进制编码的 0x30303030303030303030303030303030
.
请注意,固定盐和 IV 只能用于测试。实际上,每次加密都必须使用新的随机盐和新的随机 IV。由于 salt 和 IV 不是秘密的,它们通常在字节级别与密文连接(例如,按 salt、iv、密文的顺序)并发送给接收方,接收方将这些部分分开并使用它们进行解密。
如果双方使用相同的参数(尤其是相同的 salt 和 IV),则可以使用 Python 加密和使用 Java 解密。
使用 Python (PyCryptodome) 加密:
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Hash import SHA512
from Cryptodome.Protocol.KDF import PBKDF2
from Cryptodome.Util.Padding import pad
# Key generation (PBKDF2)
iterations = 10000
password = b'test1'
plaintext_to_encrypt = b'password1'
salt = b'5432109876543210'
iv = b'0123456789012345'
key = PBKDF2(password, salt, 32, count=iterations, hmac_hash_module=SHA512)
# Encryption (AES-256, CBC)
cipher = AES.new(key, AES.MODE_CBC, iv)
ct_bytes = cipher.encrypt(pad(plaintext_to_encrypt, AES.block_size))
encrypted = base64.b64encode(ct_bytes).decode('utf-8')
print(encrypted) # Output: kzLd5qPlCLnHq5sT7LOXzQ==
使用 Java (Jasypt) 解密:
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
encryptor.setPassword("test1");
encryptor.setSaltGenerator(new StringFixedSaltGenerator("5432109876543210"));
encryptor.setIvGenerator(new StringFixedIvGenerator("0123456789012345"));
encryptor.setKeyObtentionIterations(10000);
encryptor.setAlgorithm("PBEWITHHMACSHA512ANDAES_256");
String decryptedMsg = encryptor.decrypt("kzLd5qPlCLnHq5sT7LOXzQ==");
System.out.println("Test decr: " + decryptedMsg); // Output: Test decr: password1
顺便说一句,如果有人仍在寻找这个答案,但使用随机盐和 IV,它们似乎已按顺序附加到密文中。这是与 PBEWithHMACSHA512AndAES_256 兼容的 encryption/decryption 解决方案:
from base64 import b64decode, b64encode
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.padding import PKCS7
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
KEY = b'my awesome key'
def decrypt_pbe_with_hmac_sha512_aes_256(obj: str) -> str:
# re-generate key from
encrypted_obj = b64decode(obj)
salt = encrypted_obj[0:16]
iv = encrypted_obj[16:32]
cypher_text = encrypted_obj[32:]
kdf = PBKDF2HMAC(hashes.SHA512(), 32, salt, 1000, backend=default_backend())
key = kdf.derive(KEY)
# decrypt
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_text = decryptor.update(cypher_text) + decryptor.finalize()
# remove padding
unpadder = PKCS7(128).unpadder()
clear_text = unpadder.update(padded_text) + unpadder.finalize()
return clear_text.decode()
def encrypt_pbe_with_hmac_sha512_aes_256(obj: str, salt: bytes = None, iv: bytes = None) -> str:
# generate key
salt = salt or os.urandom(16)
iv = iv or os.urandom(16)
kdf = PBKDF2HMAC(hashes.SHA512(), 32, salt, 1000, backend=default_backend())
key = kdf.derive(KEY)
# pad data
padder = PKCS7(128).padder()
data = padder.update(obj.encode()) + padder.finalize()
# encrypt
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
cypher_text = encryptor.update(data) + encryptor.finalize()
return b64encode(salt + iv + cypher_text).decode()
然后你可以直接使用Jasypt的base64输出:
>>> decrypt_pbe_with_hmac_sha512_aes_256(encrypt_pbe_with_hmac_sha512_aes_256('hello world'))
'hello world'