SQLite密码修改故障排查:RSA加密随机性导致的数据库匹配问题
老项目,用户在使用时修改密码不成功。通过查看是因为密码修改时,根本匹配不到用户名;由于用户名通过RSA加密后每次输出都不一样,与原始数据库中存储的用户不匹配。解决办法为向用户表中添加字段UserNameHash(用户名hash值),更新匹配用户名时使用UserNameHash进行匹配。
问题现象
某老项目,在使用时被用户发现不能正常修改密码。反馈到软件部后,愚被分配到去修改此bug。
开发环境
VS2022
SqLite3
排查过程
不能修改密码?download源码后,试了下,确实有这个问题。但为什么呢?在UI上明明显示已经修改成功了啊;但是为什么用新密码登陆却显示密码错误,必须用原来的密码才能登陆成功。
猜想:应该是密码未成功修改进数据库中,UI上仅仅是显示的VM绑定的数据而已。
看了下更新进sqlite数据库的代码,如下所示:
/// <summary> /// 更新用户密码 /// </summary> /// <param name="userName"></param> /// <param name="newPassword"></param> public void UpdatePassword(string userName, string newPassword) { string encryptionAccount = RSA.EncryptByRSA(userName); string encryptionPassword = RSA.EncryptByRSA(newPassword); string cmd = "UPDATE UserInfoTable SET Password = '" + encryptionPassword + "' WHERE UserName = '" + encryptionAccount + "'"; object lockThis = new object(); lock (lockThis) { using (SQLiteConnection conn = new SQLiteConnection("Data Source=" + PathCfg.Database + ";Version=3;")) { conn.Open(); using (SQLiteCommand command = new SQLiteCommand(cmd, conn)) { try { command.ExecuteNonQuery(); command.Dispose(); conn.Close(); } catch (Exception) { } } } } }
在上述代码的 command.ExecuteNonQuery(); 将其更改下述代码后,测试了下,影响的行数为0;也就说根本就没有匹配到任何用户,当然也就更改不成功了。
var rows= command.ExecuteNonQuery();
仔细看了更新代码,同时确认用户名输入没有问题,那么问题只可能是最终的用户名匹配不到,而之所以匹配不到,反推回来,只有中间使用 RSA.EncryptByRSA(userName) 加密,在加密后导致密文与最初的密文不匹配造成的。
经验证,确实如前述所说:加密后的密文与原始数据库中的用户名密文不一样。
也就是说加密模块可能是有问题。
加密RSA类EncryptByRSA加密方法源码如下:
/// <summary> /// RSA加密 /// </summary> /// <param name="plaintext">明文字符串</param> /// <returns>密文字符串</returns> public static string EncryptByRSA(string plaintext) { UnicodeEncoding ByteConverter = new UnicodeEncoding(); byte[] dataToEncrypt = ByteConverter.GetBytes(plaintext); using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(publicKey); byte[] encryptedData = rsa.Encrypt(dataToEncrypt, true); return Convert.ToBase64String(encryptedData); } }
解密方法源码如下:
/// <summary> /// RSA解密 /// </summary> /// <param name="ciphertext">加密字符串</param> /// <returns>明文字符串</returns> public static string DecryptByRSA(string ciphertext) { UnicodeEncoding byteConverter = new UnicodeEncoding(); using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(privateKey); byte[] encryptedData = Convert.FromBase64String(ciphertext); byte[] decryptedData = rsa.Decrypt(encryptedData, true); return byteConverter.GetString(decryptedData); } }
通过在加密模块中添加测试代码后发现,用户名每次加密后的密文都不一样,但还是能解密为输入的用户名。自然每次更新用户名密码时肯定不会成功了。
以下为测试用户名加密解密时使用的代码:
public static string EncryptByRSA(string plaintext) { UnicodeEncoding ByteConverter = new UnicodeEncoding(); byte[] dataToEncrypt = ByteConverter.GetBytes(plaintext); using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(publicKey); byte[] encryptedData = rsa.Encrypt(dataToEncrypt, true); var test = Convert.ToBase64String(encryptedData); var deResult = DecryptByRSA(test); return Convert.ToBase64String(encryptedData); } }
原因分析
为什么相同输入会产生不同密文?
1. PKCS#1 OAEP填充模式的作用
代码中rsa.Encrypt(dataToEncrypt, true)的第二个参数true表示:
- 使用 OAEP (Optimal Asymmetric Encryption Padding) 填充模式
- 该模式会自动添加随机盐值(salt)到明文数据前,导致每次加密结果不同
2. 安全设计原理
| 特性 | 说明 |
|---|---|
| 防止重放攻击 | 相同明文不同密文,使攻击者无法通过重复发送密文破解 |
| 隐藏明文模式 | 即使明文有规律,密文也呈现随机性 |
| 语义安全性 | 加密结果不泄露明文的任何信息(包括是否相同) |
详细可参考RSA 类 (System.Security.Cryptography) | Microsoft Learn

浙公网安备 33010602011771号