c#BouncyCastle异常:pad块损坏 [英] c# BouncyCastle Exception: pad block corrupted

查看:242
本文介绍了c#BouncyCastle异常:pad块损坏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在使用由@nerdybeardo撰写的此处加密和解密的代码。但是,我得到错误垫块损坏,当尝试解密。

I've been using the code that was posted here to encrypt and decrypt, written by @nerdybeardo. However, I'm getting the error "pad block corrupted" when trying to decrypt.

Encryptor 类看起来像这样,然后实现加密然后MAC:

The Encryptor class looks like this, which implements 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="http://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;
        }
    }
}



我有一个简单的包装器AES引擎。它看起来像这样:

I have a simple wrapper for the AES engine. It looks like this:

public class AesSha256Encryptor
{
    private readonly Encryptor<AesEngine, Sha256Digest> provider;

    /// <summary>
    /// Initializes a new instance of the <see cref="AesSha256Encryptor"/> class.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <param name="hmacKey">The HMAC key.</param>
    public AesSha256Encryptor(byte[] key, byte[] hmacKey)
    {
        provider = new Encryptor<AesEngine, Sha256Digest>(Encoding.UTF8, key, hmacKey);
    }

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

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



我希望能有一个不同的盐数据库行,所以我有一个关键管理器,工作原理如下:

I wanted to be able to have a different salt per database row, so I have a key manager that works like this:

public static class EncryptionKeyManager
{
    /// <summary>
    /// The salt length limit
    /// </summary>
    private const int SaltLengthLimit = 32;

    /// <summary>
    /// Gets the key record.
    /// </summary>
    /// <returns></returns>
    public static KeyRecord GetKeyRecord()
    {
        // get the shared passphrasefrom appsettings
        var sharedPassphrase = GetSharedPassphrase();

        // get the client passphrase from config db to sign
        var clientPassphrase = GetClientPassphrase();

        // generate secure random salt
        var salt = GetSalt();

        // get both the encryption key and hmac key
        // these will be used for Encrypt-then-Mac
        var key = GetKeyFromPassphrase(sharedPassphrase, salt);
        var hmacKey = GetKeyFromPassphrase(clientPassphrase, salt);

        return new KeyRecord
        {
            SharedKey = key,
            HmacKey = hmacKey,
            Salt = salt
        };
    }

    /// <summary>
    /// Gets the client salt.
    /// </summary>
    /// <returns></returns>
    private static string GetClientPassphrase()
    {
        var settingsService = ServiceLocator.Current.GetInstance<ISettingService>();
        return settingsService.GetSetting(ConstantConfigSettings.EncryptionSettings.ClientPassphrase, defaultValue: "<removed>");
    }

    /// <summary>
    /// Gets the shared passphrase.
    /// </summary>
    /// <returns></returns>
    private static string GetSharedPassphrase()
    {
        return ConfigurationManager.AppSettings[ConstantConfigSettings.EncryptionSettings.SharedPassphrase] ?? "<removed>";
    }

    /// <summary>
    /// Gets the key from passphrase.
    /// </summary>
    /// <param name="passphrase">The passphrase.</param>
    /// <param name="salt">The salt.</param>
    /// <returns></returns>
    private 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)
    }

    /// <summary>
    /// Gets the salt from a secure random generator..
    /// </summary>
    /// <param name="maximumSaltLength">Maximum length of the salt.</param>
    /// <returns></returns>
    private static string GetSalt(int maximumSaltLength = SaltLengthLimit)
    {
        var salt = new byte[maximumSaltLength];
        using (var random = new RNGCryptoServiceProvider())
        {
            random.GetNonZeroBytes(salt);
        }

        return Convert.ToBase64String(salt);
    }
}

p>

It all gets used like this to encrypt:

// 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;

当我要解密时,我执行以下操作:

When I want to decrypt, I do the following:

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);

这是一个SAAS应用程序。我的基本想法是有一个密码短语是SAAS应用程序的核心,用于加密/解密,但也有一个特定的客户端密码短语用于MAC。其原因是在端点之间传播密钥(一个在数据库中,一个在配置设置中)。盐被保存到数据库,以便它可以用于使用相同的盐进行解密。

This is for a SAAS application. My basic idea was to have a passphrase that is core to the SAAS application that is used to encrypt/decrypt, but also have a specific client passphrase that is used to MAC. The reason for this was to spread the keys between endpoints (one in a database and one in a config setting). The salt gets saved to the database so that it can be used to decrypt using the same salt.

任何人都可以看到我做错了什么?为什么我得到垫块错误?

Can anyone see what I am doing wrong? Why am I getting the pad block error?

FYI:密码短语是 XKCD品种马电池订书机正确风格,所以他们有连字符。我不知道,如果这是一个红鲱鱼虽然。

FYI: The passphrases are of the XKCD variety "horse-battery-stapler-correct" style, so they have hyphens in. I'm not sure if that is a red herring though.

我也不知道每行是否需要独特的盐,还是我只能硬编码盐?是多余的吗?

I'm also not sure if the unique salt per row is required, or whether I could just hard code the salt? Is that overkill?

更新
对于发现此问题的任何人,错误仅仅是我认为使用的密码是不正确的。填充错误是结果。

Update For anyone who finds this, the error was simply that the passphrase that I thought was being used was incorrect. The padding error was the result.

推荐答案

不清楚什么代码确切地导致你的问题(我的意思是没有最小的例子我可以只是运行,看看什么是错误),但我建立了一个示例,根据您的代码正确解密,没有错误,所以你可以看看它,可能会发现你的错误。
我创建 EncryptionKeyManager.GetSharedPassphrase() public,并返回固定字符串 horse-battery-stapler-correct 。我使 EncryptionKeyManager.GetClientPassphrase()也公开,并返回固定的 horse-battery

It is not clear what code exactly causes your problem (I mean there is no minimal example I could just run and see what is wrong), but I built an example which decrypts correctly without errors, based on your code, so you can look at it and probably spot your error. I made EncryptionKeyManager.GetSharedPassphrase() public, and it returns fixed string horse-battery-stapler-correct. I made EncryptionKeyManager.GetClientPassphrase() also public and it returns fixed horse-battery.

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;
    }
}

这篇关于c#BouncyCastle异常:pad块损坏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