如何使用Bouncy Castle解密AES/CCM加密的密文?

9

加密

加密使用斯坦福Javascript加密库(SJCL)完成。以下是一个完整的加密示例,分为两个部分。第一部分是基于密码的密钥派生,使用PBKDF2。在第二部分中,使用派生密钥和初始化向量(IV)进行实际加密。请注意,盐和IV是硬编码的,以便更容易提供C#解密解决方案。

// Key derivation…
var password = "password";
var salt = sjcl.codec.hex.toBits(
    "5f9bcef98873d06a" // Random generated with sjcl.random.randomWords(2, 0);
);                     // Hex encoded with sjcl.codec.hex.toBits(randomSalt);
var iterations = 1000;
var keySize = 128;
var encryptionKey = sjcl.misc.pbkdf2(password, salt, iterations, keySize);

// Encryption…
var blockCipher = new sjcl.cipher.aes(encryptionKey);
var plainText = sjcl.codec.utf8String.toBits("secret");
var iv = sjcl.codec.hex.toBits("8291ff107e798a29");
var adata = ""; // What is adata?
var tag = 64; // What is tag? I think it is authentication strength.
var cipherText = sjcl.mode.ccm.encrypt(blockCipher, plainText, iv, adata, tag);

encryptionKey变量的值:

  • SJCL位数组:[ -74545279, -553931361, -1590906567, 1562838103 ]
  • Hex编码:fb8e8781defbad9fa12cb1395d270457
  • Base64编码:+46Hgd77rZ+hLLE5XScEVw==

iv变量的值:

  • SJCL位数组:[ -2104361200, 2121894441 ]
  • Hex编码:8291ff107e798a29
  • Base64编码:gpH/EH55iik=

cipherText变量的值:

  • SJCL 位数组: [ 1789401157, -1485204800, -440319203, 17593459146752 ]
  • 十六进制编码: 6aa81845a77992c0e5c1431d4be2
  • Base64 编码: aqgYRad5ksDlwUMdS+I=

问题

问题是:

如何使用Bouncy Castle解密密文?


在jbtule的帮助下,实现了工作解密示例

using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;

namespace SjclHelpers {

    public static class Encryption {

        /// <summary>Decrypts the cipher text.</summary>
        /// <param name="cipherText">The cipher text.</pararesm>
        /// <param name="key">The encryption key.</param>
        /// <param name="initializationVector">The IV.</param>
        /// <returns>The decrypted text.</returns>
        public static byte[] Decrypt(this byte[] cipherText,
            byte[] key, byte[] initializationVector) {
            var keyParameter = new KeyParameter(key);
            const int macSize = 64;
            var nonce = initializationVector;
            var associatedText = new byte [] {};
            var ccmParameters = new CcmParameters(
                keyParameter,
                macSize,
                nonce,
                associatedText);
            var ccmMode = new CcmBlockCipher(new AesFastEngine());
            var forEncryption = false;
            ccmMode.Init(forEncryption, ccmParameters);
            var plainBytes =
                new byte[ccmMode.GetOutputSize(cipherText.Length)];
            var res = ccmMode.ProcessBytes(
                cipherText, 0, cipherText.Length, plainBytes, 0);
            ccmMode.DoFinal(plainBytes, res);
            return plainBytes;
        }}}

我收到了一个 System.ArgumentException 异常。 我认为它是在抱怨其中一个字节数组太短了。
Boncy Castle 可以在 NuGet 网站的以下位置获取:http://nuget.org/packages/BouncyCastle

关于

AES/CCM解密方案将成为SjclHelpers项目的一部分,位于CodePlex上,并作为NuGet包发布。


密钥是如何检索的?根据我的经验,如果您没有使用证书,则可能无法正确地恢复加密密钥。 - James Black
我解码一个十六进制编码的密钥。解码过程如下:http://sjclhelpers.codeplex.com/SourceControl/changeset/view/12115#232154(测试`ToBytes_ValidHex_CorrectBytes`)。解码实现如下:http://sjclhelpers.codeplex.com/SourceControl/changeset/view/12115#232153(方法`ToBytes(this string hex)`)。 - knut
你需要至少在C#中实现PBKDF2以获得相同的密钥。如果Bouncy Castle真的没有自己的实现,那么这个旧问题中有一个实现,尽管Java版本似乎至少有一个SecretKeyFactory类可以执行此操作。 - Rup
我已经实现了PBKDF2(http://sjclhelpers.codeplex.com/SourceControl/changeset/view/12115#233554)。在这种情况下,我不需要使用PBKDF2,因为我已经从基于密码的密钥派生中得到了密钥。请参见上面的加密部分。密钥的十六进制表示为`6aa81845a77992c0e5c1431d4be2`。 - knut
1
嗯,你说你的问题“6aa81845a77992c0e5c1431d4be2”是密文而不是密钥? - jbtule
抱歉,你是对的。我现在明白了。谢谢!我已经更新了我的问题,并附上了派生加密密钥的值。十六进制的加密密钥是fb8e8781defbad9fa12cb1395d270457,不是密文6aa81845a77992c0e5c1431d4be2 - knut
2个回答

2

根据我所看到的:

  1. Nonce应该是IV。
  2. 通常使用AeadParameters而不是CcmParameters,但这可能仍然可以,但一定不要使用ParametersWithIV进行包装。
  3. associateText是可选的,因为如果需要,CCM可以验证与加密数据相关的未加密数据。您可能需要一个参数,因为它需要与sjcl adata相同,并且传输方式可以是任何内容。
  4. 正确的是tagmacSize是相同的。
  5. DoFinal应该是ccmMode.DoFinal(plainBytes,res);
  6. 出于安全考虑,在解密后,您应该将密码文本的最后(macSize / 8)个字节与ccmMode.GetMac()进行比较,以检查身份验证。
  7. var plainBytes = new byte[ccmMode.GetOutputSize(cipherText.Length)]

我还有一个Bouncy Castle中AES-GCM的示例,它与AES-CCM类似。在使用PBKDF2时,我使用associatedtext来发送关于盐和迭代的信息。 - jbtule
哦,你应该适当设置plainBytes的大小,我已经更新了答案。 - jbtule
谢谢!问题中的解密示例已更新。 - knut
@knut 请记住,在您实际的库中,在解密后检查MAC/Tag非常重要,而且应该是灵活的,以便库消费者可以在必要时传递一些相关数据,因为他们可以在JavaScript端执行此操作。 - jbtule

1
您无法使用Bouncy Castle解密sjcl JSON。因为SJCL的预先计算表与Bouncy Castle的不同。 我制作了自己的库。如果您仍在寻找解密解决方案,请尝试一下。 https://github.com/mebius1080p/SJCLDecryptor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接