75 lines
3.2 KiB
C#
75 lines
3.2 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
/// 与 JeecgBoot <c>PasswordUtil.encrypt(username, plainPassword, salt)</c> 对齐:
|
||
/// 算法 <c>PBEWithMD5AndDES</c>,迭代 1000 次;明文为 <b>用户名 UTF-8</b>,口令为明文密码;盐为字符串转字节(与 Java 一致建议使用 UTF-8)。
|
||
/// </summary>
|
||
public static class JeecgPasswordUtil
|
||
{
|
||
/// <summary>与 Java PasswordUtil 中 ITERATIONCOUNT 一致</summary>
|
||
public const int IterationCount = 1000;
|
||
|
||
/// <summary>
|
||
/// 生成与 Jeecg 数据库 <c>sys_user.password</c> 一致的十六进制密文(小写)。
|
||
/// </summary>
|
||
/// <param name="username">登录账号(Jeecg 的 username)</param>
|
||
/// <param name="plainPassword">明文密码</param>
|
||
/// <param name="salt">Jeecg 用户表 salt 字段字符串</param>
|
||
/// <param name="saltEncoding">盐字节编码;Jeecg 服务端 JVM 多为 UTF-8,建议固定 UTF-8</param>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 校验明文密码是否与 Jeecg 存储的十六进制密文一致。
|
||
/// </summary>
|
||
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();
|
||
}
|
||
}
|