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?
Related
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-----
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
I have X509 Signing Certificate inside of a string like:
var signingCertificate = -----BEGIN CERTIFICATE-----\r\nMIICTjCCAbegAw.........-----END CERTIFICATE-----
Now I want to read the content of this certificate. i know we can do it using X509Certificate2 object but that reads from file directly. Is there anyway to read the content from string?
You can convert your string to byte array, and create a X509Certificate2 object from it.
byte[] bytes = Encoding.ASCII.GetBytes(signingCertificate);
var x509Certificate2 = new X509Certificate2(bytes);
The server encrypts a password using the following criteria:
AES.HashAlgorithm.MD5,
password = "dynamicweb",
salt="dwapac2015",
iteration=2,
Key size=AES.KeySize.Key192,
Initial Vector="dwdevelopmentsmm");
The server langauge is C#.
On the client side, the same encryption is done as follows:
public class Aes {
private static final String KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";
private static final String KEY_SPEC_ALGORITHM = "AES";
private static final int KEY_LENGTH = 192;
private static final int KEY_ITERATION_COUNT = 2;
public static String key = "dynamicweb";
public static String salt = "dwapac2015";
public static String cipherTransformation = "AES/CBC/PKCS5Padding";
public static String initializationVector = "dwdevelopmentsmm";
public static String encrypt(String payload) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
KeySpec spec = new PBEKeySpec(key.toCharArray(), salt.getBytes(), KEY_ITERATION_COUNT, KEY_LENGTH);
SecretKeySpec secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), KEY_SPEC_ALGORITHM);
Cipher cipher = Cipher.getInstance(cipherTransformation);
cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(initializationVector.getBytes()));
byte[] encrypted = cipher.doFinal(payload.getBytes());
return new String(Base64.encodeBase64(encrypted));
}
}
The client language is Java.
The server returns the following error:
[Authenticate: 11/4/2015 6:42:09 AM]: [REQUEST: {UserName:BPlMi6RfvvWjntEW9Aw5Rw==,Password:BPlMi6RfvvWjntEW9Aw5Rw==}] System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed. at RestService.ServiceInterface.Helpers.DWCredentialsAuthProvider.CheckInDW(String userName, String password, Int32& currentUserID) at RestService.ServiceInterface.Helpers.DWCredentialsAuthProvider.TryAuthenticate(IServiceBase authService, String userName, String password) at ServiceStack.Auth.CredentialsAuthProvider.Authenticate(IServiceBase authService, IAuthSession session, String userName, String password, String referrerUrl) at ServiceStack.Auth.CredentialsAuthProvider.Authenticate(IServiceBase authService, IAuthSession session, Authenticate request) at ServiceStack.Auth.AuthenticateService.Authenticate(Authenticate request, String provider, IAuthSession session, IAuthProvider oAuthConfig) at ServiceStack.Auth.AuthenticateService.Post(Authenticate request) at lambda_method(Closure , Object , Object ) at ServiceStack.Host.ServiceRunner`1.Execute(IRequest request, Object instance, TRequest requestDto)"
X509Certificate is only instantiatable using the contents of a certificate (.cer file). How to instantiate this object using a .p12 file that contains both the certificate and the private key?
Here is what you need:
InputStream inStream = new FileInputStream("c:/certificate.p12");
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inStream, "password".toCharArray());
String alias = ks.aliases().nextElement();
certificate = (X509Certificate) ks.getCertificate(alias);