I need to verify a SHA256 signature where we receive in every communication the message, signature and public key.
I'm trying to load a public key we receive in an Azure Function (.net core 3.1) into a X509Certificate2 object to be able to verify the signature of a received message and signature.
The following code is used:
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var data = JsonConvert.DeserializeObject<RequestObject>(requestBody);
var keyBytes = Convert.FromBase64String(data.key);
X509Certificate2 cert = new X509Certificate2(keyBytes);
When the last statement fires, the following exception comes up:
at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[] data)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData)
at leo_signing_library.Function1.<Run>d__0.MoveNext() in
C:\Users\Leo\source\repos\leo-signing-library\Function1.cs:line 30
When running in debug the following error comes up in the debug window:
System.Private.CoreLib: Exception while executing function:
CheckSignature. System.Security.Cryptography.X509Certificates: Geen
overeenkomend object gevonden.
Just for clarity a screenshot of the debug window:
screenshot
I've added the nuget package System.Security.Cryptography.X509Certificates (version 4.3.2), but no effect.
Does anyone know how to overcome this? Or has a working example of verifying a signature based on a public key not stored in the certificate store?
Edit 1:
I'm trying to load the following public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1iyta8F+SLHhbfy+f7fL
8LrsS1LKs05UaHm6muOTlzIdSDwo4dNiWg6rbiL2v4IwpglgL8kvbXLZ8Z3/E6Uu
uUi/IW1Hakgx65Qy4LkGxsnZ/jnLu3DxfY9xycMYq9TfNxIIvFPkbEJY/pwGjZ+q
13WegTJG7m+tqf5GbA3xTLz2DSHXPc7y77OnsDawHsOEou9IZZQCdxCg3L186gU4
yj6mC07Eop3lKMjsqNAXdVmnzcUc+PixQFCSjOcL9Fpbq+aHyYW+Pk9h5dWCGGNQ
wixTIJKTeDxAEkbZ2eLuqQZOCNbkHBn9x+IBxeKMmsmRg/J4/QqwCm+t+wd4VNAJ
ewIDAQAB
-----END PUBLIC KEY-----
Related
Notice: I'm a cryptography n00b, so please take that into account.
I have a .net core 3.1 console app where I need to load a public key certificate from a .pem file into x509Certificate2.
The test file I'm using is this:
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkG2geTjB2kGqnpHsrM6s
5nza9uuNpSXFb6qckxTzKV6x7THfd9C2kthWnQX5VebfERyIevSeFHwdUaAuCl99
L12FqLi6Cj5G9V/yPxKQfvRzLYGatOTq6NPcNHQybcxOr4P5baATLykn03Jl2OgI
q5TKPAuldAn40hMAtvx9c5NjZCkFoBdzYu9mITAgbwuxDhEuhJZg7m/6wqS5T2mE
cIYF/111zaLxrVAmtVBAAWW/xc4D+rFPgh5HYo7FxgzZTtLA8JvU8XLJ6PmPhEsP
v6Q75e/Mb0ORLH+BDqui3t1WNKtHxqK2qSQcOU3QbmS/tevwWeY8rpuxBQk2MAMk
GlnZ0pzcV7OW/U5c7yEvdmY6ezB2E9358RrimdzcDFziWnNPJ9MTKsKMn8H2468q
C0n5+orFd66eu1Wv7eduYVoiOKGk57On9jHBVSTEw0B2X8e1ppIvp2I2a2VoXcej
/koQpb8/h7DzS03eD2VNwQR3+GyQHokSNnBi1t6YKg8FYSB/iwtDl+0avKwtwPh9
GOQo4L7wwJTD03GBKHuGtQ92Vrg4LKsiecpsW6VVY+RhXY6LOu5dJZ8ol8+Rp1vx
DbJW0MrybyBRnBMXPsCNbbc8HK4rB8KD1vtUFSX3wmh1rQiJCuOnKC5kBW4UkXLj
pJ6paqv7fPyOeVjyZpYV67ECAwEAAQ==
-----END PUBLIC KEY-----
And the code loading it:
public static string GetPublicKeyXml(string path)
{
if (string.IsNullOrWhiteSpace(path))
throw new ArgumentNullException(nameof(path));
var file = new FileInfo(path);
if (!file.Exists)
throw new FileNotFoundException($"{path} neexistuje!");
var certificate = new X509Certificate2(file.FullName);
.....
}
But when I try to load the file I get:
{"Požadovaný objekt nebyl nalezen."} Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException
Which translated into english is:
Required object not found
and stack trace:
at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile(Byte[] rawData, String fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String fileName, String password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String fileName)
at XXXXXXXXXXXX.Decryptor.GetPublicKeyXml(String path) in XXXXXXXXXXXXX\Decryptor.cs:line 43
How to load the file into x509Certificate2?
for the implementation of an API I use, I need to provide a certificate, which consists of 2 byte arrays one for the public key and the other one for private key.
My initial idea was to do this with X509Certificate object of .Net. But I am struggling to get the private key bytes.
var certificate = new X509Certificate2("testCert.pfx", password, X509KeyStorageFlags.Exportable);
byte[] myPublicKey = certificate.GetRawCertData();
byte[] privateKey = ???
I've tried to export the key, but I can't export the private key standalone.
And:
certificate.PrivateKey.ToXmlString(true);
is not available on a Ubuntu System :-(
Do you have any ideas, how to get the private bytes from certificates?
May be X509Certificate2 is not the best solution for this...
Use an approrpiate method of these X509Certificate2 extension methods:
GetRSAPrivateKey(X509Certificate2) -- for RSA keys
GetDSAPrivateKey(X509Certificate2) -- for DSA keys
GetECDsaPrivateKey(X509Certificate2) -- for EC keys
Extension method you need to use depends on asymmtric key algorithm.
I'm trying to load an X509Certificate2 from PEM files in a .NET standard library.
I created a self-signed certificate using openssl like so:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj /CN=localhost -days 365
I loaded the resulting PEM files into embedded string resources within the project and am trying to load them with the following code:
private X509Certificate2 GetCertificate()
{
try
{
byte[] pubPem = System.Text.Encoding.UTF8.GetBytes(Properties.Resources.DefaultPublicPem.Trim());
var cert = new X509Certificate2(pubPem);
var rsa = GetRSAFromPem(Properties.Resources.DefaultPrivatePem.Trim());
cert.PrivateKey = rsa;
return cert;
}
catch (Exception ex)
{
// ignore errors
return null;
}
}
public static RSA GetRSAFromPem(String pemstr)
{
RSA rsaKey = RSA.Create();
Func<RSA, RsaKeyParameters, RSA> MakePublicRCSP = (RSA rcsp, RsaKeyParameters rkp) =>
{
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
rcsp.ImportParameters(rsaParameters);
return rsaKey;
};
Func<RSA, RsaPrivateCrtKeyParameters, RSA> MakePrivateRCSP = (RSA rcsp, RsaPrivateCrtKeyParameters rkp) =>
{
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
rcsp.ImportParameters(rsaParameters);
return rsaKey;
};
PemReader reader = new PemReader(new StringReader(pemstr));
object kp = reader.ReadObject();
// If object has Private/Public property, we have a Private PEM
var hasPrivate = kp.GetType().GetProperty("Private") != null;
var isPrivate = kp is RsaPrivateCrtKeyParameters;
return isPrivate ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)kp) : hasPrivate ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)kp).Private)) : MakePublicRCSP(rsaKey, (RsaKeyParameters)kp);
}
I tested it on Android and it works great.
On iOS I haven't tested yet, but on UWP it fails and I get a PlatformNotSupported Exception while trying to set the PrivateKey on the certificate.
So I'm wondering what's not supported, and whether I'm not doing it right.
According to this, setting the private key on an existing certificate is not supported in .net core:
The PrivateKey property will be back in netstandard2.0 (#12295), but
it will throw on set for .NET Core.
set_PrivateKey has a large amount of nuance in .NET Framework
(depending on how you use it you can end up with side effects that
persist across machine reboots), and mirroring that level of nuance to
platforms other than Windows is awfully tricky, which is why we don't
support it.
The only supported way to have a cert with a private key on .NET Core
is through a PFX/PKCS12 file (or the cert+key pair to already be
associated via X509Store).
So one way to solve this was to merge public-private pair to a PFX file, embed it as a resource, and initialize the X509Certificate2 from that PFX.
Another way, which I ended up using, is to use the method RSACertificateExtensions.CopyWithPrivateKey on UWP.
So basically I ended up building a platform specific interface for loading the certificates. On UWP it was implemented like this:
public class UWPCertificateBuilder : ICertificateBuilder
{
public X509Certificate2 GetCertificate(X509Certificate2 cert, RSA key)
{
return cert.CopyWithPrivateKey(key);
}
}
On Android it was implemented like this:
public class DroidCertificateBuilder : ICertificateBuilder
{
public X509Certificate2 GetCertificate(X509Certificate2 cert, RSA key)
{
cert.PrivateKey = key;
return cert;
}
}
Instead of RSACryptoServiceProvider you should use base RSA class. In .NET Standard and .NET Core on Windows, RSA private key is resolved to RSACng, instead of legacy RSACryptoServiceProvider.
See this thread for more details: X509AsymmetricSecurityKey.GetAsymmetricAlgorithm returns null after .Net 4.7.2 upgrade
If have the follwoing situation:
A Client app runs on Raspian with .NETCore 2.1 (Linux)
It encrypts a message with a public cert (RSA)
Sends this encrypted message to a windows server
This server decrypts the message
Now the data has 2 more bytes at the beginning of the text!
Example: Encrypting "Hallo welt" under linux leads to "\u0004\nHallo welt" in windows.
For me it seems that there is a bug in the linux implementation of the .NETCore encryption, or (maybe padding-problem)?
Encryption is done with
public static byte[] Encrypt(byte[] plainData, X509Certificate2 certificate) {
var message = new EnvelopedCms(new ContentInfo(plainData));
message.Encrypt(new CmsRecipient(SubjectIdentifierType.IssuerAndSerialNumber, certificate));
return message.Encode();
}
Decryptiong with:
public static byte[] Decrypt(byte[] encryptedData, X509Certificate2 certificate) {
var message = new EnvelopedCms();
message.Decode(encryptedData);
message.Decrypt(new X509Certificate2Collection {certificate} );
return message.ContentInfo.Content;
}
I can also provide an very simple example project if needed...
This is a known bug in .NET Core and will be fixed in 3.0...
See: https://github.com/dotnet/corefx/issues/32978
I'm getting the following exception when i try to encrypt a byte array with a EC public key :
java.security.InvalidKeyException: No installed provider supports this key:
sun.security.ec.ECPublicKeyImpl
This exception is generated when i call Cipher.init(). The lines below show what I did in my program:
ECPublicKey publicKey ;
ECPrivateKey privateKey;
//Generating key paire (public and private keys)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "SunEC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(571, random);
KeyPair pair = keyGen.generateKeyPair();
privateKey = (ECPrivateKey) pair.getPrivate();
publicKey = (ECPublicKey) pair.getPublic();
// get an AES cipher object with CTR encription mode
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// encrypt the sharedSecret using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);**
byte[] result = cipher.doFinal(data);
Must I add a provider to support this public key ??
Finally, I found the source of this exception. The problem was initialization of cipher :
//This is the wrong initialization
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
//This is the right initialization
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding","SunJCE");
But now, i have another exception which is (it is less important than the previous one) :
java.security.InvalidKeyException: Invalid AES key length: 170 bytes
So what must I use as encrypting algorithm with ECDSA public key now ?