I have an RSA private key loaded in an RSACryptoServiceProvider object. I need to export this to a .pvk file for use with SQL Server.
Is there a way to do this from within c#? I see it's possible using openssl... https://stackoverflow.com/a/51207929/5924962
This is the only documentation I could find on the pvk file format: https://web.archive.org/web/20141117214716/http://www.drh-consultancy.demon.co.uk/pvk.html
Mono project has an open source implementation of the PVK format,
https://github.com/mono/mono/blob/master/mcs/class/Mono.Security/Mono.Security.Authenticode/PrivateKey.cs
Note the Mono.Security NuGet package is not official.
If you look at the output of RSACryptoServiceProvider.ExportCspBlob you'll see that it starts with the same sequence of bytes as offset 24 of an unencrypted PVK file. So a PVK file is just the CSP blob with a header:
using (var output = new BinaryWriter(File.Create("rsa.pvk")))
{
output.Write(0xB0B5F11Eu); // PVK magic number
output.Write(0u);
output.Write(1u); // KEYTYPE_KEYX for RSA
output.Write(0u); // not encrypted
output.Write(0u); // encryption salt length
var cspBlob = rsaCSP.ExportCspBlob(true);
output.Write((uint)cspBlob.Length);
output.Write(cspBlob);
}
I've verified that OpenSSL will read this
openssl rsa -in rsa.pvk -inform PVK
and will generate the same PEM private key export as the code in this answer that generates the PEM private key directly from the RSACryptoServiceProvider.
Related
I'm working with a third party attempting to establish digital signature matching in both directions. On my side we are using C# while the third party is using Java. So far I am able to generate a key pairing within OpenSSL and use the private key to generate a signature which they are able to match in their Java environment (C# code below). Going the opposite direction, how do I use the generated public key to create a matching signature?
I've attempted using the same code below simply changing the CngKeyBlobFormat when importing the public key but all other types produce an error.
On both sides the following OpenSSL commands were used to generate the public and private keys:
openssl genrsa -des3 -out my_rsa_key_pair 2048
openssl pkcs8 -topk8 -inform PEM -in my_rsa_key_pair -outform PEM -out private_key.pem -nocrypt
openssl rsa -in my_rsa_key_pair -outform PEM -pubout -out public_key.pem
This is the C# code used to generate a signature from the private key:
byte[] keyBytes = Convert.FromBase64String(privateKey);
byte[] valueBytes = Encoding.UTF8.GetBytes(stringToSign);
byte[] signedBytes;
using (CngKey key = CngKey.Import(keyBytes, CngKeyBlobFormat.Pkcs8PrivateBlob))
using (RSA rsa = new RSACng(key))
{
signedBytes = rsa.SignData(valueBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signedBytes);
}
Using the public key I need to generate a signature within C# that matches what was generated with the private key. What is the proper way to do this?
There are a lot of posts surrounding signature generation / verification and after hitting on the right key words I found the answer in the following post. Given the above code and method of generating public/private key pairings the answer in this post will successfully validate the signature generated with a private key using only the public key. Leaving this open for anyone searching for both sides of the solution:
Verify signature generated with RSA 2048-bit key, SHA256 algorithm and PKCSv1.5 padding
I have an ECC private and a certificate file which includes the public key. I can get them in either PEM or DER formats.
I can read the certificate into an X509Certificate with this code:
var certbytes = File.ReadAllBytes("certificate.pem");
var cert = new X509Certificate2(certbytes);
But I'm unable to load the private key. I've tried this code:
var keyContent = File.ReadAllBytes("certificate_private_key.pem");
var key = CngKey.Import(keyContent, CngKeyBlobFormat.EccPrivateBlob);
It throws Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'An error occurred during encode or decode operation'.
I've also tried other values of the CngKeyBlobFormat parameter. The Import method fails with those as well.
openssl can read the file, and it outputs the following information about it:
openssl ec -in certificate_private_key.pem -text
read EC key
Private-Key: (256 bit)
priv:
44:<cut>:68
pub:
04:<cut>:13
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcC <cut here>
-----END EC PRIVATE KEY-----
Is there built-in API in .NET or .NET Core which can do this? Or are there 3rd party libraries that can do it, and how?
.NET Core 3.0 (currently in preview) has ECDsa.ImportECPrivateKey, AsymmetricAlgorithm.ImportPkcs8PrivateKey, and AsymmetricAlgorithm.ImportEncryptedPkcs8PrivateKey, (and RSA has RSAPublicKey and RSAPrivateKey) and for the current file (BEGIN EC PRIVATE KEY) you'd want the first one.
The good news of those methods is: they exist.
The bad news is: They're part of the next version, not the current one.
Good: The next version should be the current version soonishly.
Bad: They only understand BER/DER data, not PEM.
The last point means that you currently would have to find the base64 content between the -----BEGIN EC PRIVATE KEY-----\n and \n-----END EC PRIVATE KEY----- and de-base64-it, then pass that into the methods.
The only private key formats that I know that are supported by CNG import are PKCS8, encrypted PKCS8, and CNG private formats. To use CngKey.Import you'd first need to convert the keyfile to PKCS#8 then specify that the format is Pkcs8PrivateBlob, as suggested in the comments.
I have DER encoded RSA keypair created in Crypto++, as well as cipher. They are Base64Encoded string. I first decode the data from Base64 to byte array, but I am not sure how to load them into RSACryptoServiceProvider.
static void Main()
{
string pbkeystr = "mypublickey";
string pvkeystr = "myprivatekey";
string cipherstr = "mycipher";
byte[] pbkey = Convert.FromBase64String(pbkeystr);
byte[] pvkey = Convert.FromBase64String(pvkeystr);
byte[] cipher = Convert.FromBase64String(cipherstr);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
//Set keys here..
//Decrypt the cipher using private key
rsa.Decrypt(pvkey, false);
}
There are no functions to set keys. The only thing I found was ImportParameters method, which takes RSAParameters class which consists of p, q, n, modulus, exponent etc. I don't have access to these.
Is there any way I can load the keys as string? How can I load the key into RSACryptoServiceProvider?
Is there any way I can load the keys as string? How can I load the key into RSACryptoServiceProvider?
From your other Crypto++ question, How to load Base64 RSA keys in Crypto++, it looks like you have only the public and private keys because you used DEREncode and BERDecode. That is, you have the RSA parameters, and not the subject public key info and the private key info. Your keys lack the OID identifiers and version numbers. Things are fine that way.
From Cryptographic Interoperability: Keys on the Code Project, you will need a C# class that parses the ASN.1/DER after you Base64 decode it. The CodeProject article provides a C# class called AsnKeyParser to read the ASN.1/DER and returns a RSAParameters to load into a CSP.
The code for the AsnKeyParser class is about 800 lines, and there are five other supporting files to make it all happen, so its not really appropriate to place it here. You should download it yourself. The file of interest is called CSInteropKeys.zip.
Once you wire-in the AsnKeyParser class, it will be as simple as the following for a RSA Public key. The private key will be similar, and the code is given on the CodeProject site.
// Your ASN.1/DER parser class
AsnKeyParser keyParser = new AsnKeyParser("rsa-public.der");
RSAParameters publicKey = keyParser.ParseRSAPublicKey();
// .Net class
CspParameters csp = new CspParameters;
csp.KeyContainerName = "RSA Test (OK to Delete)";
csp.ProviderType = PROV_RSA_FULL; // 1
csp.KeyNumber = AT_KEYEXCHANGE; // 1
// .Net class
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
rsa.PersistKeyInCsp = false;
rsa.ImportParameters(publicKey);
Linking to files on another site is frowned upon, but I don't know how to provide the information otherwise. There's too much source code involved to place in an answer.
For completeness, .Net does not make interop easy. They do not accept ASN.1/DER or PEM. Rather, .Net accepts some XML representation of the keys. I believe you can find it in RFC 3275, XML-Signature Syntax and Processing. Microsoft does not state that for you. I kind of pieced it together when I wrote the Code Project article.
Maybe we should add a class to Crypto++ to regurgitate XML in addition to ASN.1/DER and PEM.
I'm trying to re-use the key pair I generated for creating a PKCS10 Certificate Signing request, but I cannot figure out what the format of this private key is.
To create the key, I'm using the CERTENROLLLib CX509PrivateKey class.
I've set the Private Key ProviderType to XCV_PROV_RSA_FULL, and when I export it (trying to figure out what format it is) I use
Export("PRIVATEBLOB", EncodingType.XCN_CRYPT_STRING_BASE64)
When I export it, the private key always starts with "BwIAAACkAABSU0E"
Does anyone know what format this is? I thought ANS.1 DER Encoding always started with "MII" or someting like that.
I think I answered my own question:
The command
Export("PRIVATEBLOB", EncodingType.XCN_CRYPT_STRING_BASE64)
exports the private key as a BASE64 encoded CSP blob. In order to import is using the C# RSA libraries I had to use the following:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
byte[] CryptoKey = Convert.FromBase64String(base64CspBlob);
rsa.ImportCspBlob(CryptoKey);
That did it!
I want to create .pem file for the public key generated by this method
public static Tuple<string, string> CreateKeyPair()
{
CspParameters cspParams =
new CspParameters {
ProviderType = 1 /* PROV_RSA_FULL */
};
RSACryptoServiceProvider rsaProvider =
new RSACryptoServiceProvider(1024, cspParams);
string publicKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(false));
string privateKey = Convert.ToBase64String(rsaProvider.ExportCspBlob(true));
return new Tuple<string, string>(privateKey, publicKey);
}
Because I was generating this key for mobile application and they can not read it, they requested .pem file instead of public key as string
Please advice,
Recently I need to save PublicKey and PrivateKey generated in my C# application to file, and works with it later. I use for this purpose such library as CSharp-easy-RSA-PEM.
It is very simple and quick solution, so I will recommend this library to other guys.
I use following code to get PublicKey as string (and save it to pem file in format Base64):
string publicKeyStr = Crypto.ExportPublicKeyToX509PEM(_cryptoServiceProvider);
it returns something like this:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxnBvS8cdsnAev2sRDRYWxznm1
QxZzaypfNXLvK7CDGk8TR7K+Pzsa+tpJfoyN/Z4B6xdlpsERo2Cu6AzolvrDLx5w
ZoI0kgdfaBMbUkdOB1m97zFYjKWoPeTskFzWZ3GHcQ3EXT0NJXXFXAskY45vEpbc
5qFgEhcPy3BMqHRibwIDAQAB
-----END PUBLIC KEY-----
And I use following code to get PrivateKey as string:
string privateKeyStr = Crypto.ExportPrivateKeyToRSAPEM(_cryptoServiceProvider);
it returns something like this:
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCxnBvS8cdsnAev2sRDRYWxznm1QxZzaypfNXLvK7CDGk8TR7K+
Pzsa+tpJfoyN/Z4B6xdlpsERo2Cu6AzolvrDLx5wZoI0kgdfaBMbUkdOB1m97zFY
jKWoPeTskFzWZ3GHcQ3EXT0NJXXFXAskY45vEpbc5qFgEhcPy3BMqHRibwIDAQAB
AoGAAdwpqm7fxh0S3jOYpJULeQ45gL11dGX7Pp4CWHYzq1vQ14SDtFxYfnLWwGLz
499zvSoSHP1pvjPgz6lxy9Rw8dUxCgvh8VQydMQzaug2XD1tkmtcSWInwFKBAfQ7
rceleyD0aK8JHJiuzM1p+yIJ/ImGK0Zk2U/svqrdJrNR4EkCQQDo3d5iWcjd3OLD
38k1GALEuN17KNpJqLvJcIEJl0pcHtOiNnyy2MR/XUghDpuxwhrhudB/TvX4tuI0
MUeVo5fjAkEAw0D6m9jkwE5uuEYN/l/84rbQ79p2I7r5Sk6zbMyBOvgl6CDlJyxY
434DDm6XW7c55ALrnlratEW5HPiPxuHZBQJANnE4vtGy7nvn4Fd/mRQmAYwe695f
On1iefP9lxpx3huu6uvGN6IKPqS2alQZ/nMdCc0Be+IgC6fmNsGWtNtsdQJAJvB4
ikgxJqD9t8ZQ2CAwgM5Q0OTSlsGdIdKcOeB3DVmbxbV5vdw8RfJFjcVEbkgWRYDH
mKcp4rXc+wgfNFyqOQJATZ1I5ER8AZAn5JMMH9zK+6oFvhLUgKyWO18W+dbcFrBd
AzlTB+HHYEIyTmaDtXWAwgBvJNIHk4BbM1meCH4QnA==
-----END RSA PRIVATE KEY-----
Then you can use
RSACryptoServiceProvider publicX509key = Crypto.DecodeX509PublicKey(publicKeyStr);
RSACryptoServiceProvider privateRSAkey = Crypto.DecodeRsaPrivateKey(privateKeyStr);
to restore saved keys back to RSACryptoServiceProvider.
So, if someone need to resolve similar issue, you can just download this library, go to Solution Explorer -> (Right click on your project) -> Add -> Reference -> Overview in your Visual Studio to add this library in your project, and add using CSharp_easy_RSA_PEM; where you need it :)
First off, a so-called .pem file is not really a fixed specification or format. Several different kinds of distinct file formats are generally described as "PEM" files. When the SSLeay (now OpenSSL) project needed to produce a base64 encoded output file containing key information they borrowed formatting concepts from the old Privacy-Enhanced Mail RFCs 1421-1424 and they added the extension .pem to the end of these files. But such a file may contain public keys, private keys, certificate requests, certificates, certificate lists, and so on. Each is different. So if all you're told is to produce a .pem file you're going to have to guess what's really needed.
The easiest way to write such files is to use the Bouncycastle C# library. The package Org.BouncyCastle.OpenSsl contains a number of utilities including a PemWriter class that should help you.