I need to convert a EC private key generated by BouncyCastle to a CngKey in C#. Ultimately, I'm trying to create a PKCS12 that can be imported into the Windows Key Store and am following the information and code example found here.
The EC key pair is generated as follows:
var ecKeyPairGenerator = new ECKeyPairGenerator("ECDSA");
ECKeyGenerationParameters ecKeyGenParams = new ECKeyGenerationParameters(SecObjectIdentifiers.SecP384r1, new SecureRandom());
AsymmetricCipherKeyPair pair = ecKeyPairGenerator.GenerateKeyPair();
To create a CngKey:
PrivateKeyInfo privKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
CngKey cngPrivKey = CngKey.Import(privKeyStruct.GetDerEncoded(), CngKeyBlobFormat.Pkcs8PrivateBlob);
Searching on the web, the above should work, e.g., see here. Instead, I'm getting an Unknown error exception
(CryptographicException) at
System.Security.Cryptography.NCryptNative.ImportKey(). If I pass
CngKeyBlobFormat.EccPrivateBlob to CngKey.Import(), I get an
invalid data exception.
As a new newbie to both .NET, CNG, and Cryto, I feel I'm overlooking something. Any ideas would be appreciated.
Thanks!
Related
I am working on an application where I need to encrypt plain text using the RSA algorithm. I encrypt the plain text but it is not working as it gives Error Decoding Text. Basically, I am calling third-party API which gives me the error. When I encrypt my text using this link reference link it works perfectly fine so I think I am doing something wrong. Here is my code
public static string Encryption(string strText)
{
var publicKey = #"<RSAKeyValue><Modulus>MIIDSjCCAjKgAwIBAgIEWrJUKTANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJE
RTEPMA0GA1UECAwGQmF5ZXJuMQ8wDQYDVQQHDAZNdW5pY2gxDzANBgNVBAoMBkxl
eGNvbTEkMCIGA1UEAwwbQWdyb3BhcnRzX0RNU19CYXNrZXRfVXBsb2FkMCAXDTE4
MDMyMTEyNDYzM1oY################################################
A1UECAwG########################################################
################################################################
WaOa0parvIrMk9/#################################################
NCIeGu+epwg8oUCr6Wd0BNATNjt8Tk64pgQvhdX9/KRDSC8V4QCJBiE3LQPHUVdN
nWRixrcOpucMo6m9PPegjnicn/rBKdFZLfJqLHHm+TrHrNCsEQIDAQABMA0GCSqG
SIb3DQEBCwUAA4IBAQBGwlNnDh2UaZphkEf70MPhySFVnTnLSxUFuwuWaDu8l7YP
zBMeJxcNk3HNiXPeba03GQBj+JqGAwDALJLityGeGEzlESfv/BsgQOONt+lAJUjs
b7+vr2e5REE/dpJZ1kQRQC##########################################
np+GstsdWjIWbL6L6VoqU18qLO5b0k8OoEMsP3akUTcj0w8JwD5V5iLqDhnv1aXK
kntkd/QmVCY6zlzH/dnTh8RNO2CfRtB1GEzNnkJB</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var testData = Encoding.UTF8.GetBytes(strText);
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
rsa.FromXmlString(publicKey);
byte[] data = Encoding.UTF8.GetBytes(strText);
byte[] cipherText = rsa.Encrypt(data,true);
var base64Encrypted = Convert.ToBase64String(cipherText);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
}
}
Here is my public key. I am using an RSA certificate. I am passing the certificate key to the module tag here is my key. I think I might be using it wrong.
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIEWrJUKTANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJE
RTEPMA0GA1UECAwGQmF5ZXJuMQ8wDQYDVQQHDAZNdW5pY2gxDzANBgNVBAoMBkxl
eGNvbTEkMCIGA1UEAwwbQWdyb3BhcnRzX0RNU19CYXNrZXRfVXBsb2FkMCAXDTE4
MDMyMTEyNDYzM1oY################################################
A1UECAwG########################################################
################################################################
WaOa0parvIrMk9/#################################################
NCIeGu+epwg8oUCr6Wd0BNATNjt8Tk64pgQvhdX9/KRDSC8V4QCJBiE3LQPHUVdN
nWRixrcOpucMo6m9PPegjnicn/rBKdFZLfJqLHHm+TrHrNCsEQIDAQABMA0GCSqG
SIb3DQEBCwUAA4IBAQBGwlNnDh2UaZphkEf70MPhySFVnTnLSxUFuwuWaDu8l7YP
zBMeJxcNk3HNiXPeba03GQBj+JqGAwDALJLityGeGEzlESfv/BsgQOONt+lAJUjs
b7+vr2e5REE/dpJZ1kQRQC##########################################
np+GstsdWjIWbL6L6VoqU18qLO5b0k8OoEMsP3akUTcj0w8JwD5V5iLqDhnv1aXK
kntkd/QmVCY6zlzH/dnTh8RNO2CfRtB1GEzNnkJB
-----END CERTIFICATE-----
Any help would be highly appreciated. The encryption through this code is not working. But when I used the mentioned link above and pass this key it worked fine.
The answer to my question is here. I solved my problem and I am posting it because maybe someone in the future will have the same issue I am facing and what mistake I did to achieve my requirements.
Findings
I found during my research there is a difference between Public Key and Certificate. I miss understood the terminology I was passing a certificate instead of passing Public Key for encryption. So one of the community members #Topaco basically redirected me to the correct path which helps me to solve my problem. There are steps involved if you have a public key then you can achieve encryption but if you have a certificate then first you need to get the public key by using the method GetRSAPublicKey. When you got your public key in XML form then you pass it to encrypt method to get your result.
Here is the coding
Program.cs
var x509 = new X509Certificate2(File.ReadAllBytes(#"D:\xyz.cer"));
string xml = x509.GetRSAPublicKey().ToXmlString(false);
var result = EncryptUtil.Encryption("start01!", xml);
Utility Class
public static string Encryption(string strText, string publicKey)
{
using (var rsa = new RSACryptoServiceProvider(1024))
{
try
{
rsa.FromXmlString(publicKey);
byte[] data = Encoding.UTF8.GetBytes(strText);
byte[] cipherText = rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
var base64Encrypted = Convert.ToBase64String(cipherText);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
So you can achieve encryption using the above code you need to pass RSAEncryptionPadding.Pkcs1 for encryption.
#happycoding #keephelping
I've been give a private key, public key and a certificate to try and generate a signature for an SSO application, I've been struggling with this for a while now and I've think I've finally managed to get some code close to working as needed. I made a post a while back here: iDP connecting to SP SAML / SSO which has helped me get in the right direction.
However I am still unsure on how I meant to be signing this signature, all the code I seem to come by says I need to use the .Net class X509Certificate which usually tried to load in another file, however the certificate is in the assertion file itself.
<ds:X509Data>
<ds:X509Certificate>X509Certificate Goes Here</ds:X509Certificate>
</ds:X509Data>
I have this method here:
private SignedXml SignSignature(XmlDocument assertionDoc, string uri, string digest)
{
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "XML_DSIG_RSA_KEY";
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
SignedXml signedXml = new SignedXml(assertionDoc);
Reference reference = new Reference();
signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;
signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA1Url;
reference.Uri = uri;
reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
reference.AddTransform(new XmlDsigExcC14NTransform());
reference.DigestMethod = SignedXml.XmlDsigSHA1Url;
reference.DigestValue = Encoding.ASCII.GetBytes(digest);
signedXml.AddReference(reference);
signedXml.SigningKey = rsaKey;
HMACSHA256 key = new HMACSHA256(Encoding.ASCII.GetBytes(PrivateKey));
signedXml.ComputeSignature(key);
return signedXml;
}
Which is what I am using to sign SignatureValue of the document, however I am only making use of the private key in the SHA256 class and not the certificate, I'm not even sure if I am using the private key correctly, overall I think I am making this more complicated than it needs to be and hopefully someone from here can assist me.
When you sign an assertion, you only need to use the private key of a certificate which is the case of your code.
However, usually the public key of the certificate is inserted as keyinfo into the signature to notify a receiver. This ends up as the ds:X509Data section you mentioned above. In order to do that, you need to add some more lines of code into the method above. You can find sample code at: https://github.com/Safewhere/CHTestSigningService/blob/86a66950d1ffa5208b8bf80d03868a073ba29f12/Kombit.Samples.CHTestSigningService/Code/TokenSigningService.cs#L344 (notice line 361, 362, and 368).
I'm provided a private key (a string). I have to generate a public key by that private key to encrypt data.
I don't know how to do. Please help me. Thank you.
Simply by having the private key you can not generate a public key.
Private and public keys are generated in pair and should be provided to you for encrypting data.
However you still can sign data using private key alone.
var keypair = "Your keypair in xml format";
using (var rsa = new RSACryptoServiceProvider()) {
rsa.FromXmlString(keypair);
var publicKeyInXmlFormat = rsa.ToXmlString(false);
}
I want apply the RSA encryption to my project, but encountered some troubles:
First, I have download the JavaScripts library from
http://www.ohdave.com/rsa/ ,and add reference to my project;
Second, I have define the RSA object and code to initialize that:
internal RSACryptoServiceProvider Rsa
{
get
{
if (HttpContext.Cache["Rsa"] != null)
{
RSACryptoServiceProvider encryptKeys = (RSACryptoServiceProvider)HttpContext.Cache["Rsa"];
return encryptKeys;
}
else
{
return new RSACryptoServiceProvider(1024);
}
}
set
{
HttpContext.Cache.Remove("Rsa");
HttpContext.Cache.Insert("Rsa", value);
}
}
public ActionResult SignUp()
{
this.Rsa = Security.GetRsa();
RSAParameters param= this.Rsa.ExportParameters(true);
//this will bind to view
TempData["exponent"] = Util.BytesToHexString(param.Exponent);
TempData["key"] = Util.BytesToHexString(param.Modulus);
UserInfo user = new UserInfo();
user.Birthday = DateTime.Now.Date;
return View(user);
}
private RSACryptoServiceProvider GetRsa()
{
RSACryptoServiceProvider Rsa = new RSACryptoServiceProvider(1024);
return Rsa;
}
3.then, on JavaScript side , I have code, it encrypt the password user input and the bind it control:
var hash = document.getElementById("Pwd").value;
var exponent = document.getElementById("exponent").innerHTML;
var rsa_n = document.getElementById("key").innerHTML;
setMaxDigits(131);
var key = new RSAKeyPair(exponent, "", rsa_n);
hash = encryptedString(key, "111");
document.getElementById("Pwd").value = hash;
document.getElementById("Pwd2").value = hash;
document.getElementById("error").innerHTML = "";
document.getElementById("submit").click();
4.when user click submit, my C# code get the encrypted pwd string and try to decrypt it but failed with exception: bad data:
[HttpPost]
public ActionResult SignUp(UserInfo user)
{
user.UserId = user.UserId.ToLower(); //ignore case
user.UserGUID = Guid.NewGuid();
user.CreatedDate = DateTime.Now;
user.IsEnabled = false;
user.Pwd = Convert.ToBase64String(Rsa.Decrypt(Util.HexStringToBytes(user.Pwd), false));//Exception:Rsa.Decrypt throw bad data exception
who do you know how to fix it? thank you in advance.
I had a very similar problem in that most of the JavaScript based RSA encryption solutions wasn't "compatible" with .NET's implementation.
Almost all the implementations I found online had one or both of the following items causing the incompatibility with .NET's implementation.
The byte order encoding in JavaScript is different to the byte order that .NET used. This is a biggie as for example a string is represented with a different order of bytes in JS than it is in .NET so you'll need to convert before encrypting and after decrypting. I believe it's enough to just reverse the byte order to match .NET, but don't quote me on that.
Padding was different: .NET uses OAEP padding by default for RSA so the JS implementation of RSA should support the same padding too. I believe OAEP padding is also called PKCS#1 v2.0 padding, but don't quote me on that either.
Aside: I found an amazing JS library, called JavaScript.NET (from jocys.com) that mirrors tons of the .NET BCL functionality, including the RSA implementation, such that I could even use similar classes, properties and methods. Have a look at this. I can confirm it works with .NET RSA implementation. Give it a go - here are some links for it:
Jocys JS.NET Code Project demo
Jocys JS.NET Download
Hth
I'm trying to asymetrically encrypt a message of arbitrary length with bouncycastle. (1.4+ with C#)
This is the code I have right now. It is supposed to (but doesn't) generate a CMS message where the data itself is encrypted with AES256 with a random key and the key is encrypted with the public key from keyPair.
keyPair is an RSA-Key (RsaKeyParameters)
public static byte[] Encrypt(byte[] input, AsymmetricCipherKeyPair keyPair)
{
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
// those two lines are certainly wrong.
// I have no idea what the subKeyID parameter does
byte[] subKeyId = new byte[] {};
generator.AddKeyTransRecipient(keyPair.Public, subKeyId);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(input);
CmsEnvelopedData envelopeData =
generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
return envelopeData.GetEncoded();
}
What is the subKeyId parameter in the Encrypt method for and what value does it need to have?
aaronls is being a little unfair to the author of "Beginning cryptography with Java", who after all wrote all the unit tests himself in the first place...
As other commenters have pointed out, CMS works with certificates, you can't just pass a public key; it must be possible to refer to the key either by "SubjectKeyIdentifier" or by "IssuerAndSerialNumber". The two alternatives of AddKeyTransRecipient allow this. If these terms don't mean anything to you, you probably need to do some background reading on X.509.
Look at the function TryKekAlgorithm in the EnvelopedDataTest.cs file of the BouncyCastle source. Instead of doing AddKeyTransRecipient, they are doing AddKekRecipient.
public static byte[] Encrypt(byte[] input, AsymmetricCipherKeyPair keyPair)
{
CmsEnvelopedDataGenerator generator = new CmsEnvelopedDataGenerator();
DerObjectIdentifier algOid = //initialize
//Still trying to figure out kekId here.
byte[] kekId = new byte[] { 1, 2, 3, 4, 5 };
string keyAlgorithm = ParameterUtilities.GetCanonicalAlgorithmName("AES256");
generator.AddKekRecipient(keyAlgorithm, keyPair.Public, kekId);
CmsProcessableByteArray cmsByteArray = new CmsProcessableByteArray(input);
CmsEnvelopedData envelopeData =
generator.Generate(cmsByteArray, CmsEnvelopedDataGenerator.Aes256Cbc);
return envelopeData.GetEncoded();
}
Edit: I think the kekId is just a unique identifier used to reference the key. Just a way to "name" the key. So you can have a book of keys, and each one has an identifier. When you send an encrypted message, the unencrypted key identifier tells you which of the keys was used to encrypt the message.
Here is a pretty good explanation of key identifiers on page 140:
[http://books.google.com/books?id=Pgg-Es2j3UEC&pg=PA140&lpg=PA140&dq=understanding+key+identifiers+encrypt&source=bl&ots=nFg0BzM2ht&sig=Ux5sreXMKyuEEZu0uaxE7cXC1VI&hl=en&ei=JKKJStbHGJivtgffsNznDA&sa=X&oi=book_result&ct=result&resnum=6#v=onepage&q=&f=false][1]
And here is another book that is using BouncyCastleCrypto, but it looks like they did little more than rip off the unit test source code. They have explained it a little:
[http://books.google.com/books?id=WLLAD2FKH3IC&pg=PA343&lpg=PA343&dq=CmsEnvelopedDataGenerator+AddKekRecipient&source=bl&ots=O9HinJm3yB&sig=K5Z99DIVWW4-0abPIFR7x4lzBhU&hl=en&ei=g6aJSrjeDuHktgennNjnDA&sa=X&oi=book_result&ct=result&resnum=6#v=onepage&q=CmsEnvelopedDataGenerator%20AddKekRecipient&f=false][2]
To use AES, it is not enough to use a AsymmetricCipherKeyPair.
You should use a X509 certificate, where the public key is signed by an certificate authority (CA).
the subKeyId is an attribute of the certificate, the subject Key Identifier:
(X509Certificate) cert.getSubjectUniqueID()
To encrypt a message of artrary length, you should use AES only to exchange a symmetric Keypassword and use this key for symmetric encryption.