118 lines
4.6 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
}
|