I have 2 lines of openssl commands that work and generate the public key that would work with an api that requires it to function:
openssl req -new -utf8 -nameopt multiline,utf8 -config fa.cnf -newkey rsa:2048 -nodes -keyout fa.key -out fa.csr
openssl req -in fa.csr -noout -pubkey -out publickey.pem
As far as I understand the command, it first try to generate a .csr
file using fa.cnf
config file and an RSA private key and save them in fa.csr
and fa.csr
files relatively. then, it generates the public key by specifying the .csr
file.
Here is the configuration file:
[req]
prompt = no
distinguished_name = dn
[dn]
CN=MyCompany [Stamp]
serialNumber = 12345678910
O = Non-Governmental
C = US
I've tried to implement the exact procedure in C#, and this is as far as I could get:
var subjectName = "CN=MyCompany [Stamp],O=Non-Governmental,serialNumber=12345678910,C=US";
// Create new Object for Issuer and Subject
var subject = new X509Name(subjectName);
// Generate the key Value Pair, which in our case is a public Key
var randomGenerator = new CryptoApiRandomGenerator();
var random = new SecureRandom(randomGenerator);
const int strength = 2048;
var keyGenerationParameters = new KeyGenerationParameters(random, strength);
var keyPairGenerator = new RsaKeyPairGenerator();
keyPairGenerator.Init(keyGenerationParameters);
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
using (var fw = File.CreateText("fa.key"))
{
var writer = new PemWriter(fw);
writer.WriteObject(subjectKeyPair.Private);
}
//PKCS #10 Certificate Signing Request
var csr = new Pkcs10CertificationRequest("SHA1WITHRSA", subject, subjectKeyPair.Public, null, subjectKeyPair.Private);
//Convert BouncyCastle CSR to .PEM file.
var CSRPem = new StringBuilder();
var CSRPemWriter = new PemWriter(new StringWriter(CSRPem));
CSRPemWriter.WriteObject(csr);
CSRPemWriter.Writer.Flush();
//get CSR text
var CSRtext = CSRPem.ToString();
// Write content into a Txt file
using (StreamWriter f = new StreamWriter(@"fa.csr"))
{
f.Write(CSRtext);
}
var publicKey = csr.GetPublicKey();
using (var fw = File.CreateText("publickey.pem"))
{
var writer = new PemWriter(fw);
writer.WriteObject(publicKey);
}
But the generated public key file is not valid. and the api doesn't work. I don't understand why it doesn't work or which part I've messed up. The only difference between the files generated by these 2 methods is the header of the private key file fa.key
which writes this:
-----BEGIN RSA PRIVATE KEY-----
Instead of this:
-----BEGIN PRIVATE KEY-----
I appreciate anyone who could lead me the right direction or introduce me a library that could make it easy to generate the files.
This is fairly well built in for .NET 7. For older .NETs the PEM-exporting methods don't exist, but converting the binary data to PEM is fairly easy (with the PemEncoding class (.NET 5+), or just a helper function).
using (RSA key = RSA.Create(2048))
{
CertificateRequest request = new CertificateRequest(
"CN=MyCompany [Stamp], O=Non-Governmental, serialNumber=12345678910, C=US",
key,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
// other stuff could have modified the request here, but you aren't
// using any of the extra fancy options
File.WriteAllText("fa.key", key.ExportPkcs8PrivateKeyPem());
File.WriteAllText("fa.csr", request.CreateSigningRequestPem());
File.WriteAllText("publickey.pem", key.ExportSubjectPublicKeyInfoPem());
}
I've gone ahead and upgraded this to SHA-256, which is probably what the OpenSSL command is already doing. (Also because CertificateRequest doesn't support SHA-1, as it was already a broken algorithm when CertificateRequest was built)