Digital signature using bouncy castle and certificate private key - c#

I am developing a feature to digital sign some content. I have valid certificate with a private key. How to digital sign using the private key and bouncy castle?
I tried the following but want some right way to achieve the same using bouncy castle:
X509Certificate2 signingCert =
CryptoHelper.FindCertificate("21A6107EC254457AAF3D4D6FD286FB79");
var rsaObj = (RSACryptoServiceProvider)signingCert.PrivateKey;
_privateKey = rsaObj.ExportParameters(true);
Thanks!

I donĀ“t know exactly what you need based on your code, but there X509 namespace/code is at
bcgit/bc-csharp - X509 and there is an utility class for conversion between System.Security.Cryptography and BouncyCastle
bcgit/bc-csharp - DotNetUtilities.cs
BouncyCastle got lots of test (and examples). Have a look at bcgit/bc-csharp - TestCertificateGen.cs too. Maybe this helps you.
EDIT: In general it should go something like this
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
// Your loaded certificate
X509Certificate cert = null;
// Your loaded RSA key
AsymmetricKeyParameter privateKey = null;
AsymmetricKeyParameter publicKey = cert.GetPublicKey();
ISigner signer = SignerUtilities.GetSigner(cert.SigAlgName);
// Init for signing, you pass in the private key
signer.Init(true, privateKey);
// Init for verification, you pass in the public key
signer.Init(false, publicKey);
Greetings

Related

How to persist private key of RSAParameter in C#/.net

