代码之家  ›  专栏  ›  技术社区  ›  Alastair McCormack

Pycrypto AES GCM加密和Java解密

  •  4
  • Alastair McCormack  · 技术社区  · 7 年前

    我正在使用Pycryptodome(PyCryptoFork)创建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)
    
    2 回复  |  直到 7 年前
        1
  •  5
  •   Alastair McCormack    7 年前

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

    例如。

    ciphertext, tag = cipher.encrypt_and_digest(message)
    ciphertext = ciphertext + tag
    
        2
  •  1
  •   Alex Kulinkovich    7 年前

    幸亏 Alastair McCormack's answer above ,以下是对我有用的东西(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"));
        }
    }