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 GetPrivateKey() => Convert.FromBase64String(_privateKeyString.Split("-", StringSplitOptions.RemoveEmptyEntries)[1]); private ReadOnlySpan 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(); } } } }