douti19680318 2018-04-05 02:04 采纳率: 0%
浏览 1205

Java:采用Base64编码的AES / CFB / NoPadding加密

我想使用AES / CFB / NoPadding在Java中加密字节。

我在Stackoverflow上发现了以下问题,但它仅涉及解密功能: Golang中的AES加密和Java中的解密

我将如何在Java中编写与以下Go代码类似的加密功能?

package main

import (
    "io"
    "crypto/aes"
    "crypto/cipher"
    "encoding/base64"
    "crypto/rand"
)

func encrypt(key, data []byte) string {
    block, err := aes.NewCipher(key)
    if err != nil {
      return nil, err
    }
    encoded := base64.StdEncoding.EncodeToString(data)
    ciphertext := make( []byte, aes.BlockSize+len(encoded) )
    iv := ciphertext[:aes.BlockSize]
    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
      return nil, err
    }
    cfb := cipher.NewCFBEncrypter(block, iv)
    cfb.XORKeyStream( ciphertext[aes.BlockSize:], []byte(encoded) )
    return ciphertext, nil
}

我的Golang解密函数如下所示(它应该返回base64代码):

func decrypt(key, data []byte) ([]byte, error) {
  blockcipher, err := aes.NewCipher(key)
  if err != nil {
    return nil, err
  }
  if len(data) < aes.BlockSize {
    return nil, errors.New("ciphertext too short")
  }
  iv := data[:aes.BlockSize]
  data = data[aes.BlockSize:]
  cfb := cipher.NewCFBDecrypter(blockcipher, iv)
  cfb.XORKeyStream(data, data)
  return data, nil
}

我当前的Java加密代码(似乎无法解密)如下所示:

private byte[] encrypt(byte[] payload) {
    try {
        SecretKeySpec key_spec = new SecretKeySpec(current_encryption_key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
        byte[] encoded_payload = Base64.encode(payload, Base64.DEFAULT);
        IvParameterSpec iv = new IvParameterSpec( new byte[16] );
        cipher.init(Cipher.ENCRYPT_MODE, key_spec, iv);
        return cipher.doFinal(encoded_payload);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return new byte[0];
}

我的加密代码如下所示(并且在Golang和Java上都可以正常工作):

private byte[] decrypt(byte[] payload) {
    try {
        SecretKeySpec key_spec = new SecretKeySpec(current_encryption_key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
        int block_size = cipher.getBlockSize();
        IvParameterSpec iv = new IvParameterSpec( Arrays.copyOf(payload, block_size) );
        byte[] decryption_data = Arrays.copyOfRange(payload, block_size, payload.length);
        cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);
        byte[] decrypted_payload = cipher.doFinal(decryption_data);
        return Base64.decode(decrypted_payload, Base64.DEFAULT);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return new byte[0];
}

当我用Java加密某些内容,然后尝试在Java中使用解密器时,出现了以下解密错误:

04-13 14:16:48.382 3791-3791/com.domain.interpretest W/System.err: java.lang.IllegalArgumentException: 16 > 9
04-13 14:16:48.388 3791-3791/com.domain.interpretest W/System.err:     at java.util.Arrays.copyOfRange(Arrays.java:3447)
  • 写回答

1条回答 默认 最新

  • doudang8824 2018-04-13 06:00
    关注

    Summary of your Go encryption:

    • encrypt the base64-ed data with a random IV (and key you don't describe), putting the IV followed by the ciphertext in a single buffer

    Summary of your Java decryption:

    • take the first block from the buffer and use it as IV to decrypt the remainder of the buffer, and de-base64 it

    These match.

    Summary of your Java encryption:

    • encrypt with a fixed (all-zero) IV, and return a buffer containing only the ciphertext, not the IV anyplace

    This does not match. The decryption tries to remove the IV from a buffer that doesn't have the IV in it. In fact your ciphertext is only 9 bytes (although I don't understand why it's not a multiple of 4); this is shorter than one AES block, so the Arrays.copyOfRange fails outright.

    Solution: your Java encryption should use a random IV and return a buffer containing the IV followed by the ciphertext, like your Go encryption does. One approach that fairly closely mimics your Go:

    // once, during initialization 
    SecureRandom rand = new SecureRandom(); // or .getInstance* as you prefer 
    
    // unchanged 
    SecretKeySpec key_spec = new SecretKeySpec(current_encryption_key, "AES");
    Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
    byte[] encoded_payload = Base64.encode(payload, Base64.DEFAULT);
    // changed 
    int block_size = cipher.getBlockSize();
    // create random IV
    byte[] buffer = new byte[block_size];
    rand.nextBytes(buffer);
    IvParameterSpec iv = new IvParameterSpec (buffer);
    // expand buffer already containing IV to make room for ciphertext
    buffer = Arrays.copyOf (buffer, block_size+encoded_payload.length);
    // unchanged 
    cipher.init(Cipher.ENCRYPT_MODE, key_spec, iv);
    // changed
    // do encryption into correct part of existing buffer
    cipher.doFinal(encoded_payload,0,encoded_payload.length, buffer,block_size);
    return buffer;
    

    PS: why are you bothering to base64 encode, and decode, your plaintext? AES, like all modern ciphers, can handle any combination of bits just fine. It is more common to base64 the ciphertext and IV when other code can't handle them as bits/binary.

    评论

报告相同问题?

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?