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