C# Signature Matching with Public Key - c#

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

Related

Loading EC keys in PEM format into an .Net Fx 4.8 application

I am trying to load an EC Private key from base64 string obtained from PEM file which was generated with openssl into a netfx4.8 application this is later used to create ECDsa key object.
I have noticed that we have a PEM reader in .Net5 so what are my options while staying in .Net4.8.
To make it clear we create keys with following openssl command
openssl ecparam -out ecc_private_key.key -name secp256r1 -genkey
which gives output into following format (This is an example key only - not used anywhere)
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBfBTJGdjFUK2S0bTBWfogeRElTF52jjVjeSs2GvsIrvoAoGCCqGSM49
AwEHoUQDQgAEQEyYxahMyU6H7i4GkX0pzOtgNiJrwxLGSiLSgBgFjV/VfGIUjrQl
ia6+KPlQJd0IRtQLIG0kqXeD/yyeikuYBw==
-----END EC PRIVATE KEY-----
So we like to inject base64 contents available within "EC PRIVATE KEY" header/footer into the application.
Current workaround
Currently I am using following command to parse the contents and then injecting Private and Public key separately. this helps to remove unwanted headers available in PEM contents.
$ openssl ec -in ecc_private_key.key -noout -text
read EC key
Private-Key: (256 bit)
priv:
17:c1:4c:91:9d:8c:55:0a:d9:2d:1b:4c:15:9f:a2:
07:91:12:54:c5:e7:68:e3:56:37:92:b3:61:af:b0:
8a:ef
pub:
04:40:4c:98:c5:a8:4c:c9:4e:87:ee:2e:06:91:7d:
29:cc:eb:60:36:22:6b:c3:12:c6:4a:22:d2:80:18:
05:8d:5f:d5:7c:62:14:8e:b4:25:89:ae:be:28:f9:
50:25:dd:08:46:d4:0b:20:6d:24:a9:77:83:ff:2c:
9e:8a:4b:98:07
ASN1 OID: prime256v1
NIST CURVE: P-256

Loading an ECC private key in .NET

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.

How do I create/export to a .PVK file using C#?

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.

RSACryptoServiceProvider decrypting with ECB and MGF1 padding

I'm using RSACryptoServiceProvider from System.Security.Cryptography package. I need to decrypt some input coming from Android application using my private key. It's encrypting it in Java code with RSA/ECB/OAEPWithSHA256AndMGF1Padding algorithm. The problem is I cannot find any way to set nither padding MGFT1 nor ECB.
Could you help me somehow? Are these settings default or should I use another library? It's hard to believe it's impossible.
Crucial part of my code goes here:
RSAParameters RSAParamPrivateKey = DotNetUtilities.ToRSAParameters(privateKey)
RSACryptoServiceProvider csp = new RSACryptoServiceProvider();
csp.ImportParameters(RSAParamPrivateKey);
return csp.Decrypt(encryptedAESKey, RSAEncryptionPadding.OaepSHA256);
The "ECB" is (hopefully) meaningless. RSA should only ever be used for a one block of data.
MGF1 is the only standard defined MGF, so it's not an option that .NET lets you currently specify.
OAEP with an algorithm other than SHA-1 is beyond RSACryptoServiceProvider's capabilities. But RSACng can do it:
RSAParameters RSAParamPrivateKey = DotNetUtilities.ToRSAParameters(privateKey);
RSA rsa = new RSACng();
rsa.ImportParameters(RSAParamPrivateKey);
return rsa.Decrypt(encryptedAESKey, RSAEncryptionPadding.OaepSHA256);

Cannot Load private key from a RSA PRIVATE KEY file in C# [duplicate]

This question already has answers here:
how to get private key from PEM file?
(7 answers)
Signing and verifying signatures with RSA C#
(1 answer)
Closed 5 years ago.
I want to sign some data with RSA.SignHash().
I've created a RSA private key file via the following command in Linux OpenSSL.
openssl genrsa -out private_key.pem 1024
But when I want to load this key by an object of X509Certificate2, get the "Cannot find the requested object." exception.
Here is my code:
string keyFilePath = #"C:\Keys\private_key.pem";
X509Certificate2 cert = new X509Certificate2(keyFilePath);
I've used the Chilkat library and everything works fine, But doesn't Microsoft have any walk-though to approach it without any third-party tools?
Here is my pem file content:
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC23ivEPgSqxCcSTNUY8IbjCO0+0FwarI/azCHp97cWCBaosFAe
mN9eI7u3agv2tCk+QrKrFDlkYWskFiADm55NMvDE1fuyy7db84MMh+GFcyemYZ0o
yG5oBYPw7aNY7N7fBO5pbF9M/v4TMjKoxYGxk2kl58KT9cmvVH7TaF8xkwIDAQAB
AoGAfVzRpDbf/DF8l48Uh4Rc9EeqXOV6Ps8Nz3EhzsODQBdLcVltk4w5lM/qYFLS
4M2heI1A7wduUOJ4EMUaLV8BpcFkA2kSPah48R/1EGAMOpaD09j8qHJLCHJ2uiSc
Hqi6z/6GhpSLzU5XR1lj0UIPy9aC9X0yPAao0WZ+5YgTdAECQQDoCwCmFD5r00Y0
DcQGbAU/eekHJwNk48ACvIDquK/ysqA8aXvWgyXd3oa82fUSvSAMDGNFYr9xOxqF
Vnnsi6VXAkEAyb9yymbehjwwl33CjPcsIRbjetLDwPgWhVYii4q7jELlefyC8mHL
cpn0ejS4ln/uTUuNZCdYNHvYjQ8eXfaUJQJADfM7YsCs0AavncmGE2zDFAHcRJXP
2mzmykNS7MmVql2azIb67vaLfD84knn4Bdxg5NiJz04UfFY1TfbY9aOfmQJBAJGX
jsCQMiBPSYXZ5M+UBI2wleNqPIiCwMXinjVzndsf37kDyIAgoRCIGA0lBNzfX9r6
HgRb/GSLx4Asm+6VZt0CQQC2DwtG+CKDyqEvhIGqlrAuQivMLJExZZV3kjsFSWOA
CZFSr8JsSghb3bbxYexkzbWfalESYRkqu+zNWDPs9gpt
-----END RSA PRIVATE KEY-----
No, there is no friendly way to do it without third-party tools. If you only have private key then IMHO new X509Certificate2(keyFilePath) does not make sense.
What you could do is implement (or use a library) to parse the private key. Then encode parsed private key parameters to a xml structure that can be imported using RSA.FromXmlString.

Categories