Creating PKCS1 public key in Net Framework - c#

I need to create an RSA 1024 bit "PKCS1 public key without PEM headers" and store it as an array of bytes.
In Net. Framework I have 2 arrays: RSAParameters.Modulus and RSAParameters.Exponent (which constitue a public key as I understand).
How can I convert these two arrays into a "PKCS1 public key without PEM headers"?
Thank you.

If you need to encode only RSA public key, you can write your own wrapper, it will take 5 lines of coding.
RSA public key should be represented as the following ASN.1 structure:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
However, this will need you to learn some ASN.1 basics.
Also, you should make sure that you need to save only in this format, applications could also require algorithmIdentifier to be added.

There must be an easier way, but one way is to use the Bouncycastle C# library and a few of its classes as in the following example:
using System.Security.Cryptography;
using Org.BouncyCastle.Asn1;
public static byte[] DEREncode(RSACryptoServiceProvider rsa)
{
RSAParameters rsaParams = rsa.ExportParameters(false);
DerInteger n = new DerInteger(rsaParams.Modulus);
DerInteger e = new DerInteger(rsaParams.Exponent);
DerSequence seq = new DerSequence(new Asn1Encodable[] {n, e});
return seq.GetEncoded();
}

Related

Convert encrypt/decrypt from Java to C# using bouncycastle

I'm trying to convert a couple of function from Java to c# using Portable.BouncyCastle and while there are plenty of example out there, I don't seem to be able to find one to match my requirements as most example seem to explain one specific encryption/decryption method while this function appears to be more generic. I could be wrong of course as I'm a complete newbie to this and don't have any experience in either BouncyCastle, Java or encryption, so please bear with me on this one.
The java function is:
public static byte[] Cipher(int mode, byte[] key,
byte[] data, string algorithm, AlgorithmParameterSpec spec)
{
Cipher cipher = Cipher.getInstance(algorithm);
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
if (spec != null)
cipher.init(mode, keySpec, spec);
else
cipher.init(mode, keySpec);
return cipher.doFinal(data);
}
I found some code from BouncyCasle where I can match most of the functionality from what I can see:
byte[] K = Hex.Decode("404142434445464748494a4b4c4d4e4f");
byte[] N = Hex.Decode("10111213141516");
byte[] P = Hex.Decode("68656c6c6f20776f726c642121");
byte[] C = Hex.Decode("39264f148b54c456035de0a531c8344f46db12b388");
KeyParameter key = ParameterUtilities.CreateKeyParameter("AES", K);
IBufferedCipher inCipher = CipherUtilities.
GetCipher("AES/CCM/NoPadding");
inCipher.Init(true, new ParametersWithIV(key, N));
byte[] enc = inCipher.DoFinal(P);
1. SecretKeySpec:
SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
How do I create this using BC? Is that the equivalent of the SecretKeySpec:
KeyParameter key = ParameterUtilities.CreateKeyParameter("AES", K);
If it is, can I pass the "AES/CCM/NoPadding" instead of AES as it is done in Java?
2. spec parameter:
It passes parameters of type IvParameterSpec to the Cypher function when called from `Java` via the `AlgorithmParameterSpec spec` parameter:
Cipher(ENCRYPT_MODE, key, clearBytes,
algorithm,
new IvParameterSpec(iv))
`BouncyCastle` does not have an overloaded function for `.Init` to allow me to pass the spec parameter as it does in `Java`, so how do I mimic this behaviour?
3. IvParameterSpec: You can see that when cypher is called from java, it passes the AlgorithmParameterSpec spec as new IvParameterSpec(iv) but with BouncyCastle, it seems to be expecting a key?
ParametersWithIV(key, N)
Will that difference have any impact on the encryption/decryption?
This is current my attempt at "converting" this function:
public static byte[] Cipher(bool isEncrypt, byte[] key, byte[] data,
string algorithm, ICipherParameters spec)
{
IBufferedCipher cipher = CipherUtilities.GetCipher(algorithm);
KeyParameter keySpec = ParameterUtilities.
CreateKeyParameter(algorithm, key);
cipher.Init(isEncrypt, new ParametersWithIV(keySpec,
keySpec.GetKey()));
return cipher.DoFinal(data);
}
As you can see I've changed the spec parameter to ICipherParameters spec but I don't know if this will work as when using Bouncy, it looks like that when I create a new ParametersWithIV, it needs a key and from the test sample I provided above, that key is created using KeyParameter key = ParameterUtilities.CreateKeyParameter("AES", K); so technically won't work when trying to call my Cipher function as I will have called this function yet. Should I change spec parameter to iv and pass a byte[] instead?
Apologies if there is confusion or if things are not explained correctly but as I said, I'm new to this and trying to understand it better while also converting. I hope most of it makes sense and you'll be able to help.
Many Thanks.
PS: Note that I'm not in a position to test these in Java yet, but I will hopefully have an environment set up correctly in the new few days which will hopefully help testing values between .net & java.
UPDATE 1
Passing AES/CCM/NoPadding to:
KeyParameter key = ParameterUtilities.CreateKeyParameter
Throws an error, so this partially answers one of my questions. Is there a function in BouncyCastle to determine the correct value that is required i.e. AES when AES/CCM/NoPadding is passed?
Ended up using the code below as it appears to be working as expected but still annoyed I had to hardcode the AES as the key of IV parameter part. Ideally I would have liked this to have been based on my Algorithm. So, now I have a single function to encrypt and decrypt:
public static byte[] Cipher(bool forEncryption, byte[] key,
byte[] data, string algorithm, byte[] iv)
{
IBufferedCipher cipher = CipherUtilities.GetCipher(algorithm);
KeyParameter keySpec = ParameterUtilities.CreateKeyParameter("AES", key);
cipher.Init(forEncryption, new ParametersWithIV(keySpec, iv));
return cipher.DoFinal(data);
}
Hope this helps.

