Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
c#BouncyCastle异常:焊盘块损坏_C#_Encryption_Cryptography_Aes_Bouncycastle - Fatal编程技术网

c#BouncyCastle异常:焊盘块损坏

c#BouncyCastle异常:焊盘块损坏,c#,encryption,cryptography,aes,bouncycastle,C#,Encryption,Cryptography,Aes,Bouncycastle,我一直在使用@nerdybeardo编写的用于加密和解密的代码。但是,我在尝试解密时遇到错误“pad block corrupted” Encryptor类如下所示,它实现了encrypt-then-MAC: /// <summary> /// Encrypt/decrypt + HMAC using BouncyCastle (C# Java port) /// </summary> /// <typeparam name="TBlockCipher">Th

我一直在使用@nerdybeardo编写的用于加密和解密的代码。但是,我在尝试解密时遇到错误“pad block corrupted”

Encryptor
类如下所示,它实现了encrypt-then-MAC:

/// <summary>
/// Encrypt/decrypt + HMAC using BouncyCastle (C# Java port)
/// </summary>
/// <typeparam name="TBlockCipher">The type of the block cipher.</typeparam>
/// <typeparam name="TDigest">The type of the digest.</typeparam>
/// <see cref="https://stackoverflow.com/a/13511671/119624"/>
public sealed class Encryptor<TBlockCipher, TDigest>
    where TBlockCipher : IBlockCipher, new()
    where TDigest : IDigest, new()
{
    private readonly Encoding encoding;

    private readonly byte[] key;

    private IBlockCipher blockCipher;

    private BufferedBlockCipher cipher;

    private HMac mac;

    /// <summary>
    /// Initializes a new instance of the <see cref="Encryptor{TBlockCipher, TDigest}"/> class.
    /// </summary>
    /// <param name="encoding">The encoding.</param>
    /// <param name="key">The key.</param>
    /// <param name="macKey">The mac key.</param>
    public Encryptor(Encoding encoding, byte[] key, byte[] macKey)
    {
        this.encoding = encoding;
        this.key = key;
        this.Init(key, macKey, new Pkcs7Padding());
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="Encryptor{TBlockCipher, TDigest}"/> class.
    /// </summary>
    /// <param name="encoding">The encoding.</param>
    /// <param name="key">The key.</param>
    /// <param name="macKey">The mac key.</param>
    /// <param name="padding">The padding.</param>
    public Encryptor(Encoding encoding, byte[] key, byte[] macKey, IBlockCipherPadding padding)
    {
        this.encoding = encoding;
        this.key = key;
        this.Init(key, macKey, padding);
    }

    /// <summary>
    /// Encrypts the specified plain.
    /// </summary>
    /// <param name="plain">The plain.</param>
    /// <returns></returns>
    public string Encrypt(string plain)
    {
        return Convert.ToBase64String(EncryptBytes(plain));
    }

    /// <summary>
    /// Encrypts the bytes.
    /// </summary>
    /// <param name="plain">The plain.</param>
    /// <returns></returns>
    public byte[] EncryptBytes(string plain)
    {
        byte[] input = this.encoding.GetBytes(plain);

        var iv = this.GenerateInitializationVector();

        var cipher = this.BouncyCastleCrypto(true, input, new ParametersWithIV(new KeyParameter(key), iv));
        byte[] message = CombineArrays(iv, cipher);

        this.mac.Reset();
        this.mac.BlockUpdate(message, 0, message.Length);
        var digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        this.mac.DoFinal(digest, 0);

        var result = CombineArrays(digest, message);
        return result;
    }

    /// <summary>
    /// Decrypts the bytes.
    /// </summary>
    /// <param name="bytes">The bytes.</param>
    /// <returns></returns>
    /// <exception cref="CryptoException"></exception>
    public byte[] DecryptBytes(byte[] bytes)
    {
        // split the digest into component parts
        var digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        var message = new byte[bytes.Length - digest.Length];
        var iv = new byte[this.blockCipher.GetBlockSize()];
        var cipher = new byte[message.Length - iv.Length];

        Buffer.BlockCopy(bytes, 0, digest, 0, digest.Length);
        Buffer.BlockCopy(bytes, digest.Length, message, 0, message.Length);
        if (!IsValidHMac(digest, message))
        {
            throw new CryptoException();
        }

        Buffer.BlockCopy(message, 0, iv, 0, iv.Length);
        Buffer.BlockCopy(message, iv.Length, cipher, 0, cipher.Length);

        byte[] result = this.BouncyCastleCrypto(false, cipher, new ParametersWithIV(new KeyParameter(key), iv));
        return result;
    }

    /// <summary>
    /// Decrypts the specified bytes.
    /// </summary>
    /// <param name="bytes">The bytes.</param>
    /// <returns></returns>
    public string Decrypt(byte[] bytes)
    {
        return this.encoding.GetString(DecryptBytes(bytes));
    }

    /// <summary>
    /// Decrypts the specified cipher.
    /// </summary>
    /// <param name="cipher">The cipher.</param>
    /// <returns></returns>
    public string Decrypt(string cipher)
    {
        return this.Decrypt(Convert.FromBase64String(cipher));
    }

    /// <summary>
    /// Combines the arrays.
    /// </summary>
    /// <param name="source1">The source1.</param>
    /// <param name="source2">The source2.</param>
    /// <returns></returns>
    private static byte[] CombineArrays(byte[] source1, byte[] source2)
    {
        var result = new byte[source1.Length + source2.Length];
        Buffer.BlockCopy(source1, 0, result, 0, source1.Length);
        Buffer.BlockCopy(source2, 0, result, source1.Length, source2.Length);

        return result;
    }

    /// <summary>
    /// Ares the equal.
    /// </summary>
    /// <param name="digest">The digest.</param>
    /// <param name="computed">The computed.</param>
    /// <returns></returns>
    private static bool AreEqual(byte[] digest, byte[] computed)
    {
        if (digest.Length != computed.Length)
        {
            return false;
        }

        var result = 0;
        for (var i = 0; i < digest.Length; i++)
        {
            result |= digest[i] ^ computed[i];
        }

        return result == 0;
    }

    /// <summary>
    /// Initializes the specified key.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <param name="macKey">The mac key.</param>
    /// <param name="padding">The padding.</param>
    private void Init(byte[] key, byte[] macKey, IBlockCipherPadding padding)
    {
        this.blockCipher = new CbcBlockCipher(new TBlockCipher());
        this.cipher = new PaddedBufferedBlockCipher(this.blockCipher, padding);
        this.mac = new HMac(new TDigest());
        this.mac.Init(new KeyParameter(macKey));
    }

    /// <summary>
    /// Determines whether [is valid h mac] [the specified digest].
    /// </summary>
    /// <param name="digest">The digest.</param>
    /// <param name="message">The message.</param>
    /// <returns></returns>
    private bool IsValidHMac(byte[] digest, byte[] message)
    {
        this.mac.Reset();
        this.mac.BlockUpdate(message, 0, message.Length);
        var computed = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        this.mac.DoFinal(computed, 0);

        return AreEqual(digest, computed);
    }

    /// <summary>
    /// Bouncy Castle Cryptography.
    /// </summary>
    /// <param name="forEncrypt">if set to <c>true</c> [for encrypt].</param>
    /// <param name="input">The input.</param>
    /// <param name="parameters">The parameters.</param>
    /// <returns></returns>
    private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, ICipherParameters parameters)
    {
        try
        {
            cipher.Init(forEncrypt, parameters);

            return this.cipher.DoFinal(input);
        }
        catch (CryptoException)
        {
            throw;
        }
    }

    /// <summary>
    /// Generates the initialization vector.
    /// </summary>
    /// <returns></returns>
    private byte[] GenerateInitializationVector()
    {
        using (var provider = new RNGCryptoServiceProvider())
        {
            // 1st block
            var result = new byte[this.blockCipher.GetBlockSize()];
            provider.GetBytes(result);

            return result;
        }
    }
}
当我想要解密时,我会执行以下操作:

