将Java代码转换为Python,用于3DES加密,使用MD5消息摘要和DESede/CBC/PKCS5Padding。

huangapple go评论89阅读模式
英文:

Converting java code for 3DES Encryption with md5 message digest and DESede/CBC/PKCS5Padding to python

问题

以下是翻译好的代码部分:

Java代码:

import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Main{

    public static void main(String[] args) throws Exception {
        String text = "aug@2019";
        String codedtext = new Main().encrypt(text);
        System.out.println(codedtext);
    }

    public String encrypt(String message) throws Exception {
        final MessageDigest md = MessageDigest.getInstance("md5");
        final byte[] digestOfPassword = md.digest("Lgp!kdao2020"
                .getBytes("utf-8"));
        final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
        for (int j = 0, k = 16; j < 8;) {
            keyBytes[k++] = keyBytes[j++];
        }

        final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
        final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
        final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key, iv);

        final byte[] plainTextBytes = message.getBytes("utf-8");
        final byte[] cipherText = cipher.doFinal(plainTextBytes);
        final String encodedCipherText = new String(java.util.Base64.getMimeEncoder().encode(cipherText),
                         "UTF-8");
        return encodedCipherText;
    }

}

Python代码:

import base64
from Crypto.Cipher import AES
import pyDes
from Crypto import Random
import hashlib

def encrypt(message, passkey):
    hash_object = hashlib.md5(passkey.encode("utf-8"))
    digested_passkey = hash_object.digest()
    print(digested_passkey)
    key24 = digested_passkey[:24]
    des = pyDes.des(key24)
    message = message.encode('utf-8')
    message = message + (16 - len(message) % 16) * chr(16 - len(message) % 16)
    iv = Random.new().read(AES.block_size)
    cipher = AES.new(des, AES.MODE_CBC, iv)
    return base64.b64encode(iv + cipher.encrypt(message))

print(encrypt('aug@2019', 'Lgp!kdao2020'))

如果您在Python代码中遇到"ValueError: Invalid DES key size. Key must be exactly 8 bytes long."错误,这是由于在pyDes中使用的是DES加密算法,而您的密钥是24字节长的,与DES的8字节密钥长度不匹配。您的Java代码使用的是3DES (DESede) 加密算法,因此密钥长度为24字节。如果您希望在Python代码中使用3DES,请考虑使用其他支持3DES的加密库,如pyCryptodome。

英文:

I have this working java code which encrypts the passphrase with 3DES encryption-

import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Main{
public static void main(String[] args) throws Exception {
String text = &quot;aug@2019&quot;;
String codedtext = new Main().encrypt(text);
System.out.println(codedtext);
}
public String encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance(&quot;md5&quot;);
final byte[] digestOfPassword = md.digest(&quot;Lgp!kdao2020&quot;
.getBytes(&quot;utf-8&quot;));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j &lt; 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, &quot;DESede&quot;);
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance(&quot;DESede/CBC/PKCS5Padding&quot;);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes(&quot;utf-8&quot;);
final byte[] cipherText = cipher.doFinal(plainTextBytes);
final String encodedCipherText = new String(java.util.Base64.getMimeEncoder().encode(cipherText),
&quot;UTF-8&quot;);
return encodedCipherText;
}
}

I wanted to have the same encryption in Python, so the encryption generated from both the codes are same, this is my python code

