Reaserching BouncyCastle for C# is not for the faint of heart. After navigating an ocean of Java dead ends, I have arrived at the following code.
I believe it's very useful for someone who wants to create these files in C# to use it anywhere.
It basically generates the 3 files every webdev who uses NGINX http server would need to configure SSL certificates.
I wanted to make sure some of this BouncyCastle code isn't outdated and if it could be streamlined and improved.
There's no need for class implementations. Just pure, openssl equivalent, output.
Take a look:
// .NET Imports
using System.Collections;
using System.Text.RegularExpressions;
// BouncyCastle for .NET Imports
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
class Program
{
static void Main(string[] args)
{
// RootLenght.RootLength1024 for testing only!
// It's faster to visualize. Recommended is RootLenght.RootLength2048
GeneratePkcs3(RootLenght.RootLength1024);
GeneratePkcs8Pkcs10("US", "Utah", "Salt Lake City", "XYZ Inc.", "IT Subdivision", "www.example.com", "[email protected]", RootLenght.RootLength2048);
}
/// <summary>
/// Generates dhparam in PKCS#3 format as defined by RFC 2631.
///
/// OpenSSL equivalent command:
/// $ openssl dhparam -out dhparam.pem 2048
///
/// The minimum root length recommended for NGINX dhparam is 2048bit
///
/// *******************************************
/// Notes / Handy references:
/// http://www.keylength.com/en/compare/
///
/// </summary>
static void GeneratePkcs3(RootLenght rootLength)
{
string dhparam = null;
try
{
Console.WriteLine($"Generating {(int)rootLength}bit DH parameter...");
Console.WriteLine("");
Console.WriteLine("Choosing a Root Length > 1024bit may take a while.");
Console.WriteLine("");
const int DefaultPrimeProbability = 30;
DHParametersGenerator generator = new DHParametersGenerator();
generator.Init((int)rootLength, DefaultPrimeProbability, new SecureRandom());
DHParameters parameters = generator.GenerateParameters();
DHParameters realParams = new DHParameters(parameters.P, BigInteger.ValueOf(2));
Asn1EncodableVector seq = new Asn1EncodableVector();
seq.Add(new DerInteger(realParams.P));
seq.Add(new DerInteger(realParams.G));
byte[] derEncoded = new DerSequence(seq).GetDerEncoded();
dhparam = Convert.ToBase64String(derEncoded);
Console.WriteLine("-----BEGIN DH PARAMETERS-----");
Console.WriteLine(SpliceText(dhparam, 64));
Console.WriteLine("-----END DH PARAMETERS-----");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine($"{(int)rootLength}bit DH parameter succesfully generated.");
Console.WriteLine("");
Console.WriteLine("");
}
catch (Exception ex)
{
// Note: handles errors on the page. Redirect to error page.
//ErrorHandler(ex);
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// Generates RSA private key in PKCS#8 format as defined by RFC 5208.
/// Generates Certificate Signing Reuqest in PKCS#10 format as defined by RFC 2986.
///
/// OpenSSL equivalent command:
/// $ openssl req -new -newkey rsa:2048 -nodes -keyout www_example_com.key -out www_example_com.csr -subj "/C=US/ST=UT/L=Salt Lak City/O=XYZ Inc./OU=IT Division/CN=www.example.com/[email protected]"
///
/// The minimum root length recommended for NGINX dhparam is 2048bit
///
/// *******************************************
/// Notes / Handy references:
/// http://www.keylength.com/en/compare/
///
/// </summary>
static void GeneratePkcs8Pkcs10(string countryIso2Characters, string state, string city, string companyName, string division, string domainName, string email, RootLenght rootLength)
{
string csr = null;
string privateKey = null;
try
{
var rsaKeyPairGenerator = new RsaKeyPairGenerator();
// Note: the numbers {3, 5, 17, 257 or 65537} as Fermat primes.
// NIST doesn't allow a public exponent smaller than 65537, since smaller exponents are a problem if they aren't properly padded.
// Note: the default in openssl is '65537', i.e. 0x10001.
var genParam = new RsaKeyGenerationParameters(BigInteger.ValueOf(0x10001), new SecureRandom(), (int)rootLength, 128);
rsaKeyPairGenerator.Init(genParam);
AsymmetricCipherKeyPair pair = rsaKeyPairGenerator.GenerateKeyPair();
IDictionary attrs = new Hashtable();
attrs.Add(X509Name.C, countryIso2Characters);
attrs.Add(X509Name.L, city);
attrs.Add(X509Name.ST, state);
attrs.Add(X509Name.O, companyName);
if (division != null)
{
attrs.Add(X509Name.OU, division);
}
attrs.Add(X509Name.CN, domainName);
if (email != null)
{
attrs.Add(X509Name.EmailAddress, email);
}
var subject = new X509Name(new ArrayList(attrs.Keys), attrs);
var pkcs10CertificationRequest = new Pkcs10CertificationRequest(PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id, subject, pair.Public, null, pair.Private);
csr = Convert.ToBase64String(pkcs10CertificationRequest.GetEncoded());
var pkInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(pair.Private);
privateKey = Convert.ToBase64String(pkInfo.GetDerEncoded());
Console.WriteLine("-----BEGIN PRIVATE KEY-----");
Console.WriteLine(SpliceText(privateKey, 64));
Console.WriteLine("-----END PRIVATE KEY-----");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine($"{(int)rootLength}bit Private key succesfully generated.");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("-----BEGIN CERTIFICATE REQUEST-----");
Console.WriteLine(SpliceText(csr, 64));
Console.WriteLine("-----END CERTIFICATE REQUEST-----");
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine($"Certificate Signing Request succesfully generated.");
Console.ReadKey();
}
catch (Exception ex)
{
// Note: handles errors on the page. Redirect to error page.
//ErrorHandler(ex);
Console.WriteLine(ex.Message);
}
}
private enum RootLenght
{
RootLength1024 = 1024, // Test ONLY!
RootLength2048 = 2048,
RootLength3072 = 3072,
RootLength4096 = 4096,
}
public static string SpliceText(string text, int lineLength)
{
return Regex.Replace(text, "(.{" + lineLength + "})", "$1" + Environment.NewLine);
}
}
Here's the output:
Generating 1024bit DH parameter...
Choosing a Root Length > 1024bit may take a while.
-----BEGIN DH PARAMETERS-----
MIGHAoGBAMP7G34Trw22TCnfYzIpkv/4hJKGGbEYalW4okSq2Zkxvk8Oeikel3k4
M+2b6KbBUMETpKqMhm18M3bCVE8ENFRGyTtrhYGXfPrWAF+cbhZVttgnpnyGOpcp
7rsuI3/NHkhp8n/Fuxy+9CDzh6mva4nCNSwvbzzkzhh0tOhtkMezAgEC
-----END DH PARAMETERS-----
1024bit DH parameter succesfully generated.
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQMMj7KQMzVc7x
oDXF5mqvuJS/7LH7hJEWQnpbsYH5oIu1/dfL9+xd0yBZ25xcA4NFSlWf34otTnBj
qrRLzq/86PZNHJn239odbM+1XRqPnC6XLyHBmOcQAa4eIQL3zHL6JQcCKWVcOBy4
HvY+OTwE2IksvEBgybj6HdSbdJnGLkyCpiG9F9tjcc3OUmflev554Vpwb6Pdxt0d
2zQt3UHHoNKRJYnhZMAGVm2X0c1s5QGx33sDQxBEZp7dhO4Wq9gnU8DVzJzXmvN6
9eJykIN42TEHdQr/MycQP0B5M92BDLuIy4mf/gCj9eIarJlDFnkKBu5QAF3wVtHt
13imkCEXAgMBAAECggEAVm+iyhBNiV87c7tCCB+xtLIsZAC4JoZ0HGtOVMp+fa8n
wlAZa9BLvOqemGAi61r0Ae0qXp2XR1c1N1QU1hKCo7zvIbXZwJNRAf1+wTxd/jjo
aRGC8Nd0O4OPEGhBTLOAGxYLclzQffi4B8OnFc96eUKTtVSVX/nwiDuhXUeMXqhz
AZynm9CqhDN4fVJhMlUCVokKmaPD5Y/toUTQ1hkrsb72HYX1tcAnp9j02UwUobZW
/GJKC9d0WVkUw1Rb0cWhPlyz1vV/l4nlif1mQVa9pSWFcGyjmi34ERGqGah+iCJe
H6br82dng8VvWcSJrjD+iloa5LmwFMQbLBax1sX+ZQKBgQD6AvKQZ0jq1AGwAwXj
Hv10zaW4RhiW3RI6Qnk4w+WxuR6tqHq8ONsyK6a97k9MSp00TglNo8udiBOdBcIK
F3WjhZX2i6qMIedIWwEg1ZEYHikTCsAcx/JyQP3YqMqTMBLJEfHVZURNkS/NUiDx
xCzF+5yQfA5MhnRWznwoMglcUwKBgQDVLWUBPWtDXZZWJyLkArxS28s/dstrUdYp
Vc+jDOEmIwWoPfCzkkTLCtGpveh0k1Z1dzTC5pAcWBngvDeB3rXrvCf7VtOd7Q1R
AIhHaLSYCA5yG93tf28WeCBICK/taAhkNAMcGcOfG106LOvB9t/B19q3LoxoEiJ/
L2PWy2CvrQKBgEiPxHDu2TX2tEquhe3mV6+n5Bo4lfhrT1gDZQV5rdfIH8RNKtoo
Mk48Zxem6/L7kObWY1LaYEVncjHXsvqU1nrQUbcN0ED9sg/JAenSslrqngc9zoZL
7e0FOefBDZJsmHctMyA5VPqiUdpopNEmm8wWe9lfeBLzzx5GrbhI1XirAoGBAJGs
+LX1M8SQQrjS+7vWxrzUqDrRQkcvYGtU0ZR5q641Bpum4ELGNK6P0SDuvthTCyUw
deSuTFKiPeTemgvslmLmbGgMOIZWROBSvc4WljrCXqTJuEmg6Nfw9RZkoVxZ2Eop
ZOqiDJVAzN+BbQZaXyQHDtZZP+eqZNqHFvHkf0jtAoGAEggGixhY79ZoSpRejlHS
oTCWqtsjB9xLPNC6JLD1TP7UhHNXhpnvBRpxanqeUKwvfnMLZKR5ZH2mE+aA3hij
oiU87X9iDf7H9oQ91dzvsoDh3WbEFvTFo88kBUxb7Q2lIqWG0OjhKDrj/3TGWzpQ
XznRAR43mzbkukKE39ddYns=
-----END PRIVATE KEY-----
2048bit Private key succesfully generated.
-----BEGIN CERTIFICATE REQUEST-----
MIIC4TCCAckCAQAwgZ0xFzAVBgNVBAsMDklUIFN1YmRpdmlzaW9uMRcwFQYDVQQH
DA5TYWx0IExha2UgQ2l0eTEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5j
b20xGDAWBgNVBAMMD3d3dy5leGFtcGxlLmNvbTENMAsGA1UECAwEVXRhaDELMAkG
A1UEBhMCVVMxETAPBgNVBAoMCFhZWiBJbmMuMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA0DDI+ykDM1XO8aA1xeZqr7iUv+yx+4SRFkJ6W7GB+aCLtf3X
y/fsXdMgWducXAODRUpVn9+KLU5wY6q0S86v/Oj2TRyZ9t/aHWzPtV0aj5wuly8h
wZjnEAGuHiEC98xy+iUHAillXDgcuB72Pjk8BNiJLLxAYMm4+h3Um3SZxi5MgqYh
vRfbY3HNzlJn5Xr+eeFacG+j3cbdHds0Ld1Bx6DSkSWJ4WTABlZtl9HNbOUBsd97
A0MQRGae3YTuFqvYJ1PA1cyc15rzevXicpCDeNkxB3UK/zMnED9AeTPdgQy7iMuJ
n/4Ao/XiGqyZQxZ5CgbuUABd8FbR7dd4ppAhFwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCwaMkLKKioFy1XUSRjkjDupuvsSXT070oov3F5Vwr0TT5rxki16PeXaLFf
lFzhaiwMx88ql2vHLeq6WADrvRtW/dB2OPTbceMC+CuRb7Mk0WKI+SMfrDbSYc4I
oXdDE8NfdMWV2ntWbKptVjJUfU0OmNnsh2W9973JpiRVMl+UJv/eMXMjKhH4Trzf
sW+voJLNajjVTJwYIQktOpGM7rmfjb9rVtZ1f5ZlgCCJUeTttdFSqSPG/yRh+9yJ
HKBeBFKptJ0HsZTLMhcRWc8rEiqTmag2QmL9jJHP4krwLf5aYQVUZAABJJ2LNyR8
8yYUVt5KsHKYksMu3yOnOq5BOHNu
-----END CERTIFICATE REQUEST-----
Certificate Signing Request succesfully generated.
Console.WriteLine("");
==Console.WriteLine();
:) \$\endgroup\$