public static class KeyManager
{
    /// <summary>
    /// Gets the key from passphrase.
    /// </summary>
    /// <param name="passphrase">The passphrase.</param>
    /// <param name="salt">The salt.</param>
    /// <returns>A byte array.</returns>
    public static byte[] GetKeyFromPassphrase(string passphrase, string salt)
    {
        var saltArray = Encoding.UTF8.GetBytes(salt);
        var rfcKey = new Rfc2898DeriveBytes(passphrase, saltArray, 10000);

        return rfcKey.GetBytes(32); // for a 256-bit key (32*8=128)
    }
}

var passphraseKey = KeyManager.GetKeyFromPassphrase(this.Passphrase, this.Salt);
var hmacKey = KeyManager.GetKeyFromPassphrase(this.ClientPassphrase, this.Salt);

var aesSha256Encryptor = new AesSha256Encryptor(passphraseKey, hmacKey);
var plaintext = aesSha256Encryptor.Decrypt(this.CipherText);
公共静态类密钥管理器
{
/// 
///从密码短语获取密钥。
/// 
///密码短语。
///盐。
///字节数组。
公共静态字节[]GetKeyFromPassphrase(字符串passphrase,字符串salt)
{
var saltArray=Encoding.UTF8.GetBytes(salt);
var rfcKey=new Rfc2898DeriveBytes(密码短语,saltArray,10000);
对于256位密钥(32*8=128),返回rfcKey.GetBytes(32);//
}
}
var passphraseKey=KeyManager.GetKeyFromPassphrase(this.Passphrase,this.Salt);
var hmacKey=KeyManager.GetKeyFromPassphrase(this.ClientPassphrase,this.Salt);
var AESSA256Encryptor=新的AESSA256Encryptor(密码短语,hmacKey);
var plaintext=AESSA256Encryptor.Decrypt(this.CipherText);
这是针对SAAS应用程序的。我的基本想法是要有一个用于加密/解密的SAAS应用程序的核心密码短语,但也要有一个用于MAC的特定客户端密码短语。这样做的原因是在端点之间分散密钥(一个在数据库中,一个在配置设置中)。salt保存到数据库中,以便可以使用相同的salt进行解密

