LoginSignup
3
3

More than 1 year has passed since last update.

BouncyCastle(.NET)を使ってみた

Last updated at Posted at 2022-04-02

はじめに

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);
}

さいごに

このサンプルソースで作成した証明書は非常にミニマムな構成となっております。
実際に公開するような証明書を作る場合は、キー使用法の制限等しっかりと設定してやる必要があるかと思います。

次回、別のユースケースでのサンプルソースを載せていく予定です。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3