复制Java';Python 2.7中的s PBewithmD5和DES

复制Java';Python 2.7中的s PBewithmD5和DES,java,python-2.7,encryption,md5,des,Java,Python 2.7,Encryption,Md5,Des,如果不是很明显,让我先说我不是加密人 我的任务是在Python2.7中复制Java的PBEWithMD5AndDES(带有DES加密的MD5摘要)的行为 我确实可以访问Python的加密工具包PyCrypto 下面是我试图复制其行为的Java代码: import java.security.spec.KeySpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.SecretKey; import javax.crypto.Se

如果不是很明显,让我先说我不是加密人

我的任务是在Python2.7中复制Java的PBEWithMD5AndDES(带有DES加密的MD5摘要)的行为

我确实可以访问Python的加密工具包PyCrypto

下面是我试图复制其行为的Java代码:

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();
      }
    }
}
对于给定值,它输出以下内容:

encrypted = Icy6sAP7adLgRoXNYe9N8A==
下面是我将上述内容移植到Python的笨拙尝试,
encrypt\u 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')
它输出一个完全不同的字符串

是否可以将Java实现移植到带有标准Python库的Python实现

显然,Python实现要求我加密的明文是8个字符的倍数,我甚至不确定如何填充明文输入以满足该条件


谢谢你的帮助。

多亏了格雷格的评论,我才能够解决这个转换问题

为了便于将来参考,此Python代码模仿了上述Java代码的行为:

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')
此Python代码在Python 2.7中输出以下内容:

Icy6sAP7adLgRoXNYe9N8A==
再次感谢格雷格为我指明了正确的方向

我找到了一个


对于Python3.6,我使用以下代码进行了测试,与上面的代码相比,它的工作原理略有变化:

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))
输出:

ICY6SAP7ADLGROXNY9N8A==

MyP455w0rd


如果您在Python3中使用较新的Cryptodome库,还需要将明文_to_encrypt编码为“latin-1”,如下所示

from Cryptodome.Hash import MD5
from Cryptodome.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.encode('latin-1'))  #encoded plaintext

    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))

具体见。您需要在CBC模式下使用DES,密钥是哈希结果的前8个字节,IV是第二个8个字节。这是一种错误的密码派生方法,不应使用。取而代之的是,使用随机salt在HMAC上迭代大约100ms(salt需要与散列一起保存)。使用诸如password_hash、PBKDF2、Bcrypt等函数和类似函数。关键是让攻击者花费大量时间通过暴力手段查找密码。请参阅OWASP(开放式Web应用程序安全项目)。另请参阅Security Stackexchange。实际上,为了防止有人(如我)想知道\xe0是如何计算为-32的十六进制的,您的方法如下:找到31的十六进制,即0x1f,然后从0xff中减去它。所以0xff-0x1f=0xe0。这就是如何推导负整数的十六进制等价物。对于相同的解释(为什么和如何)谷歌数字系统和负数表示在不同的数字系统。
from Cryptodome.Hash import MD5
from Cryptodome.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.encode('latin-1'))  #encoded plaintext

    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))