C#でクライアント証明書を作成するプログラムコード
クライアント証明書をC#を使って作成するコード
今回作成する証明書は、前回、クライアント証明書認証型のWEBサイト構築で紹介した分と合わせると、このプログラムで作成したクライアント証明書を利用した、クライアント証明書認証型のWEBサイトシステムが完成する。
前回のクライアント証明書認証型WEBサイトをC#のASP.NETで構築はここをクリック
Bouncy Castleのライブラリを使用したクライアント証明書を作成
Bouncy CastleのライセンスはMIT Licenseが適用されており、著作権表示および本許諾表示をソフトウェアのバージョン情報等に記載していれば、利用方法などは自由で利用しやすいライブラリ。
ライブラリのダウンロードは、Nugetから適用する事が可能だが、本系サイトのBouncy CastleのページからC#ライブラリページをダウンロードした方が、最新モジュールを利用できる為、本家サイトからダウンロードする事が、筆者のおすすめ
zipの圧縮ファイルを解凍すると、参照設定可能なDLLが利用可能だ。
画面のデザイン
今回は、FrameWorkの4.7.2を利用してみた。
画面には、サーバー側にインポートした証明書が、後から誰の分であるか識別出来るように、フレンドリ名を任意で設定出来るようにだけした、簡素な画面である。
namespace createcert
{
partial class frmMain
{
///
/// 必要なデザイナー変数です。
///
private System.ComponentModel.IContainer components = null;
///
/// 使用中のリソースをすべてクリーンアップします。
///
/// マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows フォーム デザイナーで生成されたコード
///
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
///
private void InitializeComponent()
{
this.btnGenerate = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// btnGenerate
//
this.btnGenerate.Location = new System.Drawing.Point(28, 41);
this.btnGenerate.Name = "btnGenerate";
this.btnGenerate.Size = new System.Drawing.Size(238, 33);
this.btnGenerate.TabIndex = 0;
this.btnGenerate.Text = "証明書の作成";
this.btnGenerate.UseVisualStyleBackColor = true;
this.btnGenerate.Click += new System.EventHandler(this.button1_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(92, 16);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(174, 19);
this.textBox1.TabIndex = 1;
//
// label1
//
this.label1.AutoSize = true;
this.label1.ForeColor = System.Drawing.SystemColors.Control;
this.label1.Location = new System.Drawing.Point(26, 16);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(60, 12);
this.label1.TabIndex = 2;
this.label1.Text = "フレンドり名";
//
// frmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
this.ClientSize = new System.Drawing.Size(304, 98);
this.Controls.Add(this.label1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.btnGenerate);
this.Name = "frmMain";
this.Text = "クライアント証明書作成";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button btnGenerate;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label1;
}
}
処理側のプログラムコード
ライブラリ使用の宣言
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
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.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Collections;
ボタンの処理
private void button1_Click(object sender, EventArgs e)
{
var subjectName ="CN=Subject";
var outputFileName = @"c:\temp\cert.pfx";
var certificate = CreateSelfSignedCertificate(subjectName,
new[] { "client", "corporate.com" },
new[] { KeyPurposeID.IdKPClientAuth },
txtFriendlyName.Text);
WriteCertificate(certificate, outputFileName);
}
自己証明書作成の制御
private static X509Certificate2 CreateSelfSignedCertificate(string subjectName,
string[] subjectAlternativeNames,
KeyPurposeID[] usages,
string strFriendlyName)
{
var issuerName = subjectName;
var random = GetSecureRandom();
var subjectKeyPair = GenerateKeyPair(random, 2048);
var issuerKeyPair = subjectKeyPair;
var serialNumber = GenerateSerialNumber(random);
var issuerSerialNumber = serialNumber;
const bool isCertificateAuthority = false;
var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber,
subjectAlternativeNames, issuerName, issuerKeyPair,
issuerSerialNumber, isCertificateAuthority,
usages);
return ConvertCertificate(certificate, subjectKeyPair, random, strFriendlyName);
}
証明書作成の詳細設定
private static Org.BouncyCastle.X509.X509Certificate GenerateCertificate(SecureRandom random,
string subjectName,
AsymmetricCipherKeyPair subjectKeyPair,
BigInteger subjectSerialNumber,
string[] subjectAlternativeNames,
string issuerName,
AsymmetricCipherKeyPair issuerKeyPair,
BigInteger issuerSerialNumber,
bool isCertificateAuthority,
KeyPurposeID[] usages)
{
var certificateGenerator = new X509V3CertificateGenerator();
certificateGenerator.SetSerialNumber(subjectSerialNumber);
const string signatureAlgorithm = "SHA256WithRSA";
certificateGenerator.SetSignatureAlgorithm(signatureAlgorithm);
var issuerDN = new X509Name(issuerName);
certificateGenerator.SetIssuerDN(issuerDN);
var subjectDN = new X509Name(subjectName);
certificateGenerator.SetSubjectDN(subjectDN);
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(2);
certificateGenerator.SetNotBefore(notBefore);
certificateGenerator.SetNotAfter(notAfter);
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
AddAuthorityKeyIdentifier(certificateGenerator, issuerDN, issuerKeyPair, issuerSerialNumber);
AddSubjectKeyIdentifier(certificateGenerator, subjectKeyPair);
AddBasicConstraints(certificateGenerator, isCertificateAuthority);
if (usages != null && usages.Any())
AddExtendedKeyUsage(certificateGenerator, usages);
if (subjectAlternativeNames != null && subjectAlternativeNames.Any())
AddSubjectAlternativeNames(certificateGenerator, subjectAlternativeNames);
var certificate = certificateGenerator.Generate(issuerKeyPair.Private, random);
return certificate;
}
公開キー、秘密キー等の設定
private static void AddAuthorityKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
X509Name issuerDN,
AsymmetricCipherKeyPair issuerKeyPair,
BigInteger issuerSerialNumber)
{
var authorityKeyIdentifierExtension =
new AuthorityKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public),
new GeneralNames(new GeneralName(issuerDN)),
issuerSerialNumber);
certificateGenerator.AddExtension(
X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension);
}
private static void AddSubjectKeyIdentifier(X509V3CertificateGenerator certificateGenerator,
AsymmetricCipherKeyPair subjectKeyPair)
{
var subjectKeyIdentifierExtension =
new SubjectKeyIdentifier(
SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public));
certificateGenerator.AddExtension(
X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifierExtension);
}
private static void AddExtendedKeyUsage(X509V3CertificateGenerator certificateGenerator, KeyPurposeID[] usages)
{
certificateGenerator.AddExtension(
X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usages));
}
private static void AddSubjectAlternativeNames(X509V3CertificateGenerator certificateGenerator,
IEnumerable subjectAlternativeNames)
{
var subjectAlternativeNamesExtension =
new DerSequence(
subjectAlternativeNames.Select(name => new GeneralName(GeneralName.DnsName, name))
.ToArray());
certificateGenerator.AddExtension(
X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension);
}
証明書構造に変換
private static X509Certificate2 ConvertCertificate(Org.BouncyCastle.X509.X509Certificate certificate,
AsymmetricCipherKeyPair subjectKeyPair,
SecureRandom random,
string strFriendlyName)
{
var store = new Pkcs12Store();
string friendlyName = strFriendlyName;
var certificateEntry = new X509CertificateEntry(certificate);
store.SetCertificateEntry(friendlyName, certificateEntry);
store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry });
const string password = "password";
var stream = new MemoryStream();
store.Save(stream, password.ToCharArray(), random);
var convertedCertificate =
new X509Certificate2(stream.ToArray(),
password,
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
return convertedCertificate;
}
証明書をPFXで出力
private static void WriteCertificate(X509Certificate2 certificate, string outputFileName)
{
const string password = "password";
var bytes = certificate.Export(X509ContentType.Pfx, password);
File.WriteAllBytes(outputFileName, bytes);
}
C#言語は、一つの言語だけで、Windowsアプリケーションも、WEBシステムも構築出来るのでとても便利な言語であると改めて認識。