257 lines
8.7 KiB
C#
257 lines
8.7 KiB
C#
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|||
|
|
using Org.BouncyCastle.Crypto;
|
|||
|
|
using Org.BouncyCastle.Crypto.Prng;
|
|||
|
|
using Org.BouncyCastle.Security;
|
|||
|
|
using System;
|
|||
|
|
using System.Security.Cryptography;
|
|||
|
|
using System.Text;
|
|||
|
|
|
|||
|
|
namespace Laservall.Solidworks.Common
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 国密4加密
|
|||
|
|
/// 专供plm对接
|
|||
|
|
/// </summary>
|
|||
|
|
public class SM4Helper
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 定制的随机数生成器
|
|||
|
|
/// plm对接用
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="seed"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static byte[] CustomGenerateBytes(byte[] seed)
|
|||
|
|
{
|
|||
|
|
return CustomGenerateBytes(seed, 128);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 定制的随机数生成器
|
|||
|
|
/// plm对接用
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="seed"></param>
|
|||
|
|
/// <param name="keySize"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static byte[] CustomGenerateBytes(byte[] seed, int keySize)
|
|||
|
|
{
|
|||
|
|
var kg = GeneratorUtilities.GetKeyGenerator("SM4");
|
|||
|
|
|
|||
|
|
var rng = new CustomSHA1PRNG();
|
|||
|
|
rng.AddSeedMaterial(seed);
|
|||
|
|
var sr = new SecureRandom(rng);
|
|||
|
|
kg.Init(new KeyGenerationParameters(sr, keySize));
|
|||
|
|
return kg.GenerateKey();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 16进制字符串转bytes
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="hexString"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static byte[] HexConvert(string hexString)
|
|||
|
|
{
|
|||
|
|
byte[] byteArray = new byte[hexString.Length / 2];
|
|||
|
|
for (int i = 0; i < hexString.Length; i += 2)
|
|||
|
|
{
|
|||
|
|
byteArray[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return byteArray;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// bytes转16进制字符串
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="bytes"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static string HexConvert(byte[] bytes)
|
|||
|
|
{
|
|||
|
|
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 签名
|
|||
|
|
/// https://flowus.cn/dnge/share/7a475b74-bbdc-4686-b6bd-36ce3ee47f02
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="sk"></param>
|
|||
|
|
/// <param name="ak"></param>
|
|||
|
|
/// <param name="ts"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static string Sign(string sk, string ak, string ts)
|
|||
|
|
{
|
|||
|
|
var md5 = $"{ak}{ts}{{}}";
|
|||
|
|
|
|||
|
|
md5 = HexConvert(new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(md5)));
|
|||
|
|
var lastMD5 = HexConvert(new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(ts)))
|
|||
|
|
.Substring(16);
|
|||
|
|
|
|||
|
|
byte[] plaintext = Encoding.UTF8.GetBytes(md5);
|
|||
|
|
byte[] keyBytes = CustomGenerateBytes(Encoding.UTF8.GetBytes(sk));
|
|||
|
|
byte[] iv = CustomGenerateBytes(Encoding.UTF8.GetBytes(lastMD5));
|
|||
|
|
|
|||
|
|
KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
|
|||
|
|
ParametersWithIV keyParamWithIv = new ParametersWithIV(key, iv);
|
|||
|
|
|
|||
|
|
IBufferedCipher inCipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
|
|||
|
|
|
|||
|
|
inCipher.Init(true, keyParamWithIv);
|
|||
|
|
|
|||
|
|
return HexConvert(inCipher.DoFinal(plaintext));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 验签
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="sk"></param>
|
|||
|
|
/// <param name="ak"></param>
|
|||
|
|
/// <param name="ts"></param>
|
|||
|
|
/// <param name="target"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static bool CheckSign(string sk, string ak, string ts, string target)
|
|||
|
|
{
|
|||
|
|
var md5 = $"{ak}{ts}{{}}";
|
|||
|
|
|
|||
|
|
md5 = HexConvert(new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(md5)));
|
|||
|
|
var lastMD5 = HexConvert(new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(ts)))
|
|||
|
|
.Substring(16);
|
|||
|
|
|
|||
|
|
//byte[] plaintext = Encoding.UTF8.GetBytes(md5);
|
|||
|
|
byte[] keyBytes = CustomGenerateBytes(Encoding.UTF8.GetBytes(sk));
|
|||
|
|
byte[] iv = CustomGenerateBytes(Encoding.UTF8.GetBytes(lastMD5));
|
|||
|
|
|
|||
|
|
KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
|
|||
|
|
ParametersWithIV keyParamWithIv = new ParametersWithIV(key, iv);
|
|||
|
|
|
|||
|
|
IBufferedCipher inCipher = CipherUtilities.GetCipher("SM4/CBC/PKCS5Padding");
|
|||
|
|
|
|||
|
|
inCipher.Init(false, keyParamWithIv);
|
|||
|
|
|
|||
|
|
return md5 == Encoding.UTF8.GetString(inCipher.DoFinal(HexConvert(target)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 定制的sha1随机数生成器
|
|||
|
|
/// plm那边用seed控制随机数来稳定加密,很奇怪
|
|||
|
|
/// java和dotnet随机数实现不一样,加解密不通,所以得搬一个
|
|||
|
|
/// 找到一个其他人的实现,修修补补用起来了
|
|||
|
|
/// https://stackoverflow.com/questions/70587126/how-to-get-the-same-result-in-c-sharp-with-securerandom-getinstancesha1prng
|
|||
|
|
/// </summary>
|
|||
|
|
private class CustomSHA1PRNG : IRandomGenerator
|
|||
|
|
{
|
|||
|
|
private const int DIGEST_SIZE = 20;
|
|||
|
|
|
|||
|
|
public CustomSHA1PRNG()
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void updateState(byte[] state, byte[] output)
|
|||
|
|
{
|
|||
|
|
int last = 1;
|
|||
|
|
int v;
|
|||
|
|
byte t;
|
|||
|
|
bool zf = false;
|
|||
|
|
|
|||
|
|
// state(n + 1) = (state(n) + output(n) + 1) % 2^160;
|
|||
|
|
for (int i = 0; i < state.Length; i++)
|
|||
|
|
{
|
|||
|
|
// Add two bytes
|
|||
|
|
v = (int)(sbyte)state[i] + (int)(sbyte)output[i] + last;
|
|||
|
|
// Result is lower 8 bits
|
|||
|
|
t = (byte)(sbyte)v;
|
|||
|
|
// Store result. Check for state collision.
|
|||
|
|
zf = zf | (state[i] != t);
|
|||
|
|
state[i] = t;
|
|||
|
|
// High 8 bits are carry. Store for next iteration.
|
|||
|
|
last = v >> 8;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Make sure at least one bit changes!
|
|||
|
|
if (!zf)
|
|||
|
|
{
|
|||
|
|
state[0] = (byte)(sbyte)(state[0] + 1);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void GetBytes(byte[] seed, byte[] result)
|
|||
|
|
{
|
|||
|
|
byte[] state;
|
|||
|
|
byte[] remainder = null;
|
|||
|
|
int remCount;
|
|||
|
|
int index = 0;
|
|||
|
|
int todo;
|
|||
|
|
byte[] output = remainder;
|
|||
|
|
|
|||
|
|
using (var sha1 = new SHA1CryptoServiceProvider())
|
|||
|
|
{
|
|||
|
|
state = sha1.ComputeHash(seed);
|
|||
|
|
remCount = 0;
|
|||
|
|
|
|||
|
|
// Use remainder from last time
|
|||
|
|
int r = remCount;
|
|||
|
|
if (r > 0)
|
|||
|
|
{
|
|||
|
|
// How many bytes?
|
|||
|
|
todo = (result.Length - index) < (DIGEST_SIZE - r)
|
|||
|
|
? (result.Length - index)
|
|||
|
|
: (DIGEST_SIZE - r);
|
|||
|
|
// Copy the bytes, zero the buffer
|
|||
|
|
for (int i = 0; i < todo; i++)
|
|||
|
|
{
|
|||
|
|
result[i] = output[r];
|
|||
|
|
output[r++] = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
remCount += todo;
|
|||
|
|
index += todo;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// If we need more bytes, make them.
|
|||
|
|
while (index < result.Length)
|
|||
|
|
{
|
|||
|
|
// Step the state
|
|||
|
|
output = sha1.ComputeHash(state);
|
|||
|
|
updateState(state, output);
|
|||
|
|
|
|||
|
|
// How many bytes?
|
|||
|
|
todo = (result.Length - index) > DIGEST_SIZE ? DIGEST_SIZE : result.Length - index;
|
|||
|
|
// Copy the bytes, zero the buffer
|
|||
|
|
for (int i = 0; i < todo; i++)
|
|||
|
|
{
|
|||
|
|
result[index++] = output[i];
|
|||
|
|
output[i] = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
remCount += todo;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Store remainder for next time
|
|||
|
|
//remainder = output;
|
|||
|
|
//remCount %= DIGEST_SIZE;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static byte[] Seed { get; set; }
|
|||
|
|
|
|||
|
|
public void AddSeedMaterial(byte[] seed)
|
|||
|
|
{
|
|||
|
|
Seed = seed;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void AddSeedMaterial(long seed)
|
|||
|
|
{
|
|||
|
|
throw new NotImplementedException();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void NextBytes(byte[] bytes)
|
|||
|
|
{
|
|||
|
|
GetBytes(Seed, bytes);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void NextBytes(byte[] bytes, int start, int len)
|
|||
|
|
{
|
|||
|
|
throw new NotImplementedException();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|