在Ruby中实现PBEWithMD5AndDES

时间:2012-05-30 10:01:45

标签: ruby encryption cryptography

我正在尝试获得一个在Java世界中显然很受欢迎的加密库的ruby实现 - PBEWithMD5AndDES

有谁知道如何使用openssl或其他开源gem来执行与此格式兼容的加密/解密?

更新

我使用了一个gem chilkat来实现它,但它付了钱,我需要一个开源解决方案。

5 个答案:

答案 0 :(得分:2)

假设ruby具有DES实现,则不需要实际实现PBEWithMD5andDES。您需要实现的是密钥派生函数(从密码中获取密钥的人),然后使用适当的模式和填充将该派生密钥提供给DES。

值得庆幸的是,密钥派生函数在实现中并不是特别安全,因此您可以自己安全地完成。根据{{​​3}},PBEwithMD5AndDES实际上是在CBC模式下与DES一起使用的PBKDF1(ker派生函数)。

PBKDF1看起来并不难实现。看起来你可以通过for循环和md5调用来完成它。

请注意,由于Java和Ruby中可能使用不同的填充方案,您可能仍会得到一些奇怪的结果。我假设spec一个是pkcs 1.5 padding,但是快速浏览一下,我无法确认这个

  

rfc

     

PBKDF1应用哈希函数,该函数应为MD2 [6],MD5 [19]或   SHA-1 [18],用于导出密钥。派生密钥的长度是有界的   通过散列函数输出的长度,对于MD2是16个八位字节   SHA-1的MD5和20个八位字节。 PBKDF1与键兼容   PKCS#5 v1.5中的推导过程。

     

建议仅在与现有的兼容性时使用PBKDF1   应用程序,因为它产生的密钥可能不够大   一些应用程序。

     

PBKDF1(P,S,c,dkLen)

     

选项:哈希底层哈希函数

     

输入:P密码,八位字符串                      盐,八个八位字符串                      c迭代计数,正整数                      dkLen以派生密钥的八位字节为单位的预期长度,                                 一个正整数,最多16个MD2或                                 用于SHA-1的MD5和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.

答案 1 :(得分:2)

我知道它已经超级老了但是我遇到了同样的问题并且解决了它,所以在这里 加密,盐是你的盐刺,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

答案 2 :(得分:1)

为了它的价值,我发布了我的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)

答案 3 :(得分:1)

我已经从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()

答案 4 :(得分:1)

对于@cooljohny

我真的不记得这段代码是如何运作的,但我确信它确实如此。我通过仔细挖掘pbewithmd5anddes的Java实现中的规范来编写它,并针对它测试这个python。我把这个代码交给了客户端,它对他们来说很好。我在粘贴之前更改了常量,但这就是全部。您应该能够确认它产生与Java lib相同的加密输出,然后在ruby中复制它。祝你好运!

insert into target (col1, col2)
select source1.col1, source2.col2
from source1
inner join source2 on source1.id=source2.s1_id