Load ASN.1/DER encoded RSA keypair in C# - c#

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.

Related

C# CX509PrivateKey Format

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!

Create .pem file for public key RSA encryption C# .net

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.

Export private key from X509Certificate object

We use C# code we build X509Certificate2 with .p12 file, in the constructor we insert the path to certificate, certificate's password. We also marked it as Exportable as shown below:
X509Certificate2 x509Certificate2 = new X509Certificate2
("...\\MyCerificate.p12", "P#ssw0rd", X509KeyStorageFlags.Exportable);
we get the private key as AsymmetricAlgorithm format by the following:
x509Certificate2.PrivateKey
Now, we want to get the private key from the certificate as Base64 format - but we don't have any idea how to do it, and its so important for us.
The important question is why base64 ?
If this is for your own application then you can keep the private key as an XML string (much easier :-).
string xml = x509Certificate2.PrivateKey.ToXmlString (true);
If you want base64 (again just for your application) you can export the key (RSAParameters) then concat every byte[] and turn the merged output to a base64 string.
But if you want to interop with other applications that requires a base64 private key then you need to know the format (inside the base64 string). E.g. in many case private keys are PEM encoded (which is base64 with a special header/footer, see an example for X509Certificate).
If that what's you're looking for then you'll need to encode the private key within a PKCS#8 structure first, then turn in into base64 and add the header/footer. You can find some helpful code to do so inside Mono.Security.dll (MIT.X11 licensed code from the Mono project).
You can simply use the PrivateKey property of X509Certificate2.
The actual returned private key implementation depends on the algorithm used in the certificate - usually this is RSA:
rsaObj = (RSACryptoServiceProvider)myCertificate.PrivateKey;
Afterwards you should be able to get the RSA key information from it's ExportParameters property.
You can do that with OpenSSL Library for .NET:
using DidiSoft.OpenSsl;
...
X509Certificate2 x509Certificate2 = new X509Certificate2
("...\\MyCerificate.p12", "P#ssw0rd", X509KeyStorageFlags.Exportable);
PrivateKey privKey = PrivateKey.Load(x509Certificate2.PrivateKey);
bool withNewLines = true;
string base64PrivateKey = privKey.ToBase64String(withNewLines);
If your only problem is to get the private key Base64 encoded, you can simply do like this:
var privateKey = x509Certificate2.PrivateKey;
var encoding = new System.Text.ASCIIEncoding();
var base64String = Convert.ToBase64String(encoding.GetBytes(privateKey.ToString()));

how to decrypt an encrypted text using RSACryptoServiceProvider?

I have encrypted a text using RSACryptoServiceProvider. I exported the public and private key. Obviously I just want to expose the public key inside the decoder application, so I have written a code as follows :
private const string PublicKey = "<RSAKeyValue><Modulus>sIzQmj4vqK0QPd7RXKigD7Oi4GKPwvIPoiUyiKJMGP0qcbUkRPioe2psE/d3c1a2NY9oj4Da2y1qetjvKKFad2QAhXuql/gPIb1WmI+f6q555GClvHWEjrJrD/ho7SLoHbWd6oY6fY609N28lWJUYO97RLVaeg2jfNAUSu5bGC8=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
private string Decrypt()
{
byte[] encryptedKeyAsBytes = Convert.FromBase64String(_encryptedKey);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(PublicKey);
// read ciphertext, decrypt it to plaintext
byte[] plainBytes = rsa.Decrypt(encryptedKeyAsBytes, false);
string plainText = System.Text.Encoding.ASCII.GetString(plainBytes);
return plainText;
}
But an exception is thrown at line "byte[] plainBytes = rsa.Decrypt(encryptedKeyAsBytes, false);"
and says "Key does not exist." However if I expose the whole private and public key then it runns happily. So how can I decrypt the data using only the public key information?
You can't - that is the point of public/private key encryption. The public does the encryption; the private does the decryption.
It sounds like you need some sort of key exchange pattern. For example; if your decoder application is trying to decrypt information from another data source (Source Application), I would implement something like this:
The Source Application generates a symmetric key, like AES.
The Decoder application generates a public and private key pair.
The Source Application asks the Decoder application for the public key.
The Source application encrypts the symmetric key using the public key, and sends it back to the Decoder application.
The Decoder application uses the private key to decrypt the symmetric key.
The Decoder application gets data encrypted with the symmetric key from the Source Application.
The Decoder Application uses the exchanged symmetric key to decrypt the information it received.
There is just an example; but illustrates the basics of how to exchange data between two applications without any sensitive information transmitted over the wire. The symmetric key is not required at all; but is a very common pattern because RSA starts to introduce problems when encrypting large amounts of information. RSA is better to just encrypt an symmetric encryption key instead.
The short answer is: you can't. To decrypt messages you need the private key, that's the major principle of asymmetric cryptography.
You encrypt messages using someone's public key so that only the person in possession of the corresponding private key is able to decrypt them.
That's why the public key is called public - you may safely distribute it to the public so that they can encrypt messages to be read by you who is the sole owner of the corresponding private key.
The problem is that you're confusing encryption and signing.
Encryption is where anyone may write a message, but only the private key holder may read it. Signing is where anyone may read a message, but only the private key holder may write it.
When you call Decrypt, the RSACryptoServiceProvider is looking for encryption, that is, public write private read. Thus it looks for the private key.
You want to use the SignData and VerifyData functions to sign the payload so that people can't write it.

