4

我正在使用 Pycryptodome(一个 PyCrypto 分支)来创建 AES-GCM 密文。我使用以下 Python 代码进行加密:

cek = os.urandom(16)
nonce = os.urandom(12)

cipher = AES.new(cek, AES.MODE_GCM, nonce=nonce, mac_len=16)
ciphertext = cipher.encrypt(message)

然后我将其传递给 Java 进行解密:

byte[] nonce = new byte[12];

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(cek, "AES");

IvParameterSpec ivParameterSpec = new IvParameterSpec(nonce);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmSpec);

byte[] decBytes = mCipher.doFinal(cipherText);

但是,我收到以下错误:

Exception in thread "main" javax.crypto.AEADBadTagException: Tag mismatch!
    at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:524)
    at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1023)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:960)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
4

2 回答 2

5

您缺少一件事:Pycryptodome 不会将哈希标记添加到消息中-您必须将其附加到加密消息中:

例如

ciphertext, tag = cipher.encrypt_and_digest(message)
ciphertext = ciphertext + tag
于 2018-05-17T16:20:31.923 回答
1

感谢Alastair McCormack 在上面的回答,这对我有用(Python 代码):

from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Hash import SHA256, SHA1
from Crypto.Signature import pss
from base64 import b64encode

data = 'hello world'.encode("utf-8")

with open("joe.pub", "rb") as f:
    encodedKey = f.read()
pubkey = RSA.importKey(encodedKey)
if pubkey.has_private():
    raise Exception('need a public key for encryption')

session_key = get_random_bytes(16)

# Encrypt the session key with the public RSA key
cipher_rsa = PKCS1_OAEP.new(pubkey, hashAlgo=SHA256, mgfunc=lambda x,y: pss.MGF1(x,y, SHA1))
enc_session_key = cipher_rsa.encrypt(session_key)

# Encrypt the data with the AES session key
cipher_aes = AES.new(session_key, AES.MODE_GCM)
ciphertext, tag = cipher_aes.encrypt_and_digest(data)
ciphertext = ciphertext + tag
mesg = ''.join([x for x in (enc_session_key, cipher_aes.nonce, tag, ciphertext)])
print b64encode(mesg)

以及相关的 Java 代码:

import java.io.FileReader;
import java.io.BufferedReader;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.GCMParameterSpec;
import java.util.Base64;
import java.util.Arrays;

public class So
{
    static {
        try {
            @SuppressWarnings("unchecked")
            Class<Provider> c = (Class<Provider>)Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
            Security.addProvider(c.getDeclaredConstructor().newInstance());
        } catch(java.lang.ClassNotFoundException |
                java.lang.NoSuchMethodException |
                java.lang.InstantiationException |
                java.lang.IllegalAccessException |
                java.lang.reflect.InvocationTargetException ex) {
        System.err.println("BouncyCastle not found");
        }
    }

    static private byte[] loadPvtKey(String filePath) throws java.io.IOException
    {
    BufferedReader in = null;
    try {
        in = new BufferedReader(new FileReader(filePath));

        /* read, check and discard first line. */
        String line = in.readLine();
        if ( ! line.equals("-----BEGIN PRIVATE KEY-----") )
        throw new IllegalArgumentException(filePath + ": not a private key file");
        StringBuilder sbuf = new StringBuilder();
        while ((line = in.readLine()) != null) {
        if ( line.equals("-----END PRIVATE KEY-----") ) break;
        sbuf.append(line);
        }
        return Base64.getDecoder().decode(sbuf.toString());
    } finally {
        try { if ( in != null ) in.close(); }
        catch(java.io.IOException ex) {}
    }
    }

    static public void main(String[] args) throws Exception
    {
    if ( args.length != 2 ) {
        System.err.println("usage: java Decrypt pvtKeyFile encString64");
        System.exit(1);
    }

    int index = 0;
    String pvtKeyFile = args[index++];
    String encString64 = args[index++];
    byte[] encBytes = Base64.getDecoder().decode(encString64);
    System.err.println("encrypted bytes: " + encBytes.length);

    byte[] bytes = loadPvtKey(pvtKeyFile);
    PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(bytes);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PrivateKey pvt = kf.generatePrivate(ks);

    Base64.Encoder encoder = Base64.getEncoder();
    byte[] encSessionKey = Arrays.copyOfRange(encBytes, 0, 256);
    System.err.printf("encSessionKey -> %s\n", encoder.encodeToString(encSessionKey));
    Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
    rsaCipher.init(Cipher.DECRYPT_MODE, pvt);
    byte[] sessionKey = rsaCipher.doFinal(encSessionKey);
    System.err.printf("sessionKey -> %s\n", encoder.encodeToString(sessionKey));

    byte[] iv = Arrays.copyOfRange(encBytes, 256, 256+16);
    System.err.printf("iv -> %s\n", encoder.encodeToString(iv));
    GCMParameterSpec ivSpec = new GCMParameterSpec(128, iv);

    Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
    SecretKeySpec aesKey = new SecretKeySpec(sessionKey, "AES");
    aesCipher.init(Cipher.DECRYPT_MODE, aesKey, ivSpec);

    byte[] tag = Arrays.copyOfRange(encBytes, 256+16, 256+32);
    System.err.printf("tag[%d] -> %s\n", tag.length, encoder.encodeToString(tag));

    byte[] cipherText = Arrays.copyOfRange(encBytes, 256+32, encBytes.length);
    System.err.printf("cipherText -> %s\n", encoder.encodeToString(cipherText));

    byte[] clearText = aesCipher.doFinal(cipherText);
    System.err.printf("clearText -> %s\n", new String(clearText, "UTF-8"));
    }
}
于 2018-07-31T13:52:50.023 回答