Decryption with RSACryptoServiceProvider fails

I want to decrypt a byte array. to do this, I encrypt the data in java with the servers public key. Then I send the data to the c# server. There I want to decrypt with the "RSACryptoServiceProvider" class. I initialize it with:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(1024);
rsa.ImportParameters(keyInfo);
The "keyInfo" (RSAParameters) variable is initialized with the "modulus" and the "privateKey"
keyInfo.Modulus = m.getBytes();
keyInfo.D = d.getBytes();
The values for "m" and "d" are hard-coded and from a BigInteger instance from here. I know hard-coded is bad but currently I can do so.
When I do like this, an exception is thrown "bad data". When I add the public key in
keyInfo.Exponent = pubExpBytes
then it passes but an exception is thrown later "Key does not exist" in the method
result = rsa.Decrypt(inputBuf, false);
and the property "PublicOnly" is true even though I added the key in "keyInfo.D".
What am I doing wrong?
When using RSACryptoServiceProvider Private Key is composed of Modulus, Exponent, P, Q, DP, DQ, InverseQ, and D to be set. It is best to load the key from an xml string like:
string xmlKey = "<RSAKeyValue><Modulus>2CASUiCNfcCc/Y2gCJwmUPc1VWwsPiMW/4s4EqUWaq29WYQsZj+xUFAlQHLG1VOorjD+UU30Yj3acr4O3b/5iDfsW+2zDc1UB/T+fvzMv6nLNIMJAVST+7GulAbzcmItrdcWi9UN6VWICYatg6QFi2eJIibfwrorzA2v4NXUrja2/OpbePDeIW0NmU2y+U9kl2n0XvQOy87751iRE0bFzRBOzURumh+ZE7pYDXodf50B72bPl/ytKUPzwMOCzOA4qRoROWHwCh5puT8i1FHN8HGykZCZE7s3Enf7UpJ8hp4N4caODQCeBJhISSosN0YTCNIsT76CjyXQwQOMVrDGLQ==</Modulus><Exponent>AQAB</Exponent><P>81hQqq4N6+ejjtvynaALqwrGX9A7F6xhipYRGUzT4bQ+6n/Z2maADtOw1k2nd1Uz6NmneYGtQkzkqpEgQvJ89Ds0m/ndDqySKpV+qWJzW8QeLmm7rqhcVmjMqaTYYfb2nJj0C7a9ixf8JsKKV5I6Q4E8iZNDJQkD0Ap1mXwEXX8=</P><Q>411gemkRFxg/mu51aHk6F/D/kfgBZHV7pGohzgJ3LFFfDxsLviWeLLWcLna9nV9YZpP7QaNchIHOiRq63I/JPn9E+gieTWpAFTLOVhNRdchdI8sE/OUTE7Q8CAeNHlZWqv6DZpUxPBWNFaqO2zuq2t104CAItwFfp+599mkgilM=</Q><DP>OA2Hx0kAe+6HhkizwgszpNp+h2N3uSRD86BNn/5KcBsMwwdgLyadKM0qVrLceGhv5Jr+MMbmGfeMVJ3JSKKCld9tZuBPQyoNITH+UxYSbHjoWtiQGtM9McpCGnGWRjqU4813qZsWXgvQUT1OkI1mTlZek3IQbV9+OVWvcQNYHhE=</DP><DQ>a1ehfglurSWgmefs/GcHe1gngOXC2ofw2N3WmelmFEFSjNIrCBnAhME1PGaI/OyP0tYYsJOM0W5rcSVyhUFJNL1ndlhtTIVWPevfqJm0QtRWzNhDwZXxHMNh+DSAdosyncZNiTf0p7ZRBqKgkXapIm2diVb2Zlg6rhEV3Ski2Ms=</DQ><InverseQ>XQJzVm1AFLJkbsrGb19MUOauBiDzlssX8VXT3A9gB0zsVclhgWpZMMKia1iAgrTg5nqhy2PIqvBJ9Vu3GGO6IP0lmLXwI83cOhso7AIz9dAknI4xR4LlLKtnbmw6ozwWMzxx+5fsKDbnTB5wiAIepZDXARdm9bmr791mXJXZQWA=</InverseQ><D>YbeygOaOAscVVED2FB6B5oa3Ww2jPV8BNX59VlTFO2udmr07k+mFYrIx/Z9HjkmakVO+kQVFkyuzOsD7GMEFclUSrYfeNsXZEhXwAXuMmbYMFgyPBEZ4+Eqgi/ZOmS+RxrFi1Mt6VD8jWGdHyabeCOdBpircbf9d8Q9ZL7eOx0tKAnn4QCcxxErDsRbAYLWKF8SClSGgEu2sfvBU/zMvJ2Lm6XTepItyfzWp+/eiN6ovg6OjuUtg4p7i8jEWP7M/LmLqAusjBtSve+0HF+d38DyD2kL6+ocIPwpXyuY5DjMazJtTCarmr+ryKkiUdFRtmwRf6XufFZSHrsEnmzEp0w==</D></RSAKeyValue>"
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xmlKey);
byte[] decryptedData = rsa.Decrypt(inputBuf, false);
You can see that each of the xml tags in the string account for each component of the key.
A public key consists of just a Modulus and an Exponent. Adding all the other components makes the key private. The values you set are missing some of the private components and is why PublicOnly is true. Just to avoid confusion the Public key is what encrypts data and the private key is the only way to decrypt it, which you would be using in your C# server code.
(The key in this example is 2048 just use 1024 for your use)