import base64
from Crypto.Cipher import AES
import pyDes
from Crypto import Random
import hashlib
def encrypt(message, passkey):
hash_object = hashlib.md5(passkey.encode(&quot;utf-8&quot;))
digested_passkey = hash_object.digest()
print(digested_passkey)
key24 = digested_passkey[:24]
des = pyDes.des(key24);
message = message.encode(&#39;utf-8&#39;)
message = message + (16 - len(message) % 16) * chr(16 - len(message) % 16)
iv = Random.new().read(AES.block_size)
cipher = AES.new(des, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(message))
print(encrypt(&#39;aug@2019&#39;, &#39;Lgp!kdao2020&#39;))

I am getting an error -> ValueError: Invalid DES key size. Key must be exactly 8 bytes long.

答案1

得分: 1

这两段代码之间存在许多差异:

  • 在Java代码中,密钥是通过将16字节的MD5哈希与相同哈希的前8字节连接而生成的。在Python代码中,密钥生成似乎根本不起作用(从{: &lt;24}更改为[:24]实际上并没有改进)。第二次更改digested_passkey[:24]digested_passkey相同,并且之所以起作用,是因为PyCryptodome会自动将密钥扩展为24字节,以符合2键Triple DES的要求。
  • 在Python代码中,使用了两个库:pyDesPyCryptodome。在这里应该只应用一个库。关于PyCryptodome,使用了AES。实际上,与Java代码不同,AES是一种比Triple DES更快/现代的算法,但它只是与之不同。
  • Python代码还实现了填充,但这是不必要的,因为PyCryptodome(与传统的PyCrypto相反)支持填充。除此之外,填充的实现方式是错误的,因为它使用了16字节的块大小,但Triple DES的块大小是8字节。
  • 在Java代码中,IV采用了0向量,而在Python代码中,IV是随机的(实际上是正确的,但与Java代码不同)。
  • 在Java代码中,IV和密文没有连接在一起,而在Python代码中,它们被连接在一起(实际上是正确的,但与Java代码不同)。

除此之外,所使用的算法是不安全的(MD5)或已过时/较慢的(Triple DES),如评论中所提到的。作为IV的0向量完全是不安全的。

以下是代码部分:

import base64
from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad
import hashlib

def encrypt(message, passkey):
    hash_object = hashlib.md5(passkey)
    digested_passkey = hash_object.digest()

    key24 = digested_passkey + digested_passkey[0:8]
    
    message = pad(message, 8)
    
    iv = bytes.fromhex('0000000000000000')

    cipher = DES3.new(key24, DES3.MODE_CBC, iv)
    
    return base64.b64encode(cipher.encrypt(message))

print(encrypt(b'aug@2019', b'Lgp!kdao2020'))

这将产生与Java代码相同的结果(7B0aNUwOU1ECqKqnIZs6mQ==)。

英文:

There are a lot of differences between both codes:

  • In the Java code the key is generated by concatenating the 16 bytes MD5 hash with the first 8 bytes of the same hash. In the Python code, key generation does not seem to work at all (changing from {: &lt;24} to [:24] does not really make it better). The 2nd change finally, digested_passkey[:24], is identical to digested_passkey and works because PyCryptodome automatically extends the key to 24 bytes according to 2-key Triple DES.
  • In the Python code, two libraries are used: pyDes and PyCryptodome. Only one library should be applied here. Regarding PyCryptodome AES is used. Actually AES is the faster/modern algorithm compared to Triple DES, but it just differs from that of the Java code.
  • The Python code also implements padding, which is not necessary, because PyCryptodome (in contrast to the legacy PyCrypto) supports padding. Apart from that the padding is wrongly implemented because it uses a block size of 16 bytes, but Triple DES has a block size of 8 bytes.
  • In the Java code a 0 vector is taken for the IV, in the Python code a random IV (which is actually correct, but just differs from the Java code).
  • In the Java code IV and ciphertext are not concatenated, in the Python code they are concatenated (which is actually correct, but just differs from the Java code).

Apart from that, the algorithms used are insecure (MD5) or outdated/slow (Triple DES), as already mentioned in the comment. Also a 0-vector as IV is completely insecure.

import base64
#from Crypto.Cipher import AES
from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad
#import pyDes
#from Crypto import Random
import hashlib
def encrypt(message, passkey):
#hash_object = hashlib.md5(passkey.encode(&quot;utf-8&quot;))                       
hash_object = hashlib.md5(passkey) 
digested_passkey = hash_object.digest()
print(digested_passkey)
#key24 = &quot;[:24]&quot;.format(digested_passkey) 
key24 = digested_passkey + digested_passkey[0:8]        # Derive key as in Java
#des = pyDes.des(key24);                                # Remove pyDes
#message = message.encode(&#39;utf-8&#39;) 
#message = message + (16 - len(message) % 16) * chr(16 - len(message) % 16) 
message = pad(message, 8)                               # Use padding from PyCryptodome       
#iv = Random.new().read(AES.block_size)                 # For Java code compliance: Use 0-IV
iv = bytes.fromhex(&#39;0000000000000000&#39;)
#cipher = AES.new(des, AES.MODE_CBC, iv)                # For Java code compliance: Use TripleDES
cipher = DES3.new(key24, DES3.MODE_CBC, iv)
#return base64.b64encode(iv + cipher.encrypt(message))  # For Java code compliance: Don&#39;t concatenate IV and ciphertext
return base64.b64encode(cipher.encrypt(message)) 
#print(encrypt(&#39;aug@2019&#39;, &#39;Lgp!kdao2020&#39;))                 # Better: Pass binary data
print(encrypt(b&#39;aug@2019&#39;, b&#39;Lgp!kdao2020&#39;))

which gives the result form the Java code (7B0aNUwOU1ECqKqnIZs6mQ==).

huangapple
  • 本文由 发表于 2020年9月29日 14:11:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/64113853.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定