【问题标题】:What is the simplest way to encrypt/ decrypt a byte array using BouncyCastle's block cipher?使用 BouncyCastle 的分组密码加密/解密字节数组的最简单方法是什么?
【发布时间】:2015-08-11 06:38:33
【问题描述】:

如果我有一个BlockCipher 和一个byte[] 是从包含秘密消息的String 获得的,那么获取加密消息的byte[] 的最简单方法是什么?

在普通的Java API中,我可以只做cipher.doFinal(secretMessage),但这里似乎没有类似的东西,它只处理块。

我知道我可以使用BufferedBlockCipher,但这仍然不能显着简化事情。使用此密码最简单的高级方法是什么?

【问题讨论】:

    标签: java encryption bouncycastle


    【解决方案1】:

    好的,所以使用轻量级 API 和计数器模式,这是您将获得的最简单和现代的模式之一:

    public class BouncyEncrypt {
    
        private static final int IV_SIZE = 16;
    
        public static void main(String[] args) throws Exception {
            // key should really consist of 16 random bytes
            byte[] keyData = new byte[256 / Byte.SIZE];
            KeyParameter key = new KeyParameter(keyData);
    
            byte[] ciphertext = encryptWithAES_CTR(key, "owlstead");
            System.out.println(decryptWithAES_CTR(key, ciphertext));
        }
    
        private static byte[] encryptWithAES_CTR(KeyParameter key, String in)
                throws IllegalArgumentException, UnsupportedEncodingException,
                DataLengthException {
            // iv should be unique for each encryption with the same key
            byte[] ivData = new byte[IV_SIZE];
            SecureRandom rng = new SecureRandom();
            rng.nextBytes(ivData);
            ParametersWithIV iv = new ParametersWithIV(key, ivData);
    
            SICBlockCipher aesCTR = new SICBlockCipher(new AESFastEngine());
    
            aesCTR.init(true, iv);
            byte[] plaintext = in.getBytes("UTF-8");
            byte[] ciphertext = new byte[ivData.length + plaintext.length];
            System.arraycopy(ivData, 0, ciphertext, 0, IV_SIZE);
            aesCTR.processBytes(plaintext, 0, plaintext.length, ciphertext, IV_SIZE);
            return ciphertext;
        }
    
        private static String decryptWithAES_CTR(KeyParameter key, byte[] ciphertext)
                throws IllegalArgumentException, UnsupportedEncodingException,
                DataLengthException {
            if (ciphertext.length < IV_SIZE) {
                throw new IllegalArgumentException("Ciphertext too short to contain IV");
            }
    
            ParametersWithIV iv = new ParametersWithIV(key, ciphertext, 0, IV_SIZE);
    
            SICBlockCipher aesCTR = new SICBlockCipher(new AESFastEngine());
            aesCTR.init(true, iv);
            byte[] plaintext = new byte[ciphertext.length - IV_SIZE];
            aesCTR.processBytes(ciphertext, IV_SIZE, plaintext.length, plaintext, 0);
            return new String(plaintext, "UTF-8");
        }
    }
    

    计数器模式不需要填充并且完全在线,因此您只需致电processBytes。对于 CBC 模式,您应该查看 PaddedBufferedBlockCipher。在解密过程中,您仍然需要处理少量的缓冲区:在解密之前,您不知道存在的填充量。

    您可以删除 IV 代码和 UTF-8 字符解码 + 异常处理,但您会不安全并且可能不兼容。此代码将 IV 作为密文的前缀。

    【讨论】:

      【解决方案2】:

      BouncyCastle 实现了“普通 Java API”,因此您可以使用 Cipher.doFinal(String.getBytes()),您只需在获取 Cipher 时指定提供者“BC”:Cipher.getInstance("YourTransformation", "BC")

      【讨论】:

      • 不幸的是,我不得不使用实际的 BlockCipher 对象。我试过这样做,但我无法将 TweakableBlockCipherParams 与“普通 Java API”一起使用。我实际上也发布了一个关于此的问题:stackoverflow.com/questions/30495630/…
      【解决方案3】:

      使用 Bouncy Castle 的 CipherOutputStream。它是最接近 Java API 的东西。

      static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
      
      public static void main(String[] args) throws Exception {
          KeyGenerator kg = KeyGenerator.getInstance("Threefish-1024", PROVIDER);
          kg.init(1024);
          KeyParameter key = new KeyParameter(kg.generateKey().getEncoded());
          byte[] tweak = new byte[16];
          TweakableBlockCipherParameters params = new TweakableBlockCipherParameters(key, tweak);
      
          byte[] plaintext = "Hi! I'm cat!".getBytes();
          byte[] ciphertext = encrypt(params, plaintext);
          System.out.println(new String(decrypt(params, ciphertext)));
          // prints "Hi! I'm cat!"
      }
      
      static byte[] encrypt(TweakableBlockCipherParameters params, byte[] plaintext) throws Exception {
          return encryptOrDecrypt(true, params, plaintext);
      }
      
      static byte[] decrypt(TweakableBlockCipherParameters params, byte[] ciphertext) throws Exception {
          return encryptOrDecrypt(false, params, ciphertext);
      }
      
      static byte[] encryptOrDecrypt(boolean encrypt, TweakableBlockCipherParameters params, byte[] bytes) throws Exception {
          PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
                  new CBCBlockCipher(
                          new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024)), new PKCS7Padding());
          cipher.init(encrypt, params);
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          CipherOutputStream cos = new CipherOutputStream(baos, cipher);
          cos.write(bytes);
          // calling CipherOutputStream.close is mandatory
          // it acts like Cipher.doFinal
          cos.close();
          return baos.toByteArray();
      }
      

      Link to my answer to a similar and related question.

      【讨论】:

      • 不幸的是,我不能使用这个,我坚持使用 BouncyCastle 的实现(请参阅其他答案的评论)。
      • @codebreaker 我已经更新了我的答案,请看一下。
      猜你喜欢
      • 2011-07-12
      • 2010-09-13
      • 2011-05-20
      • 2017-08-06
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多