Computing md5 hash

I am computing md5hash of files to check if identical so I wrote the following
private static byte[] GetMD5(string p)
{
FileStream fs = new FileStream(p, FileMode.Open);
HashAlgorithm alg = new HMACMD5();
byte[] hashValue = alg.ComputeHash(fs);
fs.Close();
return hashValue;
}
and to test if for the beginning I called it like
var v1 = GetMD5("C:\\test.mp4");
var v2 = GetMD5("C:\\test.mp4");
and from debugger I listed v1 and v2 values and they are different !! why is that ?
It's because you're using HMACMD5, a keyed hashing algorithm, which combines a key with the input to produce a hash value. When you create an HMACMD5 via it's default constructor, it will use a random key each time, therefore the hashes will always be different.
You need to use MD5:
private static byte[] GetMD5(string p)
{
using(var fs = new FileStream(p, FileMode.Open))
{
using(var alg = new MD5CryptoServiceProvider())
{
return alg.ComputeHash(fs);
}
}
}
I've changed the code to use usings as well.
From the HMACMD5 constructor doc:
HMACMD5 is a type of keyed hash algorithm that is constructed from the
MD5 hash function and used as a Hash-based Message Authentication Code
(HMAC). The HMAC process mixes a secret key with the message data,
hashes the result with the hash function, mixes that hash value with
the secret key again, and then applies the hash function a second
time. The output hash will be 128 bits in length.
With this constructor, a 64-byte, randomly generated key is used.
(Emphasis mine)
With every call to GetMD5(), you're generating a new random key.
You might want to use System.Security.Cryptography.MD5Cng
My guess is that you did something like:
Console.WriteLine(v1);
Console.WriteLine(v2);
or
Console.WriteLine(v1 == v2);
That just shows that the variable values refer to distinct arrays - it doesn't say anything about the values within those arrays.
Instead, try this (to print out the hex):
Console.WriteLine(BitConverter.ToString(v1));
Console.WriteLine(BitConverter.ToString(v2))
Use ToString() methode to get the value of the array byte

RSA in C# public key is same as private key?