Starting with .Net 4.7.2 (.Net Standard 2.0) it's possible to create self-signed certificates and certificate signing requests with C#/.Net only, see the MS Documentation.
While it's straight forward to create a self-signed certificate which will assert HasPrivateKey (you just call CreateSelfSigned(notBefore, notAfter)) I'm having a hard time to figure out how to get hold of the private key in general, e.g. if I want to create a certificate signed by a CA and then want to persist the certificate as a PFX file or want to persist the private key in a .PEM file or want to store it in the MS certificate store together with the private key, or when I just want to have in memory and also assert HasPrivateKey.
What I do have is a 'RSAParameters' instance which is in possession of the relevant private information, but I failed to figure out how to (easily) use that for the purpose in question (create a PFX file or PEM file or MS Certificate Store entry) without having to read through all the relevant RFCs and write a program for that on my own. (That RSAParameter instance contains the D, Exponent and Modulus, so I could try to patch this together (with the help of this answer, hopefully), but I was hoping for a C# method which will perform these tasks for me (which I could not find) by now).
Of course the idea is to do that with .Net functionality alone, as well.
Every hint on how to achieve this is appreciated.
If you only have Modulus, Exponent, and D you first have to recover the CRT parameters (P, Q, DP, DQ, InverseQ).
As your other questions, you're mainly missing the cert.CopyWithPrivateKey(key) extension methods and rsa.ImportParameters(RSAParameters):
if I want to create a certificate signed by a CA and then want to persist the certificate as a PFX file
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
using (X509Certificate2 caSigned = GetCASignedCert(rsa))
using (X509Certificate2 withKey = caSigned.CopyWithPrivateKey(rsa))
{
File.WriteAllBytes("some.pfx", withKey.Export(X509ContentType.Pkcs12, "and a password"));
}
}
or want to persist the private key in a .PEM file
This one is available in .NET Core 3.0 daily builds:
RSAParameters rsaParameters = default(RSAParameters);
using (StreamWriter writer = new StreamWriter("rsa.key"))
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
writer.WriteLine("-----BEGIN RSA PRIVATE KEY-----");
writer.WriteLine(
Convert.ToBase64String(
rsa.ExportRSAPrivateKey(),
Base64FormattingOptions.InsertLineBreaks));
writer.WriteLine("-----END RSA PRIVATE KEY-----");
}
PKCS#8 and encrypted PKCS#8 are also available.
On existing versions this requires using the RSAParameters and a ITU-T X.690 DER encoder.
or want to store it in the MS certificate store together with the private key
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
using (X509Certificate2 caSigned = GetCASignedCert(rsa))
using (X509Certificate2 withKey = caSigned.CopyWithPrivateKey(rsa))
using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
X509Certificate2 persisted = new X509Certificate2(
withKey.Export(X509ContentType.Pkcs12, ""),
"",
X509KeyStorageFlags.PersistKeySet);
using (persisted)
{
store.Open(OpenFlags.ReadWrite);
store.Add(persisted);
}
}
}
or when I just want to have in memory and also assert HasPrivateKey.
using (RSA rsa = RSA.Create())
{
rsa.ImportParameters(rsaParameters);
using (X509Certificate2 caSigned = GetCASignedCert(rsa))
{
// Yes, this value can outlive both usings
return caSigned.CopyWithPrivateKey(rsa);
}
}
If you are using a certificate hardware security module (HSM) such as a USB key, for example, it is not possible to "get" the private key because the HSM only provides an interface for using the private key. This is for security as once a private key is in a file or memory it is potentially obtainable by a third party.
Also .NET has, historically, not presented a flexible enough interface, although it is improving. As such a many software vendors use the more complete and well-maintained, Bouncy Castle API (http://www.bouncycastle.org/csharp/) and you will find a lot of documentation around the web. Generally if .NET can't do it - bouncy castle will. Ironically an HSM requires .NET crypto access it's private key functionality on windows, but you usually encapsulate that somehow.
It is a steep learning curve using crypto APIs in general and it's unlikely that you will get much assistance without having a code example you want to make work.

Update x509Certificate2 with correct Cryptographic Service Provider in Bouncy Castle

I am creating x509Certificate2 using CertificateGenerator.GenerateCertificate from this blog post of Wiktor Zychla (http://www.wiktorzychla.com/2012/12/how-to-create-x509certificate2.html)
Bouncy Castle crypto library is used to generate the certificate file. I need stronger signature algorithm to be used, so instead of SHA1withRSA (one in example) I am using SHA256withRSA.
Certificate is generated and exported successfully to .pfx file. When using the certificate later on I get error Invalid algorithm specified.
When run certutil -dump mycert.pfx, I see incorrect Cryptographic Service Provider (CSP) is set: Microsoft Base Cryptographic Provider v1.0
...
---------------- End Nesting Level 1 ----------------
Provider = Microsoft Base Cryptographic Provider v1.0
...
How can I tell Bouncy Castle API to use different CSP? The Microsoft Enhanced RSA and AES Cryptographic Provider, that actually can deal with SHA256withRSA.
There is very little resources on Bouncy Castle and C#, so any link to some documentation or related examples would be greatly appreciated.
List of CryptoAPI CSPs and algorithms, they support:
https://msdn.microsoft.com/en-us/library/windows/desktop/bb931357(v=vs.85).aspx
The easiest way is to specify the CSP when you are importing the generated pfx. You can use this command
certutil -importPFX -csp "Microsoft Enhanced RSA and AES Cryptographic Provider" -v c:\yourpfx.pfx AT_KEYEXCHANGE,NoExport,NoProtect
which will
import into LocalMachine\My
set CSP to Microsoft Enhanced RSA and AES Cryptographic Provider
set private key usage to Exchange
set private key as non-exportable
set private key with no additional (password) protection
The CSP is windows specific field in PKCS#12 (PFX) and no one except windows is setting this. If you are using PFX from file new X509Certificate2(filename) then you have to change the private key. Convert PrivateKey property to RSACryptoServiceProvider and modify CspParameters (I don't have a snippet right now). Then set the modified RSACryptoServiceProvider back to PrivateKey property.
------- Edit
Here is the sample code that changes CSP on PFX read from file
// need to set exportable flag to be able to ... export private key
X509Certificate2 cert = new X509Certificate2(#"d:\test.pfx", "a", X509KeyStorageFlags.Exportable);
var privKey = cert.PrivateKey as RSACryptoServiceProvider;
// will be needed later
var exported = privKey.ToXmlString(true);
// change CSP
var cspParams = new CspParameters()
{
ProviderType = 24,
ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
};
// create new PrivateKey from CspParameters and exported privkey
var newPrivKey = new RSACryptoServiceProvider(cspParams);
newPrivKey.FromXmlString(exported);
// Assign edited private key back
cert.PrivateKey = newPrivKey;
// export as PKCS#12/PFX
var bytes = cert.Export(X509ContentType.Pfx, "a");

RSA using bouncy castle with key stored in CSP blog

I have a private key in a csp format. I need migrate to Bouncy Castle. But I don't find any function to import this format.
Please, can you help me to traslate this code?
using (RSACryptoServiceProvider privatekey = new RSACryptoServiceProvider()) {
privatekey.ImportCspBlob("chave em csp");
Byte[] buffer = Encoding.GetEncoding("utf-8").GetBytes(textEdit1.Text);
Byte[] signature = privatekey.SignData(buffer, "SHA1");
return Convert.ToBase64String(signature);
}
Thanks
You can start with this: C# Sign Data with RSA using BouncyCastle, it tells how to sign using Bouncy Castle, to import the key to Bouncy Castle, you need to look at the DotNetUtilities class on Org.BouncyCastle.Security namespace.

How to use GnuPG (Libgcrypt) generated keys with RSACryptoServiceProvider?

Is it possible to use PGP keys in .NET's RSA?
I tried this code, without success.
X509Certificate2 certificate = new X509Certificate2();
certificate.Import("C:\\All Public Keys.asc");
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
How to load this key?

How can I add a private key to X509Certificate2 after I have already added the public key?

My steps are:
Create X509Certificate2 with public key:
X509Certificate2 clientCertificate = new X509Certificate2("public key certificate blob as byte[]");
How do I want to load the private key blob to clientCertificate?
It looks like you can not import RSA private key with just .net framework tools.
Check out this thread How to read a PEM RSA private key from .NET.
Just in case anyone like me and up looking at this old post when searching for how to generate X509Certificate2 from pem fil/private key:
The .Net 5.0 framework has a very simplified approach to this:
var certPem = File.ReadAllText("cert.pem");
var eccPem = File.ReadAllText("ecc.pem");
var cert = X509Certificate2.CreateFromPem(certPem, eccPem);
(source: https://www.scottbrady91.com/C-Sharp/PEM-Loading-in-dotnet-core-and-dotnet)
Also see: How to import PKCS#8 RSA privateKey (created by OpenSSL) in C#
It includes a link to Mono's source code which can read PKCS#8 / PEM files and return an RSA instance from it.

Categories