using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
namespace QRBee.Core.Security
{
public abstract class SecurityServiceBase : ISecurityService
{
private IPrivateKeyHandler _privateKeyHandler;
protected SecurityServiceBase(IPrivateKeyHandler privateKeyHandler)
{
_privateKeyHandler = privateKeyHandler;
}
///
public IPrivateKeyHandler PrivateKeyHandler => _privateKeyHandler;
///
public abstract X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey);
///
public abstract X509Certificate2 Deserialize(string pemData);
///
public abstract string Serialize(X509Certificate2 cert);
///
/// Subject may only contain letters, numbers, -, . and spaces.
/// Subject should not be an empty string.
/// International letters are supported, but not tested.
///
///
/// True for valid subject name
protected static bool IsValidSubjectName(string subjectName)
{
if (string.IsNullOrWhiteSpace(subjectName))
return false;
return Regex.IsMatch(@"[\w\s[0-9]\-\.]+", subjectName);
}
///
public byte[] Decrypt(byte[] data)
{
using var rsa = LoadPrivateKey();
var res = rsa?.Decrypt(data, RSAEncryptionPadding.Pkcs1) ?? throw new CryptographicException("No private key found");
return res;
}
public abstract X509Certificate2 APIServerCertificate { get; set; }
///
public byte [] Encrypt(byte[] data, X509Certificate2 destCert)
{
using var rsa = destCert.GetRSAPublicKey();
var res = rsa?.Encrypt(data, RSAEncryptionPadding.Pkcs1) ?? throw new CryptographicException("No destination public key found");
return res;
}
///
public bool IsValid(X509Certificate2 destCert)
{
// check if certificate issued by this service
return true;
}
///
public byte[] Sign(byte[] data)
{
using var rsa = LoadPrivateKey();
var res = rsa?.SignData(data, 0, data.Length, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) ?? throw new CryptographicException("No private key found");
return res;
}
///
public bool Verify(byte[] data, byte[] signature, X509Certificate2 signedBy)
{
if ( !IsValid(signedBy))
throw new CryptographicException("Signer's certificate is not valid");
using var rsa = signedBy.GetRSAPublicKey();
var res = rsa?.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1) ?? throw new CryptographicException("No signer public key found");
return res;
}
///
public string GetSerialNumber(X509Certificate2 cert)
{
var serNo = cert.SerialNumber;
return serNo;
}
private RSA LoadPrivateKey()
{
if (!PrivateKeyHandler.Exists())
PrivateKeyHandler.GeneratePrivateKey(); //TODO: subject name
var pk = PrivateKeyHandler.LoadPrivateKey();
return pk;
}
}
}