In Windows, I use the following code to generate public, private and CSR keys using OpenSSL and RSA, with a key size 2048 bits in PEM format.
string[] textArray1 = new string[] { "req -new -utf8 -nameopt multiline,utf8 -config \"", this.CNF_FILEPATH, "\" -newkey rsa:2048 -nodes -keyout \"", this.PRIVATEKEY_FILEPATH, "\" -out \"", this.CSR_FILEPATH, "\"" };
if (this.RunOpenSslWithArguments(string.Concat(textArray1)) == 0)
{
string[] textArray2 = new string[] { "rsa -in \"", this.PRIVATEKEY_FILEPATH, "\" -out \"", this.PUBLICKEY_FILEPATH, "\" -pubout -outform PEM" };
if (this.RunOpenSslWithArguments(string.Concat(textArray2)) == 0)
{
string CSRKey = File.ReadAllText(this.CSR_FILEPATH);
string PrivateKey = File.ReadAllText(this.PRIVATEKEY_FILEPATH);
string PublicKey = File.ReadAllText(this.PUBLICKEY_FILEPATH);
MessageBox.Show("The certificate request was successfully generated");
}
}
private int RunOpenSslWithArguments(string args)
{
int exitCode;
string path = @"C:\Program Files\OpenSSL-Win64\bin\openssl.exe";
if (!Environment.Is64BitOperatingSystem)
{
path = @"C:\Program Files\OpenSSL-Win32\bin\openssl.exe";
}
if (!File.Exists(path))
{
MessageBox.Show("The path to the certificate request generation tool is not valid" );
exitCode = -1;
}
else
{
System.Diagnostics.Process process = new System.Diagnostics.Process
{
StartInfo = {
WindowStyle =System.Diagnostics.ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false,
FileName = path,
Arguments = args
}
};
process.Start();
process.WaitForExit();
exitCode = process.ExitCode;
}
return exitCode;
}
Section (1) : For Expample Generated Public Key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAph0TLX18fyIdj5e52yT1
rC1REhkotx5zKCbwMm0lKAjWEFDbDK6weNGBEnlIekS6h8wez7GHr65GckWqLPn0
9XqmMuVkXaBQFLhben+KC/j9Z89QZE4wE7PcOuUqzx8ZvfiTo2d1C89lYFuVS5xe
/RSL8YzqK2TpPm37j0TUXCOv8t4l5T7iZZsye9UxgqYXebBesCnM94GOUvoLvcTh
cRAMnsDKm9uXEh3/jzRZf6s0mUbSaqnoArUZwi+Ndao6h0MNVrH7vFMV4Ierznr9
5RPCMBh3n+rVlYAETU5FpMgRNbDk7s0E7ruX4QSl/WlhXoE75m2JZegzvQAqaCgI
LwIDAQAB
-----END PUBLIC KEY-----
**451 characters with 9 lines**
================================================
How can I implement this in MAUI for Android?
I use the following code in MAUI Android
public static void GenerateKeys()
{
using (RSA rsa = RSA.Create(2048))
{
// Export the private key
var privateKey = rsa.ExportRSAPrivateKey();
var privateKeyPem = $"-----BEGIN PRIVATE KEY-----\n{Convert.ToBase64String(privateKey, Base64FormattingOptions.InsertLineBreaks)}\n-----END PRIVATE KEY-----";
// Export the public key
var publicKey = rsa.ExportRSAPublicKey();
var publicKeyPem = $"-----BEGIN PUBLIC KEY-----\n{Convert.ToBase64String(publicKey, Base64FormattingOptions.InsertLineBreaks)}\n-----END PUBLIC KEY-----";
Console.WriteLine("Private Key:\n" + privateKeyPem);
Console.WriteLine("Public Key:\n" + publicKeyPem);
}
}
Section (2) : Generated this Public Key
-----BEGIN PUBLIC KEY-----
MIIBCgKCAQEAyTF/BTWz7BfvJwspprK22sVljBA1AcRBbCgpQUeyMBZtl4GDmgHs0IhTLRembdC/
alVNYbZqv6v3ptorTnUp2MfuI3Ffd0kge3X7yKWH3FUz/wXnu2hzJTd7Jf8NTCq429YgrVlE45Y5
GsKithqUZI9tMDyY/v76b7l6fHKkfRU/Mp3YuvcaRfqy7P7jzBXo69sB9qRPwg4Qn1XxwPCot4br
qF+xcekPb+N4lOEKKiepDg/8MEuyeJ6j1pfnzeL1ZtnMJLiAwq7lCn3mwt3qux1OIzlDbEGVExoT
sin3wWdB5KGDmoz8LqIgrq+UjmkNCT/ptbCsLJ+ZFAPksNClrwIDAQAB
-----END PUBLIC KEY-----
**419 characters with 8 lines**
The problem is here. In the Section(1) , the public key is 451 characters with 9 lines, in the Section(2) , the public key is 419 characters with 8 lines.
Section(1) is correct.
I tried a lot. But I did not get the desired result
The keys generated by your two OpenSSL statements are PEM encoded, whereby the private key has the PKCS#8 format (with the header/footer -----BEGIN PRIVATE KEY-----
/-----END PRIVATE KEY-----
) and the public key has the X.509/SPKI format (with the header/footer -----BEGIN PUBLIC KEY-----
/-----END PUBLIC KEY-----
).
However, the methods you use, ExportRSAPrivateKey()
and ExportRSAPublicKey()
, do not export the keys in these formats, but in the private and public PKCS#1 format (in this format, the header/footer for the PEM encoded private key is -----BEGIN RSA PRIVATE KEY-----
/-----END RSA PRIVATE KEY-----
and for the PEM encoded public key -----BEGIN RSA PUBLIC KEY-----
/-----END RSA PUBLIC KEY-----
).
Fortunately, the formats you need are also supported by .NET, the PKCS#8 format for private keys by ExportPkcs8PrivateKey()
and the X.509/SPKI format for public keys by ExportSubjectPublicKeyInfo()
(the latter is already mentioned in the other answer).
All four methods do not export PEM encoded keys but DER encoded keys, which is why you explicitly convert these keys to PEM encoded keys.
However, note that your conversion has a bug: The Base64FormattingOptions.InsertLineBreaks
option inserts a line break after 76 characters, while PEM uses a line break after 64 characters.
Some applications may accept 76 bytes, some may not, so it would be better to stick with 64 bytes. By the way, .NET supports the PEM encoding with the PemEncoding
class.
Since .NET 7, there are methods that provide the PEM encoded key directly, namely ExportPkcs8PrivateKeyPem()
and ExportSubjectPublicKeyInfoPem()
(as well as ExportRSAPrivateKeyPem()
and ExportRSAPublicKeyPem()
). As I am not familiar with MAUI, I cannot say whether all these methods are also available there.
The fix is therefore in the simplest case:
using System.Security.Cryptography;
...
var privateKeyPem = rsa.ExportPkcs8PrivateKeyPem();
var publicKeyPem = rsa.ExportSubjectPublicKeyInfoPem();
or with explicit PEM conversion (if the PEM methods are not available):
using System.Security.Cryptography;
...
var privateKeyPem = PemEncoding.WriteString("PRIVATE KEY", rsa.ExportPkcs8PrivateKey());
var publicKeyPem = PemEncoding.WriteString("PUBLIC KEY", rsa.ExportSubjectPublicKeyInfo());