using System.Globalization; using System.Text; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Crypto.Parameters; namespace YY.Admin.Core.Util; /// /// 与 JeecgBoot PasswordUtil.encrypt(username, plainPassword, salt) 对齐: /// 算法 PBEWithMD5AndDES,迭代 1000 次;明文为 用户名 UTF-8,口令为明文密码;盐为字符串转字节(与 Java 一致建议使用 UTF-8)。 /// public static class JeecgPasswordUtil { /// 与 Java PasswordUtil 中 ITERATIONCOUNT 一致 public const int IterationCount = 1000; /// /// 生成与 Jeecg 数据库 sys_user.password 一致的十六进制密文(小写)。 /// /// 登录账号(Jeecg 的 username) /// 明文密码 /// Jeecg 用户表 salt 字段字符串 /// 盐字节编码;Jeecg 服务端 JVM 多为 UTF-8,建议固定 UTF-8 public static string Encrypt(string username, string plainPassword, string salt, Encoding? saltEncoding = null) { saltEncoding ??= Encoding.UTF8; byte[] saltBytes = saltEncoding.GetBytes(salt); byte[] plainBytes = Encoding.UTF8.GetBytes(username); // PKCS#5 Scheme1(MD5)+ DES/CBC/PKCS7,与 Java JCE PBEWithMD5AndDES 一致 var generator = new Pkcs5S1ParametersGenerator(new MD5Digest()); generator.Init(PbeParametersGenerator.Pkcs5PasswordToBytes(plainPassword.ToCharArray()), saltBytes, IterationCount); // 派生 DES 密钥 + IV(各 64 bit) ParametersWithIV keyIv = (ParametersWithIV)generator.GenerateDerivedParameters("DES", 64, 64); var cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new DesEngine()), new Pkcs7Padding()); cipher.Init(true, keyIv); byte[] output = new byte[cipher.GetOutputSize(plainBytes.Length)]; int len = cipher.ProcessBytes(plainBytes, 0, plainBytes.Length, output, 0); len += cipher.DoFinal(output, len); if (len < output.Length) Array.Resize(ref output, len); return BytesToHexLower(output); } /// /// 校验明文密码是否与 Jeecg 存储的十六进制密文一致。 /// public static bool Verify(string username, string plainPassword, string salt, string storedPasswordHex, Encoding? saltEncoding = null) { if (string.IsNullOrEmpty(storedPasswordHex)) return false; string computed = Encrypt(username, plainPassword, salt, saltEncoding); return string.Equals(computed, storedPasswordHex, StringComparison.OrdinalIgnoreCase); } private static string BytesToHexLower(byte[] src) { if (src == null || src.Length == 0) return string.Empty; var sb = new StringBuilder(src.Length * 2); foreach (byte b in src) sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); return sb.ToString(); } }