Add Certificate X509 with passphrase key to Webservice request - c#

I'm having trouble with adding a new SSL certificate to my webService request.
var client = new RestClient(tokenUrl);
string certif = String.Format("{0}/client.cer", CertifPath);
string key = String.Format("{0}/client.key", CertifPath);
if (File.Exists(certif) && File.Exists(key))
{
X509Certificate2 cert = new X509Certificate2(certif, key);
X509CertificateCollection collection1 = new X509CertificateCollection();
collection1.Add(cert);
client.ClientCertificates = collection1;
}
I'm getting as a response : 400 no required ssl certificate was sent nginx !!!!.
In Addition : When i use PostMan Or SoapUI .I a must add a third secret key(passphrase) to be able to get response. ex :Add certificate via postman
My Question is How can i add this third parameter(secret key) in my request c# ?.
There is another way to implement certificate to my request ???

Can you use that neat little piece of code that let's you do exactly that :
byte[] certBuffer = Helpers.GetBytesFromPEM(publicCert, PemStringType.Certificate);
byte[] keyBuffer = Helpers.GetBytesFromPEM(privateKey, PemStringType.RsaPrivateKey);
X509Certificate2 certificate = new X509Certificate2(certBuffer, password);
RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
certificate.PrivateKey = prov;

Solution :
I wasted a lot of time to search for how can include three informations (Certificat.cer,certif.key and the passphrase) in one Rest call.
The solution was easy:
the certificate Object is very flexible : i can encapsulate the
three information in one certificate called (.pfx) using OpenSSL.
you can install OpenSSL via : http://slproweb.com/download/Win64OpenSSL_Light-1_1_0g.exe
After this commande :
openssl pkcs12 -export -out certificate.pfx -inkey client.key -in client.cer.
A new file is generated : certificate.pfx.
So Now I can easly include my new certificate without any ERRORS :) .

Related

Import an OpenSSL ECDSA PEM private key and sign it

To generate a private and public key with OpenSSL I have run
openssl ecparam -genkey -name secp256k1 -out private-key.pem
openssl ec -in private-key.pem -out public-key.pem -pubout
I upload the public key to the remote server. Then import the private-key.pem to a .Net Framework (it has to be .Net Framework) C# service to use it to sign an API payload:
public string LoadFromCng(byte[] request, string privateKeyFile)
{
CngKey cng = CngKey.Open(privateKeyFile);
// Sign the request body with the private key.
ECDsaCng dsa = new ECDsaCng(cng);
byte[] signedRequest = dsa.SignData(request, HashAlgorithmName.SHA256);
return Convert.ToBase64String(signedRequest);
}
With privateKeyFile = private-key.pem the above code gives Keyset does not exist
If I use mkcert and run mkcert -ecdsa -pkcs12 private-key.pem it generates a PKCS#12 key called private-key.pem.p12 and then:
public string LoadFromX509(byte[] request, string privateKeyFile)
{
var cert = new X509Certificate2(privateKeyFile, "changeit");
var key = cert.GetECDsaPrivateKey();
byte[] signedRequest = key.SignData(request, HashAlgorithmName.SHA256);
return Convert.ToBase64String(signedRequest);
}
With privateKeyFile = private-key.pem.p12 the above code appears to sign the request, but the API response is The remote server returned an error: (400) Bad Request which means the API provider can't decode the payload from the public key.
I get the same 400 error when going through the cheat sheet here and creating an X509 pfx certificate.
openssl req -new -x509 -key private-key.pem -out cert.pem -days 360
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx
The method above appears to sign the payload but the provider responds with a 400.
The suggestions here and here and here and here and others have not worked.
I can't use Net Core or NET 5 so this doesn't work either. The ImportPkcs8PrivateKey method is not available in Net Framework.
If I try and use Bouncy Castle per here I get Unable to cast object of type 'Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters' to type 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters'
The first OpenSSL statement creates an EC parameter file private-key.pem, which contains, among others, a PEM encoded private EC key in SEC1 format.
With .NET Framework the easiest way is to use BouncyCastle for key import. BouncyCastle also supports the ASN.1/DER format and the IEEE P1363 (r|s) format for EC signatures.
The following code imports a PEM encoded private EC key in SEC1 format and signs a message.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
...
string pkcs8 = #"-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIMmN/YG6BtIqm9eAcYtKdcbJosNKbB76vGMTSltPLNiioAcGBSuBBAAK
oUQDQgAESGnGxuBPdrwv1TkJEorsaNk74+ZFh2jzww2SpLTqQqkvOf5IP6fuODS/
hzztSBpsBpX9LUZh8TYHX0HRMagkaA==
-----END EC PRIVATE KEY-----";
TextReader privateKeyTextReader = new StringReader(pkcs8);
AsymmetricCipherKeyPair privateKeyParams = (AsymmetricCipherKeyPair)new PemReader(privateKeyTextReader).ReadObject();
byte[] message = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog");
ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA"); // ASN.1/DER format
//ISigner signer = SignerUtilities.GetSigner("SHA-256withPLAIN-ECDSA"); // r|s format
signer.Init(true, privateKeyParams.Private);
signer.BlockUpdate(message, 0, message.Length);
byte[] signature = signer.GenerateSignature();
Console.WriteLine(Hex.ToHexString(signature)); // 3046022100967c2e890d933c75468dceaf61152add41f143568dcb967583e1b307a0b495e30221008f6837c0d9cc01fc7bfe54ed5b6267fd3e64c2d3ff771e72762f24c20071c454

