Implementación de PBEWithMD5AndDES en Ruby
encryption cryptography (5)
Estoy tratando de obtener una implementación de ruby de una lib de encriptación aparentemente popular en el mundo de Java: PBEWithMD5AndDES
¿Alguien sabe cómo usar openssl u otra gema de código abierto para realizar cifrado / descifrado que sea compatible con este formato?
Actualizado:
Usé un chilkat de gemas para implementarlo, pero se paga, necesito una solución de código abierto.
No necesita implementar PBEWithMD5andDES suponiendo que ruby tiene una implementación DES. Lo que necesita implementar es la función de derivación de clave (a quién le obsequia una clave de una contraseña) y luego alimentar esa clave derivada a DES con el modo y relleno adecuados.
Afortunadamente, la función de derivación de claves no es especialmente crítica para la seguridad en la implementación, por lo que puede hacerlo usted mismo con la seguridad suficiente. De acuerdo con el rfc , PBEwithMD5AndDES es en realidad el PBKDF1 (una función de derivación ker) utilizada con DES en el modo CBC.
PBKDF1 no se ve tan difícil de implementar. Parece que puedes hacerlo con un bucle for y una llamada md5.
Tenga en cuenta que aún puede obtener resultados extraños debido a la posibilidad de utilizar un esquema de relleno diferente en Java y Ruby. Supongo que la especificación es relleno de pkcs 1.5, pero a primera vista, no puedo confirmarlo
PBKDF1 aplica una función hash, que será MD2 [6], MD5 [19] o SHA-1 [18], para derivar claves. La longitud de la clave derivada está limitada
por la longitud de la salida de la función hash, que es 16 octetos para MD2 y MD5 y 20 octetos para SHA-1. PBKDF1 es compatible con la clave
proceso de derivación en PKCS # 5 v1.5.PBKDF1 se recomienda solo para compatibilidad con sistemas existentes
aplicaciones ya que las claves que produce pueden no ser lo suficientemente grandes para
algunas aplicacionesPBKDF1 (P, S, c, dkLen)
Opciones: función hash subyacente hash
Entrada: contraseña P, una sal S de cadena de octeto, un recuento de iteración de cadena de ocho octetos, un entero positivo dkLen longitud prevista en octetos de clave derivada, un entero positivo, como máximo 16 para MD2 o MD5 y 20 para SHA-1
Salida: clave derivada DK, una cadena dkLen-octet
Pasos:
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.
Sé que es muy antiguo, pero tuve el mismo problema y lo resolví, así que aquí va a encriptar, donde la sal es tu aguijón de sal, la clave de acceso es tu cadena de clave de contraseña e iteraciones es el número de iteraciones que quieres usar
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
Por lo que vale, estoy publicando mi código python, que en realidad funciona (tengo toneladas de valores cifrados que se hicieron usando org.jasypt.util.text.BasicTextEncryptor, necesitaba descifrarlos).
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)
Actualicé la secuencia de comandos de python desde user3392439, con el respaldo de encriptación. Desearía que sea útil.
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()
Para @cooljohny
Realmente no recuerdo cómo funciona este código, pero estoy 99% seguro de que lo hace. Lo escribí explorando cuidadosamente las especificaciones de la implementación de Java de pbewithmd5anddes, y probando este python contra eso. Le entregué exactamente este código a un cliente, y funcionó bien para ellos. Cambié las constantes antes de pegarlas aquí, pero eso es todo. Debería poder confirmar que produce el mismo resultado encriptado que la lib de Java, y luego replicarlo en ruby. ¡Buena suerte!
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)