はじめに
C#にて証明書を発行するプログラムを作ったのですが、
.NETに慣れているとBouncyCastleの設計は少々特殊に感じる部分もあるので備忘録として残します。
OpenSSLを利用する場合と、BouncyCastleを利用する場合の2パターンを紹介します。
今回はオレオレ証明書を作ってみます。
環境
- .NET 6
- BouncyCastle.NetCore (1.8.10)
オレオレ証明書の作成手順
鍵を作成⇒証明書発行要求を作成⇒証明書作成
の流れでオレオレ証明書(自己著名証明書)を作成していきます。
秘密鍵の作成
OpenSSL
openssl genrsa 2048 > ca.key
BouncyCastle
var keyGen = new RsaKeyPairGenerator();
var keyGenParam = new KeyGenerationParameters(new SecureRandom(), 2048);
keyGen.Init(keyGenParam);
var keyPair = keyGen.GenerateKeyPair();
using (var sw = new StreamWriter("ca.key"))
{
var writer = new PemWriter(sw);
writer.WriteObject(keyPair.Private);
}
-
備考
上記OpenSSLで作成した鍵はPKCS#1フォーマットとなりますが、
そのままではBouncyCastleはPKCS#1未対応のため読み込めません。
BouncyCastleで利用するには以下のコマンドによりPKCS#8に変換する必要があります。
openssl pkcs8 -in rs256.key -out rs256.key.pkcs8 -topk8 -nocrypt
証明書著名要求の作成
OpenSSL
openssl req -new -key ca.key -subj "/CN=oreoreca" > ca.csr
BouncyCastle
// Subject
var attributes = new Dictionary<DerObjectIdentifier, string>()
{
{ X509Name.CN, "oreoreca" },
};
var attributeOrder = new []
{
X509Name.CN,
};
var subject = new X509Name(attributeOrder, attributes);
// 拡張情報
var extGen = new X509ExtensionsGenerator();
extGen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
new SubjectKeyIdentifierStructure(keyPair.Public));
extGen.AddExtension(X509Extensions.KeyUsage, false,
new KeyUsage(KeyUsage.DigitalSignature));
var extensions = extGen.Generate();
var extAttr = new AttributeX509(
PkcsObjectIdentifiers.Pkcs9AtExtensionRequest,
new DerSet(extensions));
// CSR作成
var csr = new Pkcs10CertificationRequest(
"SHA256withRSA",
subject,
keyPair.Public,
new DerSet(extAttr),
keyPair.Private);
using (var sw = new StreamWriter("ca.csr"))
{
var writer = new PemWriter(sw);
writer.WriteObject(csr);
}
オレオレ証明書の発行
OpenSSL
openssl x509 -req -in ca.csr -signkey ca.key -days 365 -out ca.crt
BouncyCastle
var random = new SecureRandom();
// CSR読み込み
using var csrStreamReader = new StreamReader("ca.csr");
var csrReader = new PemReader(csrStreamReader);
var csrPemObj = csrReader.ReadPemObject();
var csr = new Pkcs10CertificationRequest(csrPemObj.Content);
// CSR検証
if (!csr.Verify()) throw new Exception();
// 証明書著名用秘密鍵読み込み
using var keyStreamReader = new StreamReader("ca.key");
var keyReader = new PemReader(keyStreamReader);
var keyObj = keyReader.ReadObject() as AsymmetricCipherKeyPair;
var privateKey = keyObj?.Private;
if (privateKey == null) throw new Exception("Failed to read private key.");
// Issure
var attributes = new Dictionary<DerObjectIdentifier, string>()
{
{ X509Name.CN, "oreoreca" },
};
var attributeOrder = new []
{
X509Name.CN,
};
var issure = new X509Name(attributeOrder, attributes);
// 証明書作成
var certGen = new X509V3CertificateGenerator();
certGen.SetSerialNumber(new Org.BouncyCastle.Math.BigInteger(256, random));
certGen.SetIssuerDN(issure);
certGen.SetSubjectDN(csr.GetCertificationRequestInfo().Subject);
var today = DateTime.UtcNow.Date;
certGen.SetNotBefore(today);
certGen.SetNotAfter(today.AddYears(1));
certGen.SetPublicKey(csr.GetPublicKey());
certGen.AddExtension(X509Extensions.BasicConstraints, false,
new BasicConstraints(true));
certGen.AddExtension(X509Extensions.SubjectKeyIdentifier, false,
new SubjectKeyIdentifierStructure(csr.GetPublicKey()));
var factory = new Asn1SignatureFactory(
"SHA256withRSA",
privateKey,
random);
var cert = certGen.Generate(factory);
using (var sw = new StreamWriter("ca.crt"))
{
var writer = new PemWriter(sw);
writer.WriteObject(cert);
}
さいごに
このサンプルソースで作成した証明書は非常にミニマムな構成となっております。
実際に公開するような証明書を作る場合は、キー使用法の制限等しっかりと設定してやる必要があるかと思います。
次回、別のユースケースでのサンプルソースを載せていく予定です。