Sending soap request with X509 Certificate that uses sha256 signing algorithm

I'm trying to make a soap service.
To send request I need to use X509 Certificate. I used X509Certificate2 class for that purpose.
The problem is that its keys are System.Security.Cryptography.RSACryptoServiceProvider class that accepts only "http://www.w3.org/2000/09/xmldsig#rsa-sha1" signature algorithm.
In our case sha256 signature algorithm is needed.
Have you got any idea how can I deal with that?
Code looks like this:
SoapService.GetOrderStatusRequest request = new SoapService.GetOrderStatusRequest()
{
orderId = Int32.Parse(txtID.Text),
requestHeader = new SoapService.RequestHeader()
{
institutionId = 123,
requestId = "aeacbff8-ba6d-4a01-8e76-0b4384c24721",
system = "Test"
}
};
var cert = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "//cert.p12", "Passs123");
client.ChannelFactory.Credentials.ClientCertificate.Certificate = cert;
client.ClientCredentials.ClientCertificate.Certificate = cert;
SoapService.GetOrderStatusResponse response = client.getOrderStatus(request);
txtResult.Text = response.order.name;
Edit1.
I found out this is not problem with X509Certificate2 configuration, but with binding configuration... with message security to be exac.
Unfortunately I still have some errors :/

C# Store Public-Key X.509 encoded key in ANS.1

i am trying to implement an API. (https://www.loxone.com/dede/wp-content/uploads/sites/2/2016/08/0903_Communicating-with-the-Miniserver.pdf)
When you go to Page 5 you will see under 2.b) that I need to store a public key in Format: X.509 encoded key in ANS.1.
The problem is I have no idea how to do this in C#...
Can anyone please help me with that problem :)
Thanks in advance!
Here is an example for the publicKey that I get:
-----BEGIN CERTIFICATE-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC393RoCVFixg3cZicByw05GYqlIkmqfx1lylJqBaF7BMI/FffD5BBoFpFurj4N1pDdMF/QhBEj1Pb3XGOkrHFCinYc0lCMbSjreE+QPjmnWnZeaPd3mtBfdcTSF6hYH0regFU8nvFVgLsoBUCMjuhfsvuaW4sLylU6KhZ9hCqVQwIDAQAB-----END CERTIFICATE-----
I think you need to export this public key as X.509 encoded as ANS.1 which can be a .pem file.
From https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file/9717#9717 :
PEM on it's own isn't a certificate, it's just a way of encoding data. X.509 certificates are one type of data that is commonly encoded using PEM.
PEM is a X.509 certificate (whose structure is defined using ASN.1), encoded using the ASN.1 DER (distinguished encoding rules), then run through Base64 encoding and stuck between plain-text anchor lines (BEGIN CERTIFICATE and END CERTIFICATE).
You can use openssl to make a .pem file which is ANS.1 compliant.
openssl req -new -x509 -days 365 -nodes -out ./cert.pem -keyout ./cert.pem
Presumably, by "ANS.1" they meant "ASN.1" (Abstract Syntax Notation 1), and by "ASN.1" they meant "DER" (Distinguished Encoding Rules for Abstract Syntax Notation 1).
To convert your current PEM certificate into a "DER" certificate you either a) openssl x509 -in cert.pem -out cert.der -outform der or b) read the base64 string between "-----BEGIN CERTIFICATE-----" and "-----END CERTIFICATE-----" and convert it to bytes (e.g. Convert.FromBase64String).
If you still need it after 3 years, you can use the following C# (.NET 6) method:
public static async Task<RSA> GetPublicKey(string certStr)
{
string base64 = certStr;
if (base64.StartsWith("-----BEGIN CERTIFICATE-----"))
{
base64 = base64.Substring(27);
}
if (base64.EndsWith("-----END CERTIFICATE-----"))
{
base64 = base64.Substring(0, base64.Length - 25);
}
var asn = new AsnReader(Convert.FromBase64String(base64), AsnEncodingRules.DER);
asn = asn.ReadSequence();
asn.ReadSequence();
//string objectIdentifier = asn.ReadObjectIdentifier(Asn1Tag.ObjectIdentifier);
//asn.ReadNull();
var data2 = asn.ReadBitString(out int bitCount);
asn = new AsnReader(data2, AsnEncodingRules.DER);
asn = asn.ReadSequence();
var modulus = asn.ReadInteger();
var exponent = asn.ReadInteger();
var rsa = new RSACng();
rsa.ImportParameters(new RSAParameters
{
Modulus = modulus.ToByteArray(true, true),
Exponent = exponent.ToByteArray(true, true),
});
return rsa;
}
Not the best and no error handling, but works.
Then you don't need an X.509 certificate, just this RSA object:
var aes = Aes.Create();
string s = ByteArrayToHex(aes.Key) + ":" + ByteArrayToHex(aes.IV);
var encData = rsa.Encrypt(Encoding.ASCII.GetBytes(s), RSAEncryptionPadding.Pkcs1);
s = "jdev/sys/keyexchange/" + Convert.ToBase64String(encData);
Probably you can implement the ByteArrayToHex yourself :)