Padding error when using RSA Encryption in C# and Decryption in Java

Currently I am receiving the following error when using Java to decrypt a Base64 encoded RSA encrypted string that was made in C#:
javax.crypto.BadPaddingException: Not PKCS#1 block type 2 or Zero padding
The setup process between the exchange from .NET and Java is done by creating a private key in the .NET key store then from the PEM file extracted, created use keytool to create a JKS version with the private key. Java loads the already created JKS and decodes the Base64 string into a byte array and then uses the private key to decrypt.
Here is the code that I have in C# that creates the encrypted string:
public string Encrypt(string value) {
byte[] baIn = null;
byte[] baRet = null;
string keyContainerName = "test";
CspParameters cp = new CspParameters();
cp.Flags = CspProviderFlags.UseMachineKeyStore;
cp.KeyContainerName = keyContainerName;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cp);
// Convert the input string to a byte array
baIn = UnicodeEncoding.Unicode.GetBytes(value);
// Encrypt
baRet = rsa.Encrypt(baIn, false);
// Convert the encrypted byte array to a base64 string
return Convert.ToBase64String(baRet);
}
Here is the code that I have in Java that decrypts the inputted string:
public void decrypt(String base64String) {
String keyStorePath = "C:\Key.keystore";
String storepass = "1234";
String keypass = "abcd";
byte[] data = Base64.decode(base64String);
byte[] cipherData = null;
keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(keyStorePath), storepass.toCharArray());
RSAPrivateKey privateRSAKey = (RSAPrivateKey) keystore.getKey(alias, keypass.toCharArray());
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateRSAKey);
cipherData = cipher.doFinal(data);
System.out.println(new String(cipherData));
}
Does anyone see a step missing or where the padding or item needs to be changed? I have done hours of reading on this site and others but haven't really found a concrete solution.
You're help is vastly appreciated.
Thanks. -Matt
I had exactely the same problem and I finally find the solution!
I was stubborn using PKCS1Padding but I didn't manage to make it work.
The best result I got using "rsa.Encrypt(baIn, false)" on the C# side and "RSA/NONE/NoPadding" on the Java side was this kind of string : "☻?o+_>??5?l0Q*???*?R▲???♀7..." followed by my decrypted string. So in a way it got decrypted but since there is no padding specified, the data is shifted. So I tried all the paddings available in bouncycastle but I would alway get errors such as "block incorrect size" or "data hash wrong".
So I decided to start trying OAEP paddings and I finally managed to get it working by using "rsa.Encrypt(baIn, true)" on the C# side and "RSA/NONE/OAEPWithSHA1AndMGF1Padding" on the java side!
It worked for me, I hope it will work for you too! If it doesn't work make sure you're using the right key, very often the problem comes from the key.
Check that you have correctly exchanged the key.
Trying to decrypt with an incorrect key is indistinguishable from decrypting badly padded data.
I'm working through a similar problem operating between .Net and iPhone stuff in Objective - C, and I think the answer lies in this little gem from the RSACryptoServiceProvider documentation:
Unlike the RSA implementation in unmanaged CAPI, the RSACryptoServiceProvider class reverses the order of an encrypted array of bytes after encryption and before decryption. By default, data encrypted by the RSACryptoServiceProvider class cannot be decrypted by the CAPI CryptDecrypt function and data encrypted by the CAPI CryptEncrypt method cannot be decrypted by the RSACryptoServiceProvider class.
See here for more details: http://msdn.microsoft.com/en-us/library/s575f7e2(v=VS.90).aspx
I had the same problem when using Bouncy Castle 1.48 but it wasn't key-related. Instead, I found that I had to set the following system property:
-Dorg.bouncycastle.pkcs1.strict=false

Categories