Decrypting data in Python that was encrypted in 3D

2019-04-16 09:28发布

问题:

I'm trying to decrypt data using PyCrypto. The data was encoded in Java with the javax.crypto package. The encryption is Triple DES (referred to as "DESede" in Java). As far as I can tell, default settings are used for everything. However, when I go to decrypt the data in Python there is always a problem with the data.

Here's the Java code that does encrypting/decrypting:

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import java.security.spec.KeySpec;

public final class Encrypter
{
    public static final String DESEDE_ENCRYPTION = "DESede";

    private KeySpec keySpec;
    private SecretKeyFactory keyFactory;
    private Cipher cipher;

    private static final String UNICODE_FORMAT = "UTF8";

    public Encrypter(String encryptionKey)
        throws Exception
    {
        byte[] keyAsBytes = encryptionKey.getBytes(UNICODE_FORMAT);
        keySpec = new DESedeKeySpec(keyAsBytes);
        keyFactory = SecretKeyFactory.getInstance(DESEDE_ENCRYPTION);
        cipher = Cipher.getInstance(DESEDE_ENCRYPTION);
    }

    public String encryptString(String unencryptedString)
    {
        SecretKey key = keyFactory.generateSecret(keySpec);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] cleartext = unencryptedString.getBytes(UNICODE_FORMAT);
        byte[] ciphertext = cipher.doFinal(cleartext);

        BASE64Encoder base64encoder = new BASE64Encoder();
        return base64encoder.encode(ciphertext);
    }

    public String decryptString(String encryptedString)
    {
        SecretKey key = keyFactory.generateSecret(keySpec);
        cipher.init(Cipher.DECRYPT_MODE, key);
        BASE64Decoder base64decoder = new BASE64Decoder();
        byte[] ciphertext = base64decoder.decodeBuffer(encryptedString);
        byte[] cleartext = cipher.doFinal(ciphertext);

        return bytesToString(cleartext);
    }

    private static String bytesToString(byte[] bytes)
    {
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes)
        {
            sb.append((char) aByte);
        }
        return sb.toString();
    }
}

But when I take one of the base64-encoded strings that was produced by this code, I can't decode it. Here's an example of some of the python code I've tried:

from Crypto.Cipher import DES3
import array

key = <value of the key, as a hex string>
encryptedvalue = <the value that's encrypted, as a string>
keyarray = array.array('B', key.decode("hex"))
des = DES3.new(keyarray)
value = des.decrypt(encryptedvalue.decode('base64'))

value.decode('utf-8') # Gives me an error

The errors I've gotten have looked along the lines of

UnicodeDecodeError: 'utf8' codec can't decode byte 0xa7 in position 6: invalid start byte

Which means that somewhere along the way, I haven't gotten something set up correctly. I've been working on this for a few hours, even going so far as trying to look into the SunJCE source code, which implements DESede, to see what defaults they use, but to no avail. I'm going to be using this as part of a script that runs automatically, so I'd really rather not have to use Java to do my decryption. Does anyone know what I need to do to decrypt my data correctly?

回答1:

All I had to do to make it work was to change this line

keyarray = array.array('B', key.decode("hex"))

to this:

keyarray = array.array('B', key.encode("utf-8"))

This matches the way that java was encoding the key, allowing me to have the correct encryption key.


If you've come here hoping to learn something from this question, here's some general advice:

  1. Double-check your assumptions: The key string was a hex string, so I assumed it was being used as such.
  2. Make sure you know what your assumptions are: I didn't consciously think about how I was making an assumption about how the key was used. This tends to be a very frequent problem in both programming and life in general.
  3. Check all the values along the way (especially when you have an Oracle): Looking at the values in the byte arrays was what led me to realizing my problem.