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

时间:2018-06-28 07:39:09

标签: java go encryption

我尝试解密用Java加密的字符串,但出现错误:“密码:消息身份验证失败”

来自 AESCipher.engineDoFinal(byte [] input,int inputOffset,int inputLen)的Java inputOffset 的含义与Go nonceSize 相同在我的代码中?

NewGCMWithNonceSize ”是否适合我的问题?

感谢您的帮助。 工作解决方案:

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
 }

1 个答案:

答案 0 :(得分:0)

正如对该问题的评论所述,以下代码不安全,因为它使用ECB mode of operation进行加密,因此不应视为安全。


问题是GCM希望基础加密数据包含身份验证信息。 您的Java实现未提供此信息。

相反,您可以直接使用创建的AES密码来循环遍历消息的块。 看起来可能像这样:

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
}

每次调用c.Decrypt都会将ciphertext中的一个块解密为documentation中所述的result。 还要注意,循环迭代之间的增量是AES密码的块大小。

最后一块可能包含填充,您仍然需要删除该填充。