Translate

C#でクライアント証明書を作成するプログラムコード

クライアント証明書をC#を使って作成するコード




今回作成する証明書は、前回、クライアント証明書認証型のWEBサイト構築で紹介した分と合わせると、このプログラムで作成したクライアント証明書を利用した、クライアント証明書認証型のWEBサイトシステムが完成する。 前回のクライアント証明書認証型WEBサイトをC#のASP.NETで構築はここをクリック

Bouncy Castleのライブラリを使用したクライアント証明書を作成


Bouncy CastleのライセンスはMIT Licenseが適用されており、著作権表示および本許諾表示をソフトウェアのバージョン情報等に記載していれば、利用方法などは自由で利用しやすいライブラリ。

ライブラリのダウンロードは、Nugetから適用する事が可能だが、本系サイトのBouncy CastleのページからC#ライブラリページをダウンロードした方が、最新モジュールを利用できる為、本家サイトからダウンロードする事が、筆者のおすすめ

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システムも構築出来るのでとても便利な言語であると改めて認識。

このブログの人気の投稿

VBAのADOで「パラメーターが少なすぎます。xを指定してください。」と表示された場合の原因

ACCESSでバーコードスキャンしたら自動でイベントを起こす方法

PostgreSQL 11 でpg_dumpallを使ってバックアップしたデータをリストアするとき文字化けの対処法

ACCESSのVBAを実行するとACCESSが強制終了する事がある

VBSでマクロの実行時に警告を非表示にする方法

ACCESSのVBAでADOを利用したバインド変数を利用したデータベース連携方法

ACCESSでバーコードをスキャンして登録更新する簡単なサンプル

pgAdmin 4が遅いのは仕方がない | PostgreSQL things.

ACCESSのVBAでリストビュー(ListView)を使う為の設定 | Office365

ASP.NETのでクライアント証明書を使ったログイン認証を行う方法