1. はじめに
やってみた自分用メモ。「自己署名証明書」を作れたのはいいのですが、CreateSelfSigned という関数に隠されていて、よくわかりませんでした。
拙作ですみませんが
ということで、もうちょっと細かい制御ができる BouncyCastleで見てみたいと思います。こういう「なぜ?と思う横道」は大切だと思います。
2. いきなりコード
ChatGPTの出力が元になっています。利用される方は自己責任で。
sample.cs
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
class Program
{
static void Main()
{
string subjectName = "CN=localhost";
string[] sanNames = { "localhost", "example.com", "127.0.0.1", "::1" };
int validityDays = 365;
string pfxPassword = "p@ssw0rd";
X509Certificate2 cert = GenerateSelfSignedCertificateWithSAN(subjectName, sanNames, validityDays, pfxPassword);
File.WriteAllBytes("mycert_san.pfx", cert.Export(X509ContentType.Pfx, pfxPassword));
Console.WriteLine("SAN付き自己署名証明書を作成しました: mycert_san.pfx");
}
static X509Certificate2 GenerateSelfSignedCertificateWithSAN(string subjectName, string[] sanNames, int validityDays, string pfxPassword)
{
var keyGen = new RsaKeyPairGenerator();
keyGen.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
var keyPair = keyGen.GenerateKeyPair();
var certGen = new X509V3CertificateGenerator();
certGen.SetSerialNumber(BigInteger.ProbablePrime(120, new SecureRandom()));
// 対象=承認者 なので「自己署名証明書」
// 対象
certGen.SetSubjectDN(new X509Name(subjectName));
// 承認者
certGen.SetIssuerDN(new X509Name(subjectName));
certGen.SetNotBefore(DateTime.UtcNow);
certGen.SetNotAfter(DateTime.UtcNow.AddDays(validityDays));
certGen.SetPublicKey(keyPair.Public);
var sanList = new Asn1Encodable[sanNames.Length];
for (int i = 0; i < sanNames.Length; i++)
{
string name = sanNames[i];
if (IPAddress.TryParse(name, out _))
{
sanList[i] = new GeneralName(GeneralName.IPAddress, name);
}
else {
sanList[i] = new GeneralName(GeneralName.DnsName, name);
}
}
var subjectAltNames = new DerSequence(sanList);
certGen.AddExtension(X509Extensions.SubjectAlternativeName, false, subjectAltNames);
// 「自分で承認した」ことにして、自分の秘密鍵で署名する
var signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", keyPair.Private);
var certificate = certGen.Generate(signatureFactory);
var store = new Pkcs12StoreBuilder().Build();
var certificateEntry = new X509CertificateEntry(certificate);
store.SetCertificateEntry(subjectName, certificateEntry);
store.SetKeyEntry(subjectName, new AsymmetricKeyEntry(keyPair.Private), new[] { certificateEntry });
using (var ms = new MemoryStream())
{
store.Save(ms, pfxPassword.ToCharArray(), new SecureRandom());
return new X509Certificate2(ms.ToArray(), pfxPassword, X509KeyStorageFlags.Exportable);
}
}
}
3. 見えてきたもの
コード断片
// 対象=承認者 なので「自己署名証明書」
// 対象
certGen.SetSubjectDN(new X509Name(subjectName));
// 承認者
certGen.SetIssuerDN(new X509Name(subjectName));
// 「自分で承認した」ことにして、自分の秘密鍵で署名する
var signatureFactory = new Asn1SignatureFactory("SHA256WITHRSA", keyPair.Private);
「あ~そういうことね」と納得しました。自己承認なので「Self authorized ~」みたいな用語が正しいのではないかと思っていました。でも承認した結果が署名になっているのでこれでいいのです。むぅ、難しい。