Reputation: 3328
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'
Upvotes: 1
Views: 2156
Reputation: 49400
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
Upvotes: 1