为什么密码错误会导致"填充无效且无法删除"?

时间:2020-03-05 18:39:46  来源:igfitidea点击:

我需要一些简单的字符串加密,因此我编写了以下代码(此处有很多"启发"):

// create and initialize a crypto algorithm
    private static SymmetricAlgorithm getAlgorithm(string password) {
        SymmetricAlgorithm algorithm = Rijndael.Create();
        Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
            password, new byte[] {
            0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,             // salty goodness
            0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65
        }
        );
        algorithm.Padding = PaddingMode.ISO10126;
        algorithm.Key = rdb.GetBytes(32);
        algorithm.IV = rdb.GetBytes(16);
        return algorithm;
    }

    /* 
     * encryptString
     * provides simple encryption of a string, with a given password
     */
    public static string encryptString(string clearText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(clearBytes, 0, clearBytes.Length);
        cs.Close();
        return Convert.ToBase64String(ms.ToArray());
    }

    /*
     * decryptString
     * provides simple decryption of a string, with a given password
     */
    public static string decryptString(string cipherText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write);
        cs.Write(cipherBytes, 0, cipherBytes.Length);
        cs.Close();            
        return System.Text.Encoding.Unicode.GetString(ms.ToArray());
    }

该代码看起来可以正常工作,除了使用不正确的密钥解密数据时,我在cryptoString中的c.Close()行上收到CryptographicException" Padding is invalid and无法删除"。

示例代码:

string password1 = "password";
    string password2 = "letmein";
    string startClearText = "The quick brown fox jumps over the lazy dog";
    string cipherText = encryptString(startClearText, password1);
    string endClearText = decryptString(cipherText, password2);     // exception thrown

我的问题是,这是可以预期的吗?我以为用错误的密码解密只会导致无意义的输出,而不是异常。

解决方案

回答

是的,这是可以预料的,或者至少是当我们的加密例程获取不可解密的数据时发生的确切情况

回答

尽管已经回答了这个问题,但我认为最好解释一下为什么会这样。

通常采用填充方案,因为大多数密码过滤器在语义上都不安全,并且无法防止某些形式的密码攻击。例如,通常在RSA中,使用OAEP填充方案来防止某种形式的攻击(例如,选择的明文攻击或者盲目攻击)。

填充方案在发送消息之前将一些(通常)随机垃圾添加到消息m上。例如,在OAEP方法中,使用了两个Oracle(这是一个简单的解释):

  • 给定模数的大小,我们将k1位填充为0,将k0位填充为随机数。
  • 然后,通过对消息进行某种转换,我们将获得加密并发送的填充消息。

这为我们提供了对消息的随机化,并提供了一种测试消息是否为垃圾的方法。由于填充方案是可逆的,因此,当我们解密消息时却无法说出消息本身的完整性,则可以对填充进行断言,从而可以知道消息是否已正确解密或者我们做错了什么(即有人篡改了消息或者我们使用了错误的密钥)

回答

CryptoStream中可能有一些未读的字节。在完全读取流之前关闭会导致程序出错。