Kit.Core/LibCommon/Kit.Sign/Services/SignService.cs

118 lines
4.6 KiB
C#

using System.Reflection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Kit.Sign
{
[Obfuscation(Exclude = true, ApplyToMembers = true, StripAfterObfuscation = true)]
public interface ISignService
{
void Sign(Stream P_0, Stream P_1);
void Verify(Stream P_0, Stream P_1, out bool P_2);
}
[Obfuscation(Exclude = true, ApplyToMembers = false, StripAfterObfuscation = true)]
public class SignService : ISignService
{
private SignServicePrivate _service;
public SignService(string? privateKeyString, string? publicKeyString, string? password)
{
_service = new SignServicePrivate(privateKeyString, publicKeyString, password);
}
public void Sign(Stream dataStream, Stream signatureStream) => _service.Sign(dataStream, signatureStream);
public void Verify(Stream dataStream, Stream signatureStream, out bool isValid) => isValid = _service.Verify(dataStream, signatureStream);
}
internal class SignServicePrivate
{
private string? _privateKeyString;
private string? _publicKeyString;
private string? _password;
private readonly HashAlgorithmName _hashAlgorithmName;
private readonly RSASignaturePadding _signaturePadding;
public SignServicePrivate(string? privateKeyString, string? publicKeyString, string? password)
{
_privateKeyString = privateKeyString;
_publicKeyString = publicKeyString;
_password = password;
_hashAlgorithmName = HashAlgorithmName.SHA256;
_signaturePadding = RSASignaturePadding.Pkcs1;
}
private ReadOnlySpan<byte> GetPrivateKey() => Convert.FromBase64String(_privateKeyString.Split("-", StringSplitOptions.RemoveEmptyEntries)[1]);
private ReadOnlySpan<byte> GetPublicKey() => Encoding.UTF8.GetBytes(_publicKeyString);
private RSA ImportPrivate(RSA rsa)
{
if (_privateKeyString == null || _privateKeyString.Length == 0) throw new InvalidOperationException($"{nameof(ImportPrivate)} is unavailable");
if (string.IsNullOrWhiteSpace(_password) == false)
rsa.ImportEncryptedPkcs8PrivateKey(_password, GetPrivateKey(), out _);
else
rsa.ImportPkcs8PrivateKey(GetPrivateKey(), out _);
return rsa;
}
public void Sign(Stream dataStream, Stream signatureStream)
{
if (_privateKeyString == null || _privateKeyString.Length == 0) throw new InvalidOperationException("Sign is unavailable");
using (RSA rsa = ImportPrivate(RSA.Create()))
{
WriteSignatureBytes(signatureStream, rsa.SignData(ReadAllBytes(dataStream), _hashAlgorithmName, _signaturePadding));
}
}
public bool Verify(Stream dataStream, Stream signatureStream)
{
if (_publicKeyString == null || _publicKeyString.Length == 0) throw new InvalidOperationException("Verify is unavailable");
using var publicX509 = new X509Certificate2(GetPublicKey());
using (var rsa = publicX509.GetRSAPublicKey()!)
{
return rsa.VerifyData(ReadAllBytes(dataStream), ReadSignatureBytes(signatureStream), _hashAlgorithmName, _signaturePadding);
}
}
private void WriteSignatureBytes(Stream signatureStream, byte[] signatureBytes)
{
byte[] base64Signature = Encoding.UTF8.GetBytes(Convert.ToBase64String(signatureBytes));
signatureStream.Write(base64Signature, 0, base64Signature.Length);
}
private byte[] ReadSignatureBytes(Stream signatureStream)
{
long position = 0;
if (signatureStream.CanSeek)
{
position = signatureStream.Position;
}
string base64Signature = string.Empty;
using (TextReader textReader = new StreamReader(signatureStream, leaveOpen: true))
{
base64Signature = textReader.ReadToEnd();
}
if (signatureStream.CanSeek)
{
signatureStream.Position = position;
}
return Convert.FromBase64String(base64Signature);
}
private static byte[] ReadAllBytes(Stream instream)
{
if (instream is MemoryStream)
return ((MemoryStream)instream).ToArray();
using (var memoryStream = new MemoryStream())
{
instream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
}
}