我正在尝试获得一个在 Java 世界中显然很流行的加密库的 ruby 实现——PBEWithMD5AndDES
有谁知道如何使用 openssl 或其他开源 gem 来执行与这种格式兼容的加密/解密?
更新:
我使用 gem chilkat 来实现它,但它是付费的,我需要一个开源解决方案。
我正在尝试获得一个在 Java 世界中显然很流行的加密库的 ruby 实现——PBEWithMD5AndDES
有谁知道如何使用 openssl 或其他开源 gem 来执行与这种格式兼容的加密/解密?
更新:
我使用 gem chilkat 来实现它,但它是付费的,我需要一个开源解决方案。
我知道它已经很老了,但是我遇到了同样的问题并且刚刚解决了它,所以在这里进行加密,其中 salt 是你的 salt sting,passkey 是你的密码密钥字符串,迭代是你想要使用的迭代次数
def encrypt_account_number
cipher = OpenSSL::Cipher::Cipher.new("DES")
cipher.encrypt
cipher.pkcs5_keyivgen passkey, salt,iterations,digest
encrypted_account_number = cipher.update(account_number)
encrypted_account_number << cipher.final
Base64.encode64(encrypted_account_number )
end
def decrypt_account_number
cipher = OpenSSL::Cipher::Cipher.new("DES")
base_64_code = Base64.decode64(account_number)
cipher.decrypt
cipher.pkcs5_keyivgen passkey, salt,iterations,digest
decrypted_account_number = cipher.update base_64_code
decrypted_account_number << cipher.final
decrypted_account_number
end
假设 ruby 具有 DES 实现,您不需要实际实现 PBEWithMD5andDES。您需要实现的是密钥派生功能(您从密码中获取密钥的人),然后使用适当的模式和填充将该派生密钥提供给 DES。
值得庆幸的是,密钥派生功能在实施中并不是特别安全关键,因此您可以自己安全地完成。根据rfc,PBEwithMD5AndDES 实际上是在 CBC 模式下与 DES 一起使用的 PBKDF1(一种 ker 派生函数)。
PBKDF1 看起来并不难实现。看起来你可以用一个 for 循环和一个 md5 调用来做到这一点。
请注意,由于 Java 和 Ruby 中可能使用不同的填充方案,您可能仍然会得到一些奇怪的结果。我假设规范是 pkcs 1.5 填充,但快速浏览一下,我无法确认这一点
PBKDF1 应用一个散列函数,它应该是 MD2 [6]、MD5 [19] 或 SHA-1 [18],以派生密钥。派生密钥
的长度受哈希函数输出长度的限制,MD2 和 MD5 为 16 个八位字节,SHA-1 为 20 个八位字节。
PBKDF1 与PKCS #5 v1.5 中的密钥派生过程兼容。仅推荐 PBKDF1 与现有应用程序兼容,因为它生成的密钥对于 某些应用程序
可能不够大。PBKDF1 (P, S, c, dkLen)
选项:散列底层散列函数
输入:P 密码,八位字节字符串 S salt,八位字节字符串 c 迭代计数,正整数 dkLen 派生密钥的八位字节预期长度,正整数,对于 MD2 或 MD5 最多为 16,对于 SHA-1 最多为 20
输出:DK 派生密钥,一个 dkLen-octet 字符串
脚步:
1. If dkLen > 16 for MD2 and MD5, or dkLen > 20 for SHA-1, output "derived key too long" and stop. 2. Apply the underlying hash function Hash for c iterations to the concatenation of the password P and the salt S, then extract the first dkLen octets to produce a derived key DK: T_1 = Hash (P || S) , T_2 = Hash (T_1) , ... T_c = Hash (T_{c-1}) , DK = Tc<0..dkLen-1> 3. Output the derived key DK.
为了它的价值,我发布了我的 python 代码,它确实有效(我有大量使用 org.jasypt.util.text.BasicTextEncryptor 完成的加密值,我需要解密它们。)
import base64
import hashlib
from Crypto.Cipher import DES
"""
Note about PBEWithMD5AndDES in java crypto library:
Encrypt:
Generate a salt (random): 8 bytes
<start derived key generation>
Append salt to the password
MD5 Hash it, and hash the result, hash the result ... 1000 times
MD5 always gives us a 16 byte hash
Final result: first 8 bytes is the "key" and the next is the "initialization vector"
(there is something about the first 8 bytes needing to be of odd paraity, therefore
the least significant bit needs to be changed to 1 if required. We don't do it,
maybe the python crypto library does it for us)
<end derived key generation>
Pad the input string with 1-8 bytes (note: not 0-7, so we always have padding)
so that the result is a multiple of 8 bytes. Padding byte value is same as number of
bytes being padded, eg, \x07 if 7 bytes need to be padded.
Use the key and iv to encrypt the input string, using DES with CBC mode.
Prepend the encrypted value with the salt (needed for decrypting since it is random)
Base64 encode it -> this is your result
Decrypt:
Base64 decode the input message
Extract the salt (first 8 bytes). The rest is the encoded text.
Use derived key generation as in Encrypt above to get the key and iv
Decrypt the encoded text using key and iv
Remove padding -> this is your result
(I only have implemented decrypt here since that's all I needed,
but encrypt should be straighforward as well)
"""
def get_derived_key(password, salt, count):
key = password + salt
for i in range(count):
m = hashlib.md5(key)
key = m.digest()
return (key[:8], key[8:])
def decrypt(msg, password):
msg_bytes = base64.b64decode(msg)
salt = msg_bytes[:8]
enc_text = msg_bytes[8:]
(dk, iv) = get_derived_key(password, salt, 1000)
crypter = DES.new(dk, DES.MODE_CBC, iv)
text = crypter.decrypt(enc_text)
# remove the padding at the end, if any
return re.sub(r'[\x01-\x08]','',text)
我已经从 user3392439 更新了 python 脚本,支持加密。希望对您有所帮助。
import base64
import hashlib
import re
import os
from Crypto.Cipher import DES
"""
Note about PBEWithMD5AndDES in java crypto library:
Encrypt:
Generate a salt (random): 8 bytes
<start derived key generation>
Append salt to the password
MD5 Hash it, and hash the result, hash the result ... 1000 times
MD5 always gives us a 16 byte hash
Final result: first 8 bytes is the "key" and the next is the "initialization vector"
(there is something about the first 8 bytes needing to be of odd paraity, therefore
the least significant bit needs to be changed to 1 if required. We don't do it,
maybe the python crypto library does it for us)
<end derived key generation>
Pad the input string with 1-8 bytes (note: not 0-7, so we always have padding)
so that the result is a multiple of 8 bytes. Padding byte value is same as number of
bytes being padded, eg, \x07 if 7 bytes need to be padded.
Use the key and iv to encrypt the input string, using DES with CBC mode.
Prepend the encrypted value with the salt (needed for decrypting since it is random)
Base64 encode it -> this is your result
Decrypt:
Base64 decode the input message
Extract the salt (first 8 bytes). The rest is the encoded text.
Use derived key generation as in Encrypt above to get the key and iv
Decrypt the encoded text using key and iv
Remove padding -> this is your result
(I only have implemented decrypt here since that's all I needed,
but encrypt should be straighforward as well)
"""
def get_derived_key(password, salt, count):
key = password + salt
for i in range(count):
m = hashlib.md5(key)
key = m.digest()
return (key[:8], key[8:])
def decrypt(msg, password):
msg_bytes = base64.b64decode(msg)
salt = msg_bytes[:8]
enc_text = msg_bytes[8:]
(dk, iv) = get_derived_key(password, salt, 1000)
crypter = DES.new(dk, DES.MODE_CBC, iv)
text = crypter.decrypt(enc_text)
# remove the padding at the end, if any
return re.sub(r'[\x01-\x08]','',text)
def encrypt(msg, password):
salt = os.urandom(8)
pad_num = 8 - (len(msg) % 8)
for i in range(pad_num):
msg += chr(pad_num)
(dk, iv) = get_derived_key(password, salt, 1000)
crypter = DES.new(dk, DES.MODE_CBC, iv)
enc_text = crypter.encrypt(msg)
return base64.b64encode(salt + enc_text)
def main():
msg = "hello, world"
passwd = "mypassword"
s = encrypt(msg, passwd)
print s
print decrypt(s, passwd)
if __name__ == "__main__":
main()
对于@cooljohny
我真的不记得这段代码是如何工作的,但我 99% 肯定它确实如此。我通过仔细挖掘 pbewithmd5anddes 的 Java 实现中的规范来编写它,并针对它测试这个 python。我将这段代码准确地交付给了客户,并且对他们来说效果很好。我在粘贴之前更改了常量,但仅此而已。您应该能够确认它产生与 Java lib 相同的加密输出,然后在 ruby 中复制它。祝你好运!
import base64
from Crypto.Cipher import DES
from passlib.utils.pbkdf2 import pbkdf1
password = 'xxxxxxx'
iterations = 22
salt_bytes = [19,15,78,45,34,90,12,11]
# convert saltBytes to a string
salt_string = ''.join([chr(a) for a in salt_bytes])
# a sample request
raw_data = '''{"something":"to","encrypt":"here"}'''
# from the standard...
padding_value = (8 - (raw_data.__len__() % 8))
padding_data = chr(padding_value) * padding_value
padded_data = raw_data + padding_data
# 22 iterations, 16 is the # of bytes in an md5 digest
pbkres = pbkdf1(password, salt_string, iterations, 16, 'md5')
# split the digest into two 8-byte halves
# this gives the DES secret key and initializing vector
des_key, iv = pbkres[0:8], pbkres[8:16]
# encrypt with DES
cipher = DES.new(des_key, DES.MODE_CBC, iv)
cmsg = cipher.encrypt(padded_data)
# and base64 encode
base64.b64encode(cmsg)