有人能看出我做错了什么吗?为什么会出现焊盘块错误

仅供参考:这些密码短语是“horse battery Stapper correct”样式,因此其中包含连字符。不过,我不确定这是不是在转移注意力

我也不确定是否需要每行唯一的salt,或者我是否可以硬编码salt?那是不是太过分了

更新
对于任何发现这一点的人来说,错误只是我认为使用的密码短语不正确。填充错误就是结果。

不清楚到底是什么代码导致了您的问题(我的意思是没有一个简单的示例,我可以运行一下,看看哪里出了问题),但我构建了一个示例,它可以根据您的代码正确解密,没有错误,因此您可以查看它,并可能发现您的错误。 我将
EncryptionKeyManager.getSharedPassphase()
设置为公共,它将返回固定字符串
horse battery Stapper correct
。我将
EncryptionKeyManager.GetClientPassphrase()
也设置为公共的,它返回的是固定的
马电池

class Program {
    static void Main(string[] args) {
        // get key and salt from 
        var keyRecord = EncryptionKeyManager.GetKeyRecord();
        var aesSha256Encryptor = new AesSha256Encryptor(keyRecord.SharedKey, keyRecord.HmacKey);
        string targetData = "4343423343";

        var encrypted =  aesSha256Encryptor.Encrypt(targetData);
        var salt = keyRecord.Salt;
        var decrypted = Decrypt(encrypted, salt);
        Debug.Assert(targetData == decrypted);
        Console.WriteLine(decrypted);
        Console.ReadKey();

    }

    private static string Decrypt(string data, string salt) {
        var passphraseKey = KeyManager.GetKeyFromPassphrase(EncryptionKeyManager.GetSharedPassphrase(), salt);            
        var hmacKey = KeyManager.GetKeyFromPassphrase(EncryptionKeyManager.GetClientPassphrase(), salt);
        var aesSha256Encryptor = new AesSha256Encryptor(passphraseKey, hmacKey);
        var plaintext = aesSha256Encryptor.Decrypt(data);
        return plaintext;
    }
}

有点令人不安的是,你得到了一个填充错误,这意味着mac在解密之前没有经过验证或检查。我想,既然您使用的是两种不同的密码短语,那么如果您的mac密码短语是正确的,并且您的加密是关闭的,那么这可能是有意义的

泄漏填充错误的原因是选择密文攻击,因此只需扫描您借用的过于复杂的类,看起来应该检查mac,但是,如果您输入了错误的mac密码,我至少会再次检查,它不会给您一个填充错误,因为如果它这样做了,这意味着您正在使用的类存在问题

如果您使用的是密码短语,则每行的含盐量是最低的,而不是过度使用。我更喜欢每加密一次,正如你在书中看到的那样


