用Golang解密用Java加密的内容(无iv)

用Golang解密用Java加密的内容(无iv)

问题描述:

Wenn I try to decrypt a string, encrypted in Java I get an error: "cipher: message authentication failed".

Does java inputOffset from AESCipher.engineDoFinal(byte[] input, int inputOffset, int inputLen) meens the same as the Go nonceSize in my code?

And is "NewGCMWithNonceSize" a right one decoder for my issue?

Thanks for help. Working solution:

JAVA

public static String encryptGCM(String data) throws CryptException {
    try {
        SecureRandom random = SecureRandom.getInstanceStrong();
        byte[] iv = new byte[12];
        random.nextBytes(iv);
        log.trace("IV: {}", Arrays.toString(iv));
        Key key = generateGcmKey();
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
        byte[] cipherText = cipher.doFinal(data.getBytes("UTF-8"));
        log.trace("encrypted: {}", Arrays.toString(cipherText));
        byte[] result = new byte[cipherText.length + iv.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(cipherText, 0, result, iv.length, cipherText.length);
        log.trace("Not encoded result: {}", Arrays.toString(result));
        return Base64.getEncoder().encodeToString(result);
    } catch (Exception ex) {
        log.error("Failure occured while encrypt text value!", ex);
        throw new CryptException(data, ex);
    }
}

private static Key generateGcmKey() throws CryptException {
    try {
        SecretKey originalKey = new SecretKeySpec(KEY.getBytes(), 0, KEY.getBytes().length, ALGO);
        log.trace("Encoded key: {}", Base64.getEncoder().encodeToString(originalKey.getEncoded()));
        return originalKey;
    } catch (Exception ex) {
        log.error("Failure occured while generate key!", ex);
        throw new CryptException("Failure occured while generate key!", ex);
    }
}

GO

func decode(data string) (string, error) {
  ciphertext, _ := base64.StdEncoding.DecodeString(data)
  key, _ := base64.URLEncoding.DecodeString(decodeKey)
  c, err := aes.NewCipher([]byte(key))
  if err != nil {
    return "", err
  }

  gcm, err := cipher.NewGCM(c)
  if err != nil {
    return "", err
  }

   nonceSize := 12
   if len(ciphertext) < nonceSize {
     return  "", errors.New("ciphertext too short")
   }

   nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

   result, err := gcm.Open(nil, nonce, ciphertext, nil)
   if err != nil {
     return "", err
   }
   return string(result), nil
 }

As noted in the comments to the question the follwing code is not secure since it uses the ECB mode of operation for encryption which should not be considered secure.


The problem is that GCM expects the underlying encrypted data to contain authentification information. This information is not supplied by your Java implementation.

Instead you can use your created AES cipher directly to loop over the blocks of the message. This could look like this:

func decode(data string) (string, error) {
    ciphertext, err := base64.StdEncoding.Decode(data)
    if err != nil {
        return "", err
    }

    c, err := aes.NewCipher([]byte(DECODE_KEY))
    if err != nil {
        return "", err
    }

    result := make([]byte, len(ciphertext))
    for i := 0; i < len(ciphertext); i += aes.BlockSize {
        c.Decrypt(result[i:], ciphertext[i:])
    }

    return string(result), nil
}

Each call to c.Decrypt decrypts one block from ciphertext into result as noted in the documentation. Also notice that the increment between loop iterations is the block size of the AES cipher.

The last block may contain padding, which you still need to remove.