I searched a lot online but stuck with doubts in RSA public key and private key cryptography.
When I checked MSDN site, I tried this
RSACryptoServiceProvider rsaEncryptDecrypt = new RSACryptoServiceProvider();
byte[] privateKeyByte = rsaEncryptDecrypt.ExportParameters(true).Modulus;
byte[] publicKeyByte = rsaEncryptDecrypt.ExportParameters(false).Modulus;
string privateKey = Convert.ToBase64String(privateKeyByte);
string publicKey = Convert.ToBase64String(publicKeyByte);
The string public key and private key are Same!!!
Is it correct? I mean how can the strings be same? Isn't suppose to be two different keys?
Please correct me if I am wrong. I am confused !
Thank you in advance!
UPDATE
I mistook the parameters,
But then: When I saw
https://stackoverflow.com/questions/6592990/simple-rsa-encryption-decryption-in-net#answer-6593054"
How can I get string value? because I have to store it in App.config and access it whenever I want. I mean I need to store the public and private keys both in App.config
UPDATE2
I am sorry, I just used ToXmlString property of RSACryptoServiceProvider's instance.
Got the private key and public key.
The Modulus is the same for both.
The public key consists of the encryption exponent e and the modulus n.
Traditionally the decryption key consists of the decryption exponent d and the same modulus n. For better performance, it often includes some more numbers, such as the prime factors p and q of n.
To better visualize what a public key includes try ToXmlString(false/true)
Public key ToXmlString(false):
<RSAKeyValue>
<Modulus>4ZpwnuksQkLuqLczu5eJcS6aIFaPsTwGpS57/P9rviJWI7sweYZnE/eBVtPVKoanhgHxmcHyk4GbyvCnXKSzDw==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Public Key+Private Key ToXmlString(true):
<RSAKeyValue>
<Modulus>4ZpwnuksQkLuqLczu5eJcS6aIFaPsTwGpS57/P9rviJWI7sweYZnE/eBVtPVKoanhgHxmcHyk4GbyvCnXKSzDw==</Modulus>
<Exponent>AQAB</Exponent>
<P>8lLDYv+MEBUdp0eUY3an4mlU7ovmyV6f60RJoXOB9Hs=</P>
<Q>7lYYef5/PvPOyrN0HGZPt/RWknfVd4c3Kc6WVEZICX0=</Q>
<DP>UI3GufAthWMfmm4nG/Fj2dYeD7aeH66/BpyKxYr6VmU=</DP>
<DQ>sBZkFx30nWo8in5zdtgQZfTcUXLAAIOiOf0sDC+w4XE=</DQ>
<InverseQ>GBkNq0KZ4ERaEO/oVQoQDONw6ZHixNimR5IJ7cbzKXw=</InverseQ>
<D>ErLyUrmQ6Y0SqvlEWHAe/DqYm8WQ82e+RUKtFDM3gvK9ygloqftx6rhn9XvM/ji1JnrDqiuepn5T3D3F+3GVQQ==</D>
</RSAKeyValue>
Look at the documentation for RSAParameters - the public key is formed from {e, n} (Exponent and Modulus). The private key is formed from {d, n} (D and Modulus). so when you call ExportParameters(false) you would get the same Modulus as that's part of the public information - but you won't get a value for the D property.
Hmm, I guess my comment is a good answer after all:
As you only transformed the modulus part of the keys and this part is there in both privat and public key it's no wonder.
See here: http://en.wikipedia.org/wiki/RSA_%28algorithm%29#Key_generation

Using a previously generated RSA public/private key with the .net framework

I have a pre-existing public/private key pair for RSA encryption which I need to use in .net .
All the examples I can find online demonstrate how to generate a new private/public pair and then encrypt/decrypt. ie. something like this:
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "SpiderContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
rsa = new RSACryptoServiceProvider(cspParams);
.....
rsa.encrypt(...)
rsa.decrypt(...)
As can be seen, there is no avenue for specifying a pre-existing public/private key.
Would anyone know how to accomplish what I am trying to do?
Any help would be much appreciated.
Cheers
Naren
To use an existing key, you can use the ImportParameters-method:
RSAParameters parameters = new RSAParameters()
parameters.Modulus = // ...
parameters.Exponent = // ...
RSA rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(parameters);
rsa.Encrypt(/*...*/);
You can add the private parameters, too, in order to use it for decrypting or signing.
In order to tell you how to get from your existing keydata to the parameters, we need to know exactly how they are encoded. Try showing us the strings (replace most of the private key with Xs if it is a real key).
I realise this is a very old question but maybe someone still looks at this...
Nowadays you can retrieve/store your keys in XML format (which you possibly could back in the days too).
Example import:
this.RSAKey = RSA.FromXmlString(xmlString: myRSAXMLKey);
Example export:
this.RSAKey_XMLString = RSA.ToXmlString(includePrivateParameters: false);

Categories

Resources