Hi Iam using the OpenSSL .NET wrapper in my c# project. i want to generate an X509 certification but i don't really know the procedure. what should it contain (what parameters)...etc
this is my code, I did it after looking some tests:
OpenSSL.X509.X509Certificate x509 = new OpenSSL.X509.X509Certificate();
OpenSSL.Crypto.RSA rsa = new OpenSSL.Crypto.RSA();
rsa.GenerateKeys(1024, 0x10001, null, null);
OpenSSL.Crypto.CryptoKey key = new OpenSSL.Crypto.CryptoKey(rsa);
OpenSSL.Crypto.MessageDigestContext digest = new OpenSSL.Crypto.MessageDigestContext(
OpenSSL.Crypto.MessageDigest.SHA1);
I suppose that the certificate should take RSA private key and the digest as parameters and i have to configure it (date...and others parameters).
Can any one help me about that ? to finish my code ? thank you.
I use the following routine:
// Initialize the following with your information
var serial = 1234;
var issuer = new X509Name("issuer");
var subject = new X509Name("subject");
// Creates the key pair
var rsa = new RSA();
rsa.GenerateKeys(1024, 0x10001, null, null);
// Creates the certificate
var key = new CryptoKey(rsa);
var cert = new X509Certificate(serial, subject, issuer, key, DateTime.Now, DateTime.Now.AddYears(20));
// Dumps the certificate into a .cer file
var bio = BIO.File("C:/temp/cert.cer", "w");
cert.Write(bio);
Related
I am trying to create a private key and add a certificate(self-signed or signed from a CA) where I should be able to export the certificate only and make the private key non-exportable in C#. That is, if someone tries to export the certificate from certmgr, the export option will be disabled like this picture-
I want the same non-exportable option programmatically in C# while creating it. The private key usually becomes non-exportable when a .pfx/.p12 file is installed using Crypto Shell Extensions when Mark this Key is exportable is unchecked.
I can successfully create key pairs and add certificate entries in the windows key store. But Yes, export the private key option always becomes enabled that is I can't restrict the private key from export. I have tried this -
public void init(){
AsymmetricCipherKeyPair asymmetricCipherKeyPair = GetKeyPair();
X509Name issuer = this.GenerateRelativeDistinguishedName("test org");
X509Name subject = this.GenerateRelativeDistinguishedName("test user1");
Org.BouncyCastle.X509.X509Certificate cert = GenerateCertificate(issuer, subject, asymmetricCipherKeyPair.Private, asymmetricCipherKeyPair.Public);
importSelfSignedCert(asymmetricCipherKeyPair, cert);
}
private AsymmetricCipherKeyPair GetKeyPair()
{
return new Pkcs1xHandler().GenerateKeyPair(Constants.RsaKeyLength.Length2048Bits);
}
protected X509Name GenerateRelativeDistinguishedName(String commonName)
{
IDictionary attributes = new Hashtable();
IList ordering;
attributes.Add(X509Name.CN, commonName);
ordering = new ArrayList(attributes.Keys);
return new X509Name(ordering, attributes);
}
protected void importSelfSignedCert(AsymmetricCipherKeyPair asymmetricCipherKeyPair, Org.BouncyCastle.X509.X509Certificate cert)
{
try
{
int ID =1;
AsymmetricCipherKeyPair ackp = asymmetricCipherKeyPair;
var rsaPriv = Org.BouncyCastle.Security.DotNetUtilities.ToRSA(ackp.Private as RsaPrivateCrtKeyParameters);
// Setup RSACryptoServiceProvider with "KeyContainerName" set to "KeyContainer"+ enrollmentID
var csp = new CspParameters();
csp.KeyContainerName = "TestPrivKey" + ID;
csp.Flags |= CspProviderFlags.UseMachineKeyStore;
var rsaPrivate = new RSACryptoServiceProvider(csp);
// Import private key to windows keystrore, from already generated BouncyCastle rsa privatekey
rsaPrivate.ImportParameters(rsaPriv.ExportParameters(true));
//Console.Write("rsaprivate key:" + rsaPrivate.ToXmlString(true));
System.Security.Cryptography.X509Certificates.X509Certificate2 certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2();
var flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet;
certificate.Import(cert.GetEncoded(), String.Empty, flags);
certificate.PrivateKey = rsaPrivate;
// opening up the windows cert store because thats where I want to save it.
System.Security.Cryptography.X509Certificates.X509Store store = new System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser);
store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.MaxAllowed);
store.Add(certificate);
store.Close();
rsaPrivate.PersistKeyInCsp = true; //persisting the key in container is important to retrieve the key later
///make non exporable
csp.Flags = CspProviderFlags.UseNonExportableKey;
var rsaPrivate2 = new RSACryptoServiceProvider(csp);
rsaPrivate2.ExportParameters(false); //restrict to export
rsaPrivate2.PersistKeyInCsp = true;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("Error : " + e);
Console.WriteLine(e);
Log.Print(LogLevel.High, e.ToString());
}
}
And when I try to export the certificate, I get the option to Yes, export the private key too, like the below image -
Is there any way to make the private-key non-exportable like the first image while creating it programmatically? I would grateful for any hints, references or code samples. Thanks.
To make the private key non-exportable, the CspProviderFlags.UseNonExportableKey must additionally be set when importing the key:
...
var csp = new CspParameters();
csp.KeyContainerName = "TestPrivKey" + ID;
csp.Flags |= CspProviderFlags.UseMachineKeyStore | CspProviderFlags.UseNonExportableKey; // Fix
var rsaPrivate = new RSACryptoServiceProvider(csp);
rsaPrivate.ImportParameters(rsaPriv.ExportParameters(true));
...
If this is done, the Yes, export the private key option is disabled in the wizard.
Note that in the posted code that flag is also set, but it is set too late, namely after the certificate has been saved to the store.
I create a CSR with Bouncy Castle and sign it on HSM (EC Key). It works fine! But in the CSR content (and in the certificate later) there is no EC curve name. Instead, there are the parameters of the curve. Some clients have problems with this, e.g .: Phyton. On the screenshot you can see how OpenSSL does it. How can I force Bouncy Castle to write the curve name in the CSR?
The code for the Adding the Public Key to CSR:
IList oids = new ArrayList();
IList values = new ArrayList();
..........
SubjectKeyIdentifier subjectKeyIdentifier = new SubjectKeyIdentifierStructure(publicKey);
X509Extension ski = new X509Extension(true, new DerOctetString(subjectKeyIdentifier));
oids.Add(X509Extensions.SubjectKeyIdentifier);
values.Add(ski);
AttributePkcs attribute = new AttributePkcs(PkcsObjectIdentifiers.Pkcs9AtExtensionRequest,
new DerSet(new X509Extensions(oids, values)));
//End Extensions
// SIGN on HSM
Pkcs10CertificationRequestDelaySigned csr = new Pkcs10CertificationRequestDelaySigned(
signatureAlgorithmStr,
subject,
publicKey,
new DerSet(attribute)
);
......
Screenshot: CSR with Bouncy Castle und OpenSSL
The issue isn't with BouncyCastle, but rather how the public key is being generated / initialized.
You can either opt for named curve or as expanded group parameters, but not both.
Technically they are equivalent, but usage wise, they don't mix well with one another. So you will have to generate both if you want to cater to both of those scenarios.
I don't know how you are initializing the public key, but you can convert to a different public key type before signing.
AsymmetricKeyParameter publicKey = null;
// ... existing public key by some means
var castedPublicKey = (ECPublicKeyParameters)publicKey;
var newPublicKey = new ECPublicKeyParameters(
castedPublicKey.AlgorithmName
,castedPublicKey.Q,
X9ObjectIdentifiers.Prime256v1);
// ... rest of code
// SIGN on HSM
Pkcs10CertificationRequestDelaySigned csr = new Pkcs10CertificationRequestDelaySigned(
signatureAlgorithmStr,
subject,
newPublicKey,
new DerSet(attribute)
);
Change Prime256v1 to whatever curve you are using
How can I achieve the very basic CSR Signing HSM functionality with Azure Key Vault?
I had found a very long and manual process to somehow achieve it:
Create a private key in Key Vault
Create a CSR, digest it with SHA256
Sign the digest with the previous private key using the Sign() method
Create a local x.509 cert and append the signature
Upload the new signed cert to Key Vault
Problem is, it is manual, long (also, quite a bit of latency) and error prone. Also I haven't found a single C# code example for this, and I'm looking for EC and not RSA.
The question is, is there a simple CertificateRequest.Sign() function in Key Vault? this seems to be so basic for an HSM-like service...
Thanks
This blog post by Vitaliy Slepakov describes a solution that he created which implements the steps you listed above using C#/.NET Core.
The code is available here:
https://github.com/vslepakov/keyvault-ca/
The heart of it is the following:
byte[] certificateRequest = /* ... */;
string issuerCertificateName = /* ... */;
KeyVaultServiceClient keyVaultServiceClient = /* ... */;
X509SignatureGenerator generator = /* see next section */;
var pkcs10CertificationRequest = new Pkcs10CertificationRequest(certificateRequest);
//TODO: Validate CSR via pkcs10CertificationRequest.Verify()
var info = pkcs10CertificationRequest.GetCertificationRequestInfo();
var notBefore = DateTime.UtcNow.AddDays(-1);
// Get the RSA public key from the CSR
var asymmetricKeyParameter = Org.BouncyCastle.Security.PublicKeyFactory.CreateKey(info.SubjectPublicKeyInfo);
var rsaKeyParameters = (Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)asymmetricKeyParameter;
var rsaKeyInfo = new RSAParameters
{
Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned(),
Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned()
};
var publicKey = RSA.Create(rsaKeyInfo);
//TODO: Validate publicKey
var certBundle = await keyVaultServiceClient.GetCertificateAsync(issuerCertificateName).ConfigureAwait(false);
var signingCert = new X509Certificate2(certBundle.Cer);
// new serial number
var serialNumber = new byte[SerialNumberLength];
RandomNumberGenerator.Fill(serialNumber);
serialNumber[0] &= 0x7F;
var subjectDN = new X500DistinguishedName(subjectName);
var request = new CertificateRequest(subjectDN, publicKey, GetRSAHashAlgorithmName(hashSizeInBits), RSASignaturePadding.Pkcs1);
// Basic constraints
request.CertificateExtensions.Add(
new X509BasicConstraintsExtension(caCert, caCert, 0, true));
// Subject Key Identifier
var ski = new X509SubjectKeyIdentifierExtension(
request.PublicKey,
X509SubjectKeyIdentifierHashAlgorithm.Sha1,
false);
request.CertificateExtensions.Add(ski);
// Authority Key Identifier
if (issuerCAKeyCert != null)
request.CertificateExtensions.Add(BuildAuthorityKeyIdentifier(issuerCAKeyCert));
else
request.CertificateExtensions.Add(BuildAuthorityKeyIdentifier(subjectDN, serialNumber.Reverse().ToArray(), ski));
if (caCert)
request.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature |
X509KeyUsageFlags.KeyCertSign |
X509KeyUsageFlags.CrlSign,
true));
else
{
// Key Usage
var defaultFlags =
X509KeyUsageFlags.DigitalSignature |
X509KeyUsageFlags.DataEncipherment |
X509KeyUsageFlags.NonRepudiation |
X509KeyUsageFlags.KeyEncipherment;
request.CertificateExtensions.Add(new X509KeyUsageExtension(defaultFlags, true));
// Enhanced key usage
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection {
new Oid("1.3.6.1.5.5.7.3.1"),
new Oid("1.3.6.1.5.5.7.3.2") }, true));
}
if (issuerCAKeyCert != null)
{
if (notAfter > issuerCAKeyCert.NotAfter)
{
notAfter = issuerCAKeyCert.NotAfter;
}
if (notBefore < issuerCAKeyCert.NotBefore)
{
notBefore = issuerCAKeyCert.NotBefore;
}
}
var issuerSubjectName = issuerCAKeyCert != null ? issuerCAKeyCert.SubjectName : subjectDN;
X509Certificate2 signedCert = request.Create(
issuerSubjectName,
generator,
notBefore,
notAfter,
serialNumber);
The custom X509SignatureGenerator implementation is here and used the following Key Vault SDK method:
HashAlgorithm hash = /* see GitHub */;
var digest = hash.ComputeHash(data);
var resultKeyVaultPkcs = await keyVaultClient.SignAsync(signingKey, algorithm, digest, RSASignaturePadding.Pkcs1);
Hopefully you can adapt this code to meet your needs. I'll be doing that as well. 😀
KV has a sign operation, but it's not specific to any data type (such as a cert request).
The KV .NET SDK offers this https://learn.microsoft.com/en-us/dotnet/api/microsoft.azure.keyvault.keyvaultclient.signwithhttpmessagesasync?view=azure-dotnet-legacy which is just a wrapper over REST APIs https://learn.microsoft.com/en-us/rest/api/keyvault/
This isn't exactly what you want, but I wrote some code to demonstrate C# KV encrypt/decrypt which should help you get started https://github.com/x509cert/AzureKeyVault
I created a self signed certificate in Azure Key Vault using the following method :
public void CreateRootCertificate()
{
var certPolicy = new CertificatePolicy();
certPolicy.Attributes = new CertificateAttributes();
certPolicy.Attributes.NotBefore = DateTime.Now;
certPolicy.Attributes.Expires = DateTime.Now.AddDays(1);
certPolicy.IssuerParameters = new IssuerParameters()
{
Name = "Self",
};
certPolicy.KeyProperties = new KeyProperties(true);
certPolicy.SecretProperties = new SecretProperties();
certPolicy.X509CertificateProperties = new X509CertificateProperties()
{
Subject = "CN=testyMcTesterson",
};
var operation = this.client.CreateCertificateAsync(keyVaultUrl, testRootName, certPolicy);
operation.Wait();
}
Now after I create the self signed certificate, I'd like to sign other certificates using this one. The only caveat is, I'd like to do it without having to pull the private key out of key vault. Is this even possible? I've tried several permutations of the following method.
public void CreateSignedCertificate()
{
var certPolicy = new CertificatePolicy();
certPolicy.Attributes = new CertificateAttributes();
certPolicy.Attributes.NotBefore = DateTime.Now;
certPolicy.Attributes.Expires = DateTime.Now.AddDays(1);
certPolicy.IssuerParameters = new IssuerParameters()
{
Name = "CN=testyMcTesterson"
};
certPolicy.KeyProperties = new KeyProperties(true);
certPolicy.SecretProperties = new SecretProperties();
certPolicy.X509CertificateProperties = new X509CertificateProperties()
{
Subject = "CN=testyJunior",
};
var operation = this.client.CreateCertificateAsync(keyVaultUrl, "testyJunior", certPolicy);
operation.Wait();
}
This includes setting the issuer to "testyMcTesterson" without CN=, setting it to the key vault certificate identifier and the key vault secret. I'd like to set it up so only the .cer file of the signing cert will ever leave key vault. All of these throw a 400 excpetion saying the IssuerParameters.Name property is invalid. I realize I'm more than likely missing some EKUs on both the root and the client, but the problem I'm trying to address right now is finding out if this scenario is even feasible. The documentation on the IssuerParameters class is lacking.
I'm signing a PDF document with a mobile signing service. I recieve a certificate from the service after it has signed the hash of the document. I am able to replace zero padded signature container in the document with the certificate without any problems, but I"m having problems with including a certificate chain.
I have the root, intermediate and leaf certificates with the application, but I'm not able to include them in the signature. What I thought I would be able to do was to create a chain in code and then inject the encoded bytes from that chain, but this results in an invalid certificate.
The code I'm using to do that is as follows:
X509CertificateParser cp = new X509CertificateParser();
var certFromServer = getCertFromServer();
var rootCert = cp.ReadCertificate(new X509Certificate2(rootCertPath).RawData);
var interCert = cp.ReadCertificate(new X509Certificate2(interCertPath)RawData);
var leafCert = cp.ReadCertificate(new X509Certificate2(leafCertPath).RawData);
List<X509Certificate> intermediateCerts = new List<X509Certificate> {
interCert,
leafCert
};
X509CertificateParser parser = new X509CertificateParser();
PkixCertPathBuilder builder = new PkixCertPathBuilder();
X509CertStoreSelector holder = new X509CertStoreSelector {
Certificate = parser.ReadCertificate(certFromServer)
};
intermediateCerts.Add(holder.Certificate);
HashSet rootCerts = new HashSet {new TrustAnchor(rootCert, null)};
PkixBuilderParameters builderParams = new PkixBuilderParameters(rootCerts, holder)
{
IsRevocationEnabled = false
};
X509CollectionStoreParameters intermediateStoreParameters =
new X509CollectionStoreParameters(intermediateCerts);
builderParams.AddStore(X509StoreFactory.Create(
"Certificate/Collection", intermediateStoreParameters)
);
PkixCertPathBuilderResult result = builder.Build(builderParams);
byte[] certChainBytes = result.CertPath.GetEncoded("PKCS7");
// ExternalSignatureContainer is a container that simply returns the cert bytes
// from its Sign method without changing them.
IExternalSignatureContainer container = new ExternalSignatureContainer(certChainBytes);
MakeSignature.SignDeferred(reader, _signatureFieldName, baos, container);
The method to create the chain is from the following StackOverflow question: Build certificate chain in BouncyCastle in C#
What is the correct way to build a certificate chain for the signature container in iTextSharp?