If it's not immediately obvious, let me start by saying I am not a crypto person.
I have been tasked with replicating the behavior of Java's PBEWithMD5AndDES (MD5 digest with DES encryption) in Python 2.7.
I do have access to Python's cryptography toolkit PyCrypto.
Here is the Java code whose behavior I am trying to replicate:
import java.security.spec.KeySpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
public class EncryptInJava
{
public static void main(String[] args)
{
String encryptionPassword = "q1w2e3r4t5y6";
byte[] salt = { -128, 64, -32, 16, -8, 4, -2, 1 };
int iterations = 50;
try
{
KeySpec keySpec = new PBEKeySpec(encryptionPassword.toCharArray(), salt, iterations);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterations);
Cipher encoder = Cipher.getInstance(key.getAlgorithm());
encoder.init(Cipher.ENCRYPT_MODE, key, paramSpec);
String str_to_encrypt = "MyP455w0rd";
byte[] enc = encoder.doFinal(str_to_encrypt.getBytes("UTF8"));
System.out.println("encrypted = " + DatatypeConverter.printBase64Binary(enc));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
For the given values, it outputs the following:
encrypted = Icy6sAP7adLgRoXNYe9N8A==
Here's my ham-handed attempt to port the above to Python, encrypt_in_python.py
:
from Crypto.Hash import MD5
from Crypto.Cipher import DES
_password = 'q1w2e3r4t5y6'
_salt = '\x80\x40\xe0\x10\xf8\x04\xfe\x01'
_iterations = 50
plaintext_to_encrypt = 'MyP455w0rd'
if "__main__" == __name__:
"""Mimic Java's PBEWithMD5AndDES algorithm to produce a DES key"""
hasher = MD5.new()
hasher.update(_password)
hasher.update(_salt)
result = hasher.digest()
for i in range(1, _iterations):
hasher = MD5.new()
hasher.update(result)
result = hasher.digest()
key = result[:8]
encoder = DES.new(key)
encrypted = encoder.encrypt(plaintext_to_encrypt + ' ' * (8 - (len(plaintext_to_encrypt) % 8)))
print encrypted.encode('base64')
It outputs a completely different string.
Is it possible to port the Java implementation to a Python implementation with standard Python libraries?
Apparently the Python implementation requires that the plaintext that I encrypt be a multiple of eight characters, and I'm not even sure exactly how to pad my plaintext input to meet that condition.
Thanks for your help.
Thanks to GregS's comment, I was able to sort this conversion out!
For future reference, this Python code mimics the behavior of the Java code above:
from Crypto.Hash import MD5
from Crypto.Cipher import DES
_password = 'q1w2e3r4t5y6'
_salt = '\x80\x40\xe0\x10\xf8\x04\xfe\x01'
_iterations = 50
plaintext_to_encrypt = 'MyP455w0rd'
# Pad plaintext per RFC 2898 Section 6.1
padding = 8 - len(plaintext_to_encrypt) % 8
plaintext_to_encrypt += chr(padding) * padding
if "__main__" == __name__:
"""Mimic Java's PBEWithMD5AndDES algorithm to produce a DES key"""
hasher = MD5.new()
hasher.update(_password)
hasher.update(_salt)
result = hasher.digest()
for i in range(1, _iterations):
hasher = MD5.new()
hasher.update(result)
result = hasher.digest()
encoder = DES.new(result[:8], DES.MODE_CBC, result[8:16])
encrypted = encoder.encrypt(plaintext_to_encrypt)
print encrypted.encode('base64')
This Python code outputs the following in Python 2.7:
Icy6sAP7adLgRoXNYe9N8A==
Thanks again to GregS for pointing me in the right direction!
For Python 3.6, I tested with following code and it works with a little change from above:
from Crypto.Hash import MD5
from Crypto.Cipher import DES
import base64
import re
_password = b'q1w2e3r4t5y6'
_salt = b'\x80\x40\xe0\x10\xf8\x04\xfe\x01'
_iterations = 50
plaintext_to_encrypt = 'MyP455w0rd'
# Pad plaintext per RFC 2898 Section 6.1
padding = 8 - len(plaintext_to_encrypt) % 8
plaintext_to_encrypt += chr(padding) * padding
if "__main__" == __name__:
"""Mimic Java's PBEWithMD5AndDES algorithm to produce a DES key"""
hasher = MD5.new()
hasher.update(_password)
hasher.update(_salt)
result = hasher.digest()
for i in range(1, _iterations):
hasher = MD5.new()
hasher.update(result)
result = hasher.digest()
encoder = DES.new(result[:8], DES.MODE_CBC, result[8:16])
encrypted = encoder.encrypt(plaintext_to_encrypt)
print (str(base64.b64encode(encrypted),'utf-8'))
decoder = DES.new(result[:8], DES.MODE_CBC, result[8:])
d = str(decoder.decrypt(encrypted),'utf-8')
print (re.sub(r'[\x01-\x08]','',d))
Output:
Icy6sAP7adLgRoXNYe9N8A==
MyP455w0rd
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With