Posted at

BouncyCastle(C#)で、キーストア(PKCS#12)や、CMSデータ(PKCS#7)を作成する

More than 3 years have passed since last update.


はじめに

以下二つの記事を読んでいることが前提

この記事では、証明書や秘密鍵を持ち運ぶためのフォーマットであるp12(PKCS#12)と、暗号化データ持ち運び用フォーマットであるp7b(PKCS#7)

について、BouncyCastle(C#)での作り方について記述していく


PKCS#12

秘密鍵と、それに関連する証明書データを一緒にして一つのバイナリデータとして扱うためのフォーマット。

仕様としてデータをパスワードで暗号化されることも想定している。

昔はPFX(Personal inFormation eXchange)というMicrosoftの規格だったが、後継としてPKCS12が誕生した、という歴史がある。

そういう経緯があるので、拡張子はpfxとp12のどちらかが使用される。


作成例

BouncyCastleで生成するためのコードは以下

// using Org.BouncyCastle.Asn1;

// using Org.BouncyCastle.Asn1.Pkcs;
// using Org.BouncyCastle.X509;
// using Org.BouncyCastle.Crypto;
/// <summary>与えられた秘密鍵と証明書でPKCS#12形式のバイナリを作成する</summary>
/// <param name="alias">エントリーのフレンドリ名。インポート後の証明書ストアの"フレンドリ名"に反映される</param>
/// <param name="privateKey">格納するキー</param>
/// <param name="certChain">対応する証明書。先頭がメインの証明書となる</param>
static byte[] CreateP12(string alias,string password,AsymmetricKeyParameter privateKey,IEnumerable<X509Certificate> certChain)
{
var store = new Pkcs12StoreBuilder().Build();
store.SetKeyEntry(alias, new AsymmetricKeyEntry(keyPair.Private)
, certChain.Select(x => new X509CertificateEntry(x)).ToArray());
using (var mstm = new MemoryStream())
{
// 読出しはstore.Loadした後、store.GetKey(alias)や、store.GetCertificate(alias)などを使って行う
store.Save(mstm, password.ToCharArray(), new SecureRandom());
return mstm.ToArray();
}
}


PKCS#7

証明書データと、それに付属するキーによって暗号化、あるいは署名されたデータをまとめられるようにした規格。

現在は発展形としてCryptographic Message Syntax(CMS)が定義されている。

署名要求の応答バイナリとしてこの形式が使われることもある。

p12ファイルでも複数の証明書は格納は可能なのだが、p7bにすると、フレンドリ名は付加できないが、

エクスプローラ上でダブルクリックすると、中に入っている証明書一覧が見えて、選択的に証明書のインポートができるので、

複数の証明書をお手軽に扱いたい場合はこちらの方が便利だと思う。

拡張子はp7bまたはspcがよく使われる。


作成例

BouncyCastleで作成するためのコードは以下

static byte[] CreateP7b(IEnumerable<X509Certificate> certs)

{
var cmsgen = new CmsSignedDataGenerator();
var collections = new X509CollectionStoreParameters(
certs.ToArray()
);
// 第一引数の文字列は、"ATTRIBUTECERTIFICATE","CERTIFICATE","CERTIFICATEPAIR","CRL"のどれかを先頭に使用する(大文字小文字問わず
// '/'以下は固定で"COLLECTION"を使用する(大文字小文字問わず)
var store = X509StoreFactory.Create("CERTIFICATE/COLLECTION", collections);
cmsgen.AddCertificates(store);
var cmsbytes = new CmsProcessableFile(new FileInfo("bctest.dll"));
// var cmsbytes = new CmsProcessableByteArray(Encoding.ASCII.GetBytes("abcdefghijkl"));
var cms = cmsgen.Generate(cmsbytes,true);
// 読出しを行う場合は以下のようにする
// using(var mstm = new MemoryStream(cms.GetEncoded()))
// {
// store = new CmsSignedDataParser(mstm).GetCertificates("COLLECTION");
// foreach(var x in store.GetMatches(null).OfType<X509CertificateEntry>())
// {
// ...
// }
// }
return cms.GetEncoded();
}


終りに

PKCS#12とPKCS#7のファイルをBouncyCastleから作成するコードを紹介したが、

正直な話ここで触れた部分は本当に機能の一部分に過ぎない。

bc-csharpの場合、引数がobjectだったり単なるSystem.Collections.ICollectionだったりで、何が入ってくるか

一見してわからないということが非常に多いので、そのようになった場合はソースコードを見るのが一番の近道だったりする。