I am using ECDSA with SHA1 encryption because I am trying to make a licencing activation for a desktop application. For that I use a PHP server to which I give PC information and the server gives me the public the key and then I want to validate the data in C#.
I generated this public key in PHP:
"-----BEGIN PUBLIC KEY-----
MDIwEAYHKoZIzj0CAQYFK4EEAAYDHgAEKzL3PFVVo3IWftdEYmwiSO/4zULGM/wB
8BrLjQ==
-----END PUBLIC KEY-----";
I used the code from here http://securitydriven.net/inferno/ To get to this
byte[] thePublicKeyToBytes = GetBytesFromPEM(thePublicKey2, "PUBLIC KEY");
CngKey dsaKeyPublic2 = thePublicKeyToBytes.ToPublicKeyFromBlob();
byte[] theRestToBytes = GetBytes(theRestInBinary);
byte[] meinData = GetBytes("Blabla");
using (var ecdsa = new ECDsaCng(dsaKeyPublic2) { HashAlgorithm = CngAlgorithm.Sha1 }) // verify DSA signature with public key
{
if (ecdsa.VerifyData(meinData, theRestToBytes)) MessageBox.Show("Signature verified.");
else MessageBox.Show("Signature verification failed.");
}
where the procedure is:
byte[] GetBytesFromPEM(string pemString, string section)
{
var header = String.Format("-----BEGIN {0}-----", section);
var footer = String.Format("-----END {0}-----", section);
var start = pemString.IndexOf(header, StringComparison.Ordinal) + header.Length;
var end = pemString.IndexOf(footer, start, StringComparison.Ordinal) - start;
if (start < 0 || end < 0)
{
return null;
}
return Convert.FromBase64String(pemString.Substring(start, end));
}
The problem is that I get this exception "cryptographicexception the parameter is incorrect" at this line:
CngKey dsaKeyPublic2 = thePublicKeyToBytes.ToPublicKeyFromBlob();
I can't show the inferno's public key, but I saw that the length of their key is 384. Is this where I am doing it wrong? The length of the generated public key?
Your public key is 52 bytes long - it is too short. How are you generating it?
The ToPublicKeyFromBlob() method is a shortcut for return CngKey.Import(byteArray, CngKeyBlobFormat.EccPublicBlob) - it works only on Ecc-based keys, and those generated by .NET. Inferno uses ECC keys over P384 curve, which means that each public key will have 48*2=96 bytes, plus 8 header bytes (as described here), for a total of 104 bytes.
Andrei, Inferno uses the NIST P-384 curve only. More importantly, the only curves supported by .NET framework (out-of-the-box) are P-256, P-384, and P-521.
Related
I want to create iothub device certificates from C# code. The root CA is stored in keyvault as a .pfx, fetched as a string, and then converted from base 64 in order to obtain the certificate bytes as it is required for a certificate stored in keyvault: Azure Key Vault Certificates does not have the Private Key when retrieved via IKeyVaultClient.GetCertificateAsync
I want to write a function that will take these bytes, along with a subject name (for the leaf certificate) and will create a x509 certificate (with both public and private keys) that would have the issuer as the root.
Here is what I have sketched so far:
public static X509Certificate2 GenerateCertificateBasedOnIssuer(string subjectName, byte[] issuerByteCert)
{
var issuerCertificate = new X509Certificate2(issuerByteCert);
RSA keyProvider = issuerCertificate.GetRSAPrivateKey();
CertificateRequest certificateRequest = new CertificateRequest($"CN={subjectName}", keyProvider, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
SecureRandom random = new SecureRandom(randomGenerator);
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
var publicOnlyDeviceCertificate = certificateRequest.Create(issuerCertificate, issuerCertificate.NotBefore, issuerCertificate.NotAfter, serialNumber.ToByteArray());
return publicOnlyDeviceCertificate; // oh no ! :(
}
The issue I am having with this solution is that the created certificate only contains a public key.
I found another solution that appears to solve my problem on another Stack Overflow question using BouncyCastle's X509V3CertificateGenerator: Generate a self-signed certificate on the fly
The issue I have with this solution is that I cannot convert my rootCA certificate's private key to an AsymmetricKeyParameter (first parameter of the X509V3CertificateGenerator.Generate method). I tried converting the issuer's key to AsymmetricKeyParameter using this solution: convert PEM encoded RSA public key to AsymmetricKeyParameter, but I got an invalid operation exception.
I was wondering if I was on the right path (as far as understanding goes) and if there is a way to generate a certificate with a private (and public key) based on the code I currently have in place.
UPDATE: I have been able to convert a private key to an AsymmetricKeyParameter by hardcoding the key as follows:
string testKey = #"-----BEGIN PRIVATE KEY-----
<THE KEY>
-----END PRIVATE KEY-----
";
var stringReader = new StringReader(testKey);
var pemReader = new PemReader(stringReader);
var pemObject = pemReader.ReadObject();
var keyParam = ((AsymmetricKeyParameter)pemObject);
Azure keyvault stores certificate in a pfx format. I am thinking of storing the private key as a secret string. I will keep testing with an hardcoded key for now until I get to a working solution.
I am now testing with BouncyCastle and will come back with a working solution if it works!
The key you pass to CertificateRequest is used as the public key in the cert... so you want to pass a new key, not the issuer's key.
Then, once you now have the subject key, you use CopyWithPrivateKey at the end to glue them back together.
public static X509Certificate2 GenerateCertificateBasedOnIssuer(string subjectName, byte[] issuerByteCert)
{
using (var issuerCertificate = new X509Certificate2(issuerByteCert))
using (RSA subjectKey = RSA.Create(2048))
{
CertificateRequest certificateRequest = new CertificateRequest($"CN={subjectName}", subjectKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
SecureRandom random = new SecureRandom(randomGenerator);
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random);
var publicOnlyDeviceCertificate = certificateRequest.Create(issuerCertificate, issuerCertificate.NotBefore, issuerCertificate.NotAfter, serialNumber.ToByteArray());
using (publicOnlyDeviceCertificate)
{
return publicOnlyDeviceCertificate.CopyWithPrivateKey(subjectKey);
}
}
I am learning about Cryptography in .NET and I wrote the following function as a test:
byte[] foo(byte[] input, string keyContainerName)
{
CngKey key = CngKey.Open(keyContainerName);
RSACng rsa = new RSACng(key);
rsa.KeySize = 2048;
byte[] v = rsa.Encrypt(input, RSAEncryptionPadding.OaepSHA512);
CngKey keyb = CngKey.Open(keyContainerName);
RSACng rsab = new RSACng(keyb);
rsab.KeySize = 2048;
return rsab.Decrypt(v, RSAEncryptionPadding.OaepSHA512);
}
When I try executing it, rsab.Decrypt() throws a Cryptographic exception with the message: "The parameter is incorrect.".
Why is this happening? Where did I go wrong?
P.S. I previously created a key pair in the KSP with CngKey.Create(). foo is called with keyContainerName beeing the keyName passed to CngKey.Create().
If you want to create an app that does symmetric and asymmetric encryption and decryption, You can try integrating ExpressSecurity library via NuGet
More info: https://github.com/sangeethnandakumar/Express-Security-Library
AES - Symetric Encryption (For files)
var password = "sangeeth123";
var inputPath = "C:\sample.txt";
var outputPath = "C:\sample.txt.aes";
//AES Encription
AESEncription.AES_Encrypt(inputPath, password);
//AES Description
AESEncription.AES_Decrypt(outputPath, password);
RSA - Asymmetric Encryption (For strings and text)
//Generate Keys
var publicKeyPath = "C:\public_key.rsa";
var privateKeyPath = "C:\private_key.rsa";
RSAEncription.MakeKey(publicKeyPath, privateKeyPath);
var input = "sangeeth"
//RSA Encription
var ciphertext = RSAEncription.EncryptString(input, publicKeyPath);
//RSA Description
input = RSAEncription.DecryptString(ciphertext, privateKeyPath);
I'm running this code successfully on my Windows machine (Win 10 x64, running dotnet 4.7.2). It generates an EC keypair ("P-256"), hashes the plaintext with SHA-256, signs the hash with the ec private key and verifies the signature against the hashed plaintext with the ec public key.
I'm getting this output so everything works fine:
EC signature curve secp256r1 / P-256 string
dataToSign: The quick brown fox jumps over the lazy dog
* * * sign the plaintext with the EC private key * * *
EC keysize: 256
signature (Base64): cwLBRSt1vtO33tHWcTdx1OTu9lBFXHEJgvdRyDUynLLE5eMakUZUAKLwaJvYoS7NBylx2Zz0+G6dvgJ6xv5qNA==
* * *verify the signature against hash of plaintext with the EC public key * * *
signature verified: True
Now I'm trying to find any online compiler that is been able to run the code. My favorite compiler
(https://repl.it/, Mono C# compiler version 6.8.0.123, full code: https://repl.it/#javacrypto/EcSignatureFull#main.cs) is running into this error:
Unhandled Exception:
System.NotImplementedException: The method or operation is not implemented.
at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.NotImplementedException: The method or operation is not implemented.
at EcSignatureString.Main () [0x00036] in <13e2ad358a924efc874a89efad35ffe7>:0
exit status 1
Using another platform (https://dotnetfiddle.net/, Compiler .net 5, full code: https://dotnetfiddle.net/lSPpjz) is giving this similar error:
Unhandled exception. System.PlatformNotSupportedException: Windows Cryptography Next Generation (CNG) is not supported on this platform.
at System.Security.Cryptography.ECDsaCng..ctor(Int32 keySize)
at EcSignatureString.Main()
Command terminated by signal 6
So my question: is there any online compiler available that is been able to run the code?
I assume my question might be a slice off-topic for SO - in this case - is there any other stackexchange-site that would be a better place for my question?
Warning: the following code has no exception handling and is for educational purpose only:
using System;
using System.Security.Cryptography;
class EcSignatureString {
static void Main() {
Console.WriteLine("EC signature curve secp256r1 / P-256 string");
string dataToSignString = "The quick brown fox jumps over the lazy dog";
byte[] dataToSign = System.Text.Encoding.UTF8.GetBytes(dataToSignString);
Console.WriteLine("dataToSign: " + dataToSignString);
try {
Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");
ECDsaCng ecDsaKeypair = new ECDsaCng(256);
Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);
byte[] hashedData = null;
byte[] signature = null;
// create new instance of SHA256 hash algorithm to compute hash
HashAlgorithm hashAlgo = new SHA256Managed();
hashedData = hashAlgo.ComputeHash(dataToSign);
// sign Data using private key
signature = ecDsaKeypair.SignHash(hashedData);
string signatureBase64 = Convert.ToBase64String(signature);
Console.WriteLine("signature (Base64): " + signatureBase64);
// get public key from private key
string ecDsaPublicKeyParametersXml = ecDsaKeypair.ToXmlString(ECKeyXmlFormat.Rfc4050);
// verify
Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");
ECDsaCng ecDsaVerify = new ECDsaCng();
bool signatureVerified = false;
ecDsaVerify.FromXmlString(ecDsaPublicKeyParametersXml, ECKeyXmlFormat.Rfc4050);
signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
Console.WriteLine("signature verified: " + signatureVerified);
}
catch(ArgumentNullException) {
Console.WriteLine("The data was not signed or verified");
}
}
}
Microsoft has decided that encryption and hashing must be fully delegated to the OS (in .NET Framework it was half and half), so now .NET 5 (and .NET Core) has multiple backends for encryption (for example for ECDsa it has ECDsaCng that uses Windows services and ECDsaOpenSsl for Linux/MacOs that uses OpenSsl (see MSDN)
Now... the solution for your problem is to use the ECDsa class and let it select its backend. There are some problems with it. You can't easily export the keys to xml format, nor you can easily export them to PEM format. You can easily export them to a byte[], and you can easily import them from PEM format. This isn't really a big problem, because rarely you'll need to generate keys, and normally your program receives its keys from an external source, or if it generates them itself, it can save them to binary format to reuse them later.
var dataToSignString = "Hello world!";
var dataToSign = Encoding.UTF8.GetBytes(dataToSignString);
Console.WriteLine("dataToSign: " + dataToSignString);
try
{
Console.WriteLine("\n* * * sign the plaintext with the EC private key * * *");
var ecDsaKeypair = ECDsa.Create(ECCurve.NamedCurves.nistP256);
// Normally here:
//ecDsaKeypair.ImportFromPem()
Console.WriteLine("EC keysize: " + ecDsaKeypair.KeySize);
byte[] hashedData = null;
byte[] signature = null;
// create new instance of SHA256 hash algorithm to compute hash
HashAlgorithm hashAlgo = new SHA256Managed();
hashedData = hashAlgo.ComputeHash(dataToSign);
// sign Data using private key
signature = ecDsaKeypair.SignHash(hashedData);
string signatureBase64 = Convert.ToBase64String(signature);
Console.WriteLine("signature (Base64): " + signatureBase64);
// get public key from private key
string ecDsaPublicKeyParameters = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());
// verify
Console.WriteLine("\n* * *verify the signature against hash of plaintext with the EC public key * * *");
var ecDsaVerify = ECDsa.Create(ECCurve.NamedCurves.nistP256);
bool signatureVerified = false;
// Normally here:
//ecDsaKeypair.ImportFromPem()
var publicKey = Convert.FromBase64String(ecDsaPublicKeyParameters);
ecDsaVerify.ImportSubjectPublicKeyInfo(publicKey, out _);
signatureVerified = ecDsaVerify.VerifyHash(hashedData, signature);
Console.WriteLine("signature verified: " + signatureVerified);
}
catch (ArgumentNullException)
{
Console.WriteLine("The data was not signed or verified");
}
About the From/ToXmlFormat, the current comment on them on the github of .NET Core is:
// There is currently not a standard XML format for ECC keys, so we will not implement the default
// To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
// use an overload which allows the user to specify the format they'd like to serialize into.
Mmmh from some tests done, exporting in PEM format seems to be quite easy:
public static IEnumerable<string> Split(string str, int chunkSize)
{
for (int i = 0; i < str.Length; i += chunkSize)
{
yield return str.Substring(i, Math.Min(chunkSize, str.Length - i));
}
}
and then
string b64privateKey = Convert.ToBase64String(ecDsaKeypair.ExportPkcs8PrivateKey());
b64privateKey = string.Join("\r\n", Split(b64privateKey, 64));
string pemPrivateKey = "-----BEGIN PRIVATE KEY-----\r\n" + b64privateKey + "\r\n-----END PRIVATE KEY-----";
or
string b64publicKey = Convert.ToBase64String(ecDsaKeypair.ExportSubjectPublicKeyInfo());
b64publicKey = string.Join("\r\n", Split(b64publicKey, 64));
string pemPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" + b64publicKey + "\r\n-----END PUBLIC KEY-----";
(note that I had to split the string manually in blocks of 64 characters, that is the exact number given in the rfc7468, because Convert.ToBase64String() supports only the 76 line length)
I'm trying to encrypt some data in Mono C#, send it to a NodeJS server and decrypt it there. I'm trying to figure out what algorithms to use to match the two.
I send the encrypted string encoded with base64. So I do something like this in Javascript, where I know the key which was used to encrypt the data in my C# application:
var decipher = crypto.createDecipher('aes192',binkey, biniv);
var dec = decipher.update(crypted,'base64','utf8');
dec += decipher.final('utf8');
console.log("dec", dec);
In Mono I create my Cypher with:
using System.Security.Cryptography;
using (Aes aesAlg = Aes.Create("aes192"))
I need to pass the correct string to Aes.Create() in order to have it use the same algorithm, but I can't find what it should be. "aes192" is not correct it seems.
I don't need aes192 this was just a tryout. Suggest a different encryption flavor if it makes sense. Security is not much of an issue.
Here are links to .NET and Nodejs docs:
http://msdn.microsoft.com/en-us/library/system.security.cryptography.aes.aspx
http://nodejs.org/api/crypto.html
This code works for my Node.js side, but please replace the static iv, otherwhise aes encryption would be useless.
var crypto = require('crypto');
function encrypt(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
cipher = crypto.createCipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
cipher.update(data.toString(), 'utf8', 'base64');
return cipher.final('base64');
}
function decipher(data, key) {
key = key || new Buffer(Core.config.crypto.cryptokey, 'binary'),
decipher = crypto.createDecipheriv('aes-256-cbc', key.toString('binary'), str_repeat('\0', 16));
decipher.update(data, 'base64', 'utf8');
return decipher.final('utf8');
}
function str_repeat(input, multiplier) {
var y = '';
while (true) {
if (multiplier & 1) {
y += input;
}
multiplier >>= 1;
if (multiplier) {
input += input;
} else {
break;
}
}
return y;
}
I hope this helps You.
NOTE: You need to deliver an 265bit aka 32 character key for this algorithm to work.
POSSIBLE .NET SOLUTION: This may help you Example
You should simply write new AesManaged().
You don't need to call Create().
You then need to set Key and IV, then call CreateDecryptor() and put it in a CryptoStream.
It turned out to be a stupid mistake. I thought the create function in Node.js could take a variable argument count. Turns out you need to call the createDecipheriv() instead.
Just for the record, you can easily check the padding and mode by looking at those properties in the Aes object. The defaults are CBC and PKCS7. That padding is also used in nodejs crypto. So a for a 128 key size my code to decrypt a base64 encoded string would be:
var crypto = require('crypto');
var binkey = new Buffer(key, 'base64');
var biniv = new Buffer(iv, 'base64');
var decipher = crypto.createDecipheriv('aes-128-cbc', binkey, biniv);
var decrypted = decipher.update(crypted,'base64','utf8');
decrypted += decipher.final('utf8');
console.log("decrypted", decrypted);
I am working on a project where I need to use a "public key" to encrypt a message using RSA algorithm. I was provided with a certificate and my first thought was to use Public Key from that certificate and after investigation I learned I need to use RSACryptoServiceProvider for encryption.
I have checked msdn and only method I thought I should use is RSACryptoServiceProvider.ImportCspBlob(byte[] keyBlob).
When I tried to use public key exported from certificate I was getting an error that the header data for certificate is invalid.
I know I can cast X509certificate2.PublicKey.Key to RSACryptoServiceProvider but from what I understood from my client is that going forward I will be given only a public key and not the certificate. This key will have to be saved in .xml configuration file.
So to summarize: Is there a way to generate an RSACryptoServiceProvider given only a certificate's public key?
You can try to look at this example: RSA public key encryption in C#
var publicKey = "<RSAKeyValue><Modulus>21wEnTU+mcD2w0Lfo1Gv4rtcSWsQJQTNa6gio05AOkV/Er9w3Y13Ddo5wGtjJ19402S71HUeN0vbKILLJdRSES5MHSdJPSVrOqdrll/vLXxDxWs/U0UT1c8u6k/Ogx9hTtZxYwoeYqdhDblof3E75d9n2F0Zvf6iTb4cI7j6fMs=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var testData = Encoding.UTF8.GetBytes("testing");
using ( var rsa = new RSACryptoServiceProvider(1024))
{
try
{
// client encrypting data with public key issued by server
//
rsa.FromXmlString(publicKey);
var encryptedData = rsa.Encrypt(testData, true);
var base64Encrypted = Convert.ToBase64String(encryptedData);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
You are OK and following a good typical pattern. The Sender of the data does not need the private key.
The following may confirm some of the code you already have figured out.
The one line where I set the private key for the receiver/decoder I left out.
I took this from a test case I have in my build deploy stuff.
byte[] certBytAr; // This is the certificate as bianry in a .cer file (no private key in it - public only)
X509Certificate2 cert2 = new X509Certificate2(certBytAr);
string strToEncrypt = "Public To Private Test StackOverFlow PsudeoCode. Surfs Up at Secret Beach.";
byte[] bytArToEncrypt = Encoding.UTF8.GetBytes(strToEncrypt);
RSACryptoServiceProvider rsaEncryptor = (RSACryptoServiceProvider)cert2.PublicKey.Key;
byte[] dataNowEncryptedArray = rsaEncryptor.Encrypt(bytArToEncrypt, true);
// done - you now have encrypted bytes
//
// somewhere elxe ...
// this should decrpyt it - simulate the destination which will decrypt the data with the private key
RSACryptoServiceProvider pk = // how this is set is complicated
// set the private key in the x509 oobject we created way above
cert2.PrivateKey = pk;
RSACryptoServiceProvider rsaDecryptor = (RSACryptoServiceProvider)cert2.PrivateKey;
byte[] dataDecrypted = rsaDecryptor.Decrypt(dataNowEncryptedArray, true);
Console.WriteLine(" encrypt 1 Way Intermediate " + BitConverter.ToString(dataDecrypted));
string strDecodedFinal = Encoding.UTF8.GetString(dataDecrypted);
if (strDecodedFinal == strToEncrypt)
{
}
else
{
Console.WriteLine(" FAILURE OF ENCRYPTION ROUND TRIP IN SIMPLE TEST (Direction: Public to Private). No Surfing For You ");
}