Unable to Instantiate X509Certificate2 from Byte Array

I am trying to use a self-signed certificate to configure IdentityServer3,
I used openssl to create a x509 certificate as follows:
openssl req -x509 -sha256 -days 365 -key key.pem -in csr.csr -out certificate.pem
then merged the key and cert using :
pkcs12 -export -in my-cert.pem inkey my-key.pem -out xyz-cert.pfx
i then converted the content of xyz-cert.pfx to Base64String which is stored in a key in web.config, then tried to use the certificate to instantiate X509Certificate2 as follows:
var certificate = Convert.FromBase64String(ConfigurationManager.AppSettings["SigningCertificate"]);
var options = new IdentityServerOptions
{
SigningCertificate = new X509Certificate2(certificate, ConfigurationManager.AppSettings["SigningCertificatePassword"]),
RequireSsl = false, // DO NOT DO THIS IN
Factory = factory
};
the following exception is then thrown:
I can't figure out where i got it wrong.
Thanks for your help
var options = new IdentityServerOptions
{
string CertText = ConfigurationManager.AppSettings["SigningCertificatePassword"];
byte[] certBytes = Convert.FromBase64String(certText);
SigningCertificate = new X509Certificate2(certificate, certBytes),
RequireSsl = false, // DO NOT DO THIS IN
Factory = factory
};

Constructing RSACryptoServiceProvider from X509Certificate2 without LDAP request being made

When the line to extract the public key is executed, an LDAP request is sent:
this.certificate = new X509Certificate2(buffer);
System.Security.Cryptography.X509Certificates.PublicKey key = this.certificate.PublicKey;
50 0.853745000 xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx LDAP 404 searchRequest(1) "" baseObject
...which I believe is authenticating the currently logged on user. I really need to avoid this call, as on a customer system this causes a long delay because of network configuration.
I'm assuming it's attempting to do some authentication around some kind of key store, but as in this case the certificate is all contained in the buffer provided, all I want is for the key to be used without this request being sent.
All I actually want is to create an RSACryptoServiceProvider from the private key in the certificate. I tried a few methods I've found on here involving GetPrivateKey, but struggled to get anything to work.
Thanks in advance!
EDIT Test program:
static void Main( string[] args )
{
var certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(#"E:\Temp\Cert.cer");
System.Security.Cryptography.X509Certificates.PublicKey key = certificate.PublicKey;
}
The certificate I tested with can be found here: Cert.cer
Yes, it's not the strongest signature or key, before I get comments!
Thanks again.
EDIT: I actually worked around this by using a suggestion to use BouncyCastle. I use this to parse the certificate:
X509CertificateParser parser = new X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate cert = parser.ReadCertificate(buffer);
I then extract the modulus and exponent and push them into a Microsoft RSAParameters:
RsaKeyParameters key = (RsaKeyParameters)cert.GetPublicKey();
// Construct a microsoft RSA crypto service provider using the public key in the certificate
RSAParameters param = new RSAParameters();
param.Exponent = key.Exponent.ToByteArrayUnsigned();
param.Modulus = key.Modulus.ToByteArrayUnsigned();
I can then construct the Microsoft RSACryptoServiceProvider from this:
using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
{
provider.ImportParameters(param);
byte[] rsaBlock = provider.Encrypt(preMasterSecret, false);
this.Client.Writer.Write(rsaBlock);
}
I never got any other response, so here's the Bouncycastle implementation I used.
X509CertificateParser parser = new X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate cert = parser.ReadCertificate(buffer);
I then extract the modulus and exponent and push them into a Microsoft RSAParameters:
RsaKeyParameters key = (RsaKeyParameters)cert.GetPublicKey();
// Construct a microsoft RSA crypto service provider using the public key in the certificate
RSAParameters param = new RSAParameters();
param.Exponent = key.Exponent.ToByteArrayUnsigned();
param.Modulus = key.Modulus.ToByteArrayUnsigned();
I can then construct the Microsoft RSACryptoServiceProvider from this:
using (RSACryptoServiceProvider provider = new RSACryptoServiceProvider())
{
provider.ImportParameters(param);
byte[] rsaBlock = provider.Encrypt(preMasterSecret, false);
this.Client.Writer.Write(rsaBlock);
}

Categories