您提到的另一件事是通过将一个密钥放在数据库中,另一个放在配置中来传播密钥。如果这是您的目标,那么将加密密钥的存储从mac密钥中分离出来并不是一个好方法,但是我对您的目标有点不清楚。如果你的应用程序在数据库中被sql注入破坏了,比如说,解密只需要加密密钥,mac就可以验证密文没有被篡改。如果您只想将密钥存储扩展一点,那么最好使用存储在数据库中的解密密钥对密文进行加密,这些密钥是使用存储在配置中的密钥进行解密的

谢谢。我已经把它放进了一个小的控制台应用程序中,它似乎工作得很好,但当我从数据库中获取实际数据时,它就不工作了。我开始怀疑,当数据写入数据库然后再读回时,问题是否在于损坏。数据类型是当前的“nvarchar(max)”,其中“varchar(max)”可能足以用于base64编码的字符串(尽管我不确定为什么使用“nvarchar”会产生不同)。如果您存储二进制数据-使用varbinary列,为什么要从base64转换为\并存储为字符串?对于您的问题,首先检查您的加密是否正确。从数据库中获取精确的加密数据和盐,并将其输入到您的控制台应用程序。如果这是好的-那么加密部分是好的-添加更多的日志到您的解密过程,以检查加密的数据在哪里损坏。老实说,我不信任我必须使用的ORM,但我可以尝试一下。我刚刚在本地运行了应用程序(SQL Server Express),并比较了base64字符串在写入数据库之前和之后的情况。结果是相同的。关于数据库排序是否有可能导致字符串损坏的问题?谁知道呢,这就是为什么我建议对二进制数据使用二进制列。。。但是,这个base64字符串在测试控制台应用程序中被正确解密了吗?好的,我发现了错误。问题是我期望的密码短语不正确。结果是填充错误。我有点不高兴。你的测试代码很有用,所以我会把你的答案标记为正确答案。
// get key and salt from 
var keyRecord = EncryptionKeyManager.GetKeyRecord();
var aesSha256Encryptor = new AesSha256Encryptor(keyRecord.SharedKey, keyRecord.HmacKey);

// now encrypt and store, include salt
entity.AccountNumber = aesSha256Encryptor.Encrypt(accountNumber);
entity.SortCode = aesSha256Encryptor.Encrypt(sortCode);
entity.Salt = keyRecord.Salt;
public static class KeyManager
{
    /// <summary>
    /// Gets the key from passphrase.
    /// </summary>
    /// <param name="passphrase">The passphrase.</param>
    /// <param name="salt">The salt.</param>
    /// <returns>A byte array.</returns>
    public static byte[] GetKeyFromPassphrase(string passphrase, string salt)
    {
        var saltArray = Encoding.UTF8.GetBytes(salt);
        var rfcKey = new Rfc2898DeriveBytes(passphrase, saltArray, 10000);

        return rfcKey.GetBytes(32); // for a 256-bit key (32*8=128)
    }
}

var passphraseKey = KeyManager.GetKeyFromPassphrase(this.Passphrase, this.Salt);
var hmacKey = KeyManager.GetKeyFromPassphrase(this.ClientPassphrase, this.Salt);

var aesSha256Encryptor = new AesSha256Encryptor(passphraseKey, hmacKey);
var plaintext = aesSha256Encryptor.Decrypt(this.CipherText);
class Program {
    static void Main(string[] args) {
        // get key and salt from 
        var keyRecord = EncryptionKeyManager.GetKeyRecord();
        var aesSha256Encryptor = new AesSha256Encryptor(keyRecord.SharedKey, keyRecord.HmacKey);
        string targetData = "4343423343";

        var encrypted =  aesSha256Encryptor.Encrypt(targetData);
        var salt = keyRecord.Salt;
        var decrypted = Decrypt(encrypted, salt);
        Debug.Assert(targetData == decrypted);
        Console.WriteLine(decrypted);
        Console.ReadKey();

    }

    private static string Decrypt(string data, string salt) {
        var passphraseKey = KeyManager.GetKeyFromPassphrase(EncryptionKeyManager.GetSharedPassphrase(), salt);            
        var hmacKey = KeyManager.GetKeyFromPassphrase(EncryptionKeyManager.GetClientPassphrase(), salt);
        var aesSha256Encryptor = new AesSha256Encryptor(passphraseKey, hmacKey);
        var plaintext = aesSha256Encryptor.Decrypt(data);
        return plaintext;
    }
}