using System; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.RegularExpressions; using QRBee.Core.Security; namespace QRBee.Api.Services { internal class SecurityService : SecurityServiceBase { private const int CertificateValidityPeriodDays = 365; private const string CertHeader = "-----BEGIN CERTIFICATE-----"; private const string CertFooter = "-----END CERTIFICATE-----"; public SecurityService(IPrivateKeyHandler privateKeyHandler) : base(privateKeyHandler) { } /// public override X509Certificate2 CreateCertificate(string subjectName, byte[] rsaPublicKey) { using var rsa = new RSACryptoServiceProvider(); rsa.ImportRSAPublicKey(rsaPublicKey, out _); var distinguishedName = new X500DistinguishedName($"CN={subjectName}"); var req = CreateClientCertRequest(distinguishedName, rsa); var pk = PrivateKeyHandler.LoadPrivateKey(); var clientCert = req.Create(pk, DateTimeOffset.UtcNow - TimeSpan.FromDays(1), DateTimeOffset.UtcNow + TimeSpan.FromDays(CertificateValidityPeriodDays), Guid.NewGuid() .ToByteArray()); return clientCert; } /// /// Generate Client certificate request (i.e. without KeyCertSign usage extension) /// /// /// /// private static CertificateRequest CreateClientCertRequest(X500DistinguishedName distinguishedName, RSA rsa) { var request = new CertificateRequest( distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1 ); request.CertificateExtensions.Add( new X509KeyUsageExtension( X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false)); return request; } /// public override X509Certificate2 Deserialize(string pemData) { var start = pemData.IndexOf(CertHeader); if (start == -1) throw new ApplicationException("Invalid certificate format"); start = start + CertHeader.Length; var end = pemData.IndexOf(CertFooter); if (end == -1) throw new ApplicationException("Invalid certificate format"); var base64 = pemData.Substring(start, end - start); // contains new lines, but it does not matter var data = Convert.FromBase64String(base64); return new X509Certificate2(data); } /// public override string Serialize(X509Certificate2 cert) { // https://stackoverflow.com/questions/43928064/export-private-public-keys-from-x509-certificate-to-pem var builder = new StringBuilder(); builder.AppendLine(CertHeader); builder.AppendLine(Convert.ToBase64String(cert.RawData, Base64FormattingOptions.InsertLineBreaks)); builder.AppendLine(CertFooter); return builder.ToString(); } } }