public IActionResult RevokeCertificate(RevokeRequest revokeRequest) { User user = _userDBAuthenticator.GetUser(revokeRequest.Uid); if (user != null) { _caDBModifier.RevokeAllCertificatesOfUser(user); return(NoContent()); } else { _logger.LogWarning( string.Format( "Failed to revoke certificates of user {0}; No such user found.", revokeRequest.Uid ) ); return(BadRequest()); } }
public IActionResult IssueCertificate(CertRequest certRequest) { if (certRequest.CertPassphrase.Length < Constants.MinPassphraseLength) { _logger.LogWarning( string.Format( "User {} tried to issue new certificate with too short passphrase.", certRequest.Uid ) ); return(BadRequest("Too short passphrase")); } CipherSuite cipherSuite = certRequest.RequestedCipherSuite; if (!cipherSuite.IsValidCipherSuite()) { _logger.LogWarning("Invalid cipher suite:\n" + cipherSuite); return(BadRequest("Invalid cipher suite.")); } User user = _userDBAuthenticator.AuthenticateAndGetUser(certRequest.Uid, certRequest.Password); if (user != null) { // Load the certificate X509Certificate2 coreCACert = new X509Certificate2(CAConfig.CoreCACertPath); HashAlgorithmName hashAlg = new HashAlgorithmName(cipherSuite.HashAlg); AsymmetricAlgorithm privKey = null; string privKeyExport = null; CertificateRequest req = null; if (cipherSuite.Alg.Equals(EncryptionAlgorithms.RSA)) { privKey = RSA.Create(cipherSuite.KeySize); privKeyExport = ((RSA)privKey).ToPem(); req = new CertificateRequest( "CN=" + user.Id, (RSA)privKey, hashAlg, RSASignaturePadding.Pss ); } else if (cipherSuite.Alg.Equals(EncryptionAlgorithms.ECDSA)) { privKey = ECDsa.Create(); privKeyExport = ((ECDsa)privKey).ToPem(); req = new CertificateRequest( "CN=" + user.Id, (ECDsa)privKey, hashAlg ); } else { _logger.LogError( string.Format( "Unknown encryption algorithm '{0}'.", cipherSuite.Alg ) ); throw new CryptographicUnexpectedOperationException(); } // Add email as SAN SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder(); sanBuilder.AddEmailAddress(user.Email); req.CertificateExtensions.Add(sanBuilder.Build()); // Arguments: Is no CA, no restricted nr of path levels, (nr of path levels), is not critical req.CertificateExtensions.Add( new X509BasicConstraintsExtension(false, false, 0, false) ); req.CertificateExtensions.Add( new X509SubjectKeyIdentifierExtension(req.PublicKey, false) ); req.CertificateExtensions.Add( new X509KeyUsageExtension( X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation, false ) ); OidCollection oidCollection = new OidCollection(); // Set Client Authentication Oid oidCollection.Add(new Oid("1.3.6.1.5.5.7.3.2")); // Set Secure Email / Email protection Oid oidCollection.Add(new Oid("1.3.6.1.5.5.7.3.4")); req.CertificateExtensions.Add( new X509EnhancedKeyUsageExtension( oidCollection, false ) ); // Add CRL Distribution Point (CDP) req.CertificateExtensions.Add( new System.Security.Cryptography.X509Certificates.X509Extension( new Oid(X509Extensions.CrlDistributionPoints.Id), new CrlDistPoint( new[] { new DistributionPoint( new DistributionPointName( new GeneralNames( new GeneralName( GeneralName.UniformResourceIdentifier, CAConfig.CrlDistributionPoint ) ) ), null, null ) } ).GetDerEncoded(), false ) ); X509Certificate2 coreCaPublicCert = new X509Certificate2(coreCACert.Export(X509ContentType.Cert)); if (coreCaPublicCert.HasPrivateKey) { _logger.LogError("Core CA public certificate exported with private key!"); throw new CryptographicUnexpectedOperationException(); } // Use transaction to prevent race conditions on the serial number X509Certificate2 userCert; using ( IDbContextTransaction scope = _caDBModifier.GetScope() ) { SerialNumber serialNr = _caDBModifier.GetMaxSerialNr(); // It is necessary to use this constructor to be able to sign keys // that use different algorithms than the one for the core CA's key userCert = req.Create( coreCACert.SubjectName, X509SignatureGenerator.CreateForRSA( (RSA)coreCACert.PrivateKey, RSASignaturePadding.Pss ), DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddDays(CAConfig.UserCertValidityPeriod), serialNr.SerialNrBytes ); _caDBModifier.RevokeAllCertificatesOfUser(user); // Add certificate to DB _caDBModifier.AddCertificate( new PublicCertificate { SerialNr = serialNr.SerialNr, Uid = user.Id, Certificate = Convert.ToBase64String( userCert.Export(X509ContentType.Cert) ), IsRevoked = false } ); scope.Commit(); } var collection = new X509Certificate2Collection(); X509Certificate2 userCertWithPrivKey; if (privKey is RSA rsa) { userCertWithPrivKey = userCert.CopyWithPrivateKey(rsa); } else if (privKey is ECDsa dsa) { userCertWithPrivKey = userCert.CopyWithPrivateKey(dsa); } else { throw new CryptographicUnexpectedOperationException(); } collection.Add(userCertWithPrivKey); collection.Add(coreCaPublicCert); BackupPrivateKey(privKeyExport, user.Id + '_' + userCert.Thumbprint + ".pem.enc"); byte[] certBytes = collection.Export(X509ContentType.Pkcs12, certRequest.CertPassphrase); string pkcs12ArchiveB64 = Convert.ToBase64String(certBytes); // Add encrypted private key to DB _caDBModifier.AddPrivateKey( new PrivateKey { Uid = user.Id, KeyPkcs12 = pkcs12ArchiveB64 } ); _logger.LogInformation("Successfully issued new certificate for user " + user.Id); return(Ok( new UserCertificate { Pkcs12Archive = pkcs12ArchiveB64 } )); } else { _logger.LogWarning( "Unauthorized attempt to issue certificate for user " + certRequest.Uid ); return(Unauthorized()); } }