SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

RFC3820(代理証明書)のC#による実装

C#によるセキュリティライブラリの学習

torofield[著]

ダウンロード ソース差分 (18.2 KB)

ProxyCertInfo拡張の生成・解析実装方法

実装のコツ(私の場合)

 Bouncy Castleは、膨大なライブラリなので、いきなり全部を見るのは不可能です。こういう場合は、同じような実装をしている部分をコピーすればいいと思います。

 証明書拡張で、ちょっと難しい感じの場合、私は「AuthorityKeyIdentifier」か「SubjectKeyIdentifier」の実装を参考にします。

 Bouncy Castleでは、

  • csharp\crypto\src\asn1\x509\AuthorityKeyIdentifier.cs

 に配置され、

namespace Org.BouncyCastle.Asn1.X509
    public class AuthorityKeyIdentifier

 として実装してあります。

ProxyCertInfo拡張の生成・解析のソースすべて

 ProxyCertInfo拡張の生成・解析のソースは以下の通りです。

ProxyCertInfo.cs
using System;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Math;

namespace Org.BouncyCastle.Asn1.X509
{
    /**
     * The ProxyCertInfo object.
     * <pre>
     * ProxyCertInfo ::= SEQUENCE {
        pCPathLenConstraint   INTEGER (0..MAX) OPTIONAL,
        proxyPolicy           ProxyPolicy }
     * </pre>
     */
    public class ProxyCertInfo
        : Asn1Encodable
    {
        internal readonly DerInteger pCPathLenConstraint = null;
        internal readonly ProxyPolicy proxyPolicy;

        public static ProxyCertInfo GetInstance(
            Asn1TaggedObject    obj,
            bool                explicitly)
        {
            return GetInstance(Asn1OctetString.GetInstance(obj, 
                                                         explicitly));
        }

    public static ProxyCertInfo GetInstance(
            object obj)
        {
            if (obj is ProxyCertInfo)
            {
                return (ProxyCertInfo) obj;
            }

            if (obj is DerSequence)
            {
                return new ProxyCertInfo((DerSequence)obj);
            }

            if (obj is Asn1OctetString)
            {
                return new ProxyCertInfo(
                          (DerSequence)Asn1Object.FromByteArray(
                               ((Asn1OctetString)obj).GetOctets()));
            }

            if (obj is X509Extension)
            {
                return GetInstance(
                            X509Extension.ConvertValueToObject(
                                               (X509Extension) obj));
            }

            throw new ArgumentException(
                     "Invalid ProxyCertInfo: " + obj.GetType().Name);
        }

        public ProxyCertInfo(
                     BigInteger pCPathLenConstraint,
                     ProxyPolicy proxyPolicy)
        {
            if (proxyPolicy == null)
                throw new ArgumentNullException("proxyPolicy");

            this.pCPathLenConstraint
            = new DerInteger (pCPathLenConstraint); /* null accepts */

            this.proxyPolicy = proxyPolicy; /* null NOT accepts */
 
        }

        public ProxyCertInfo(ProxyPolicy proxyPolicy)
        {
            if (proxyPolicy == null)
                throw new ArgumentNullException("proxyPolicy");

            this.pCPathLenConstraint = null; /* null accepts */
            this.proxyPolicy = proxyPolicy; /* null NOT accepts */
        }

        public ProxyCertInfo(DerSequence seq)
        {
            if (seq.Count > 0 && seq.Count < 3)
            {
                if (seq[0] is DerInteger)
                {
                    this.pCPathLenConstraint
                         = DerInteger.GetInstance(seq[0]);
                }
                else
                {
                    this.proxyPolicy = ProxyPolicy.GetInstance(seq[0]);
                }

                if (seq.Count > 1)
                {
                    if (this.pCPathLenConstraint == null)
                        throw new ArgumentException(
                             "wrong sequence in constructor", "seq");

                    this.proxyPolicy = ProxyPolicy.GetInstance(seq[1]);
                }
            }
        }

        public BigInteger GetpCPathLenConstraint()
        {
            return this.pCPathLenConstraint.Value;
        }

        public ProxyPolicy GetProxyPolicy()
        {
            return this.proxyPolicy;
        }

        
        public BigInteger PCPathLenConstraint
        {
            get { return this.pCPathLenConstraint.Value; }
        }

        public ProxyPolicy ProxyPolicy
        {
            get { return this.proxyPolicy; }
        }
        

        public override Asn1Object ToAsn1Object()
        {
            Asn1EncodableVector v = new Asn1EncodableVector();

            if (pCPathLenConstraint != null)
            {
                v.Add(pCPathLenConstraint);
            }

            if (proxyPolicy != null)  // proxyPolicy always exists 
            {
                v.Add(proxyPolicy);
            }

            return new DerSequence(v);
        }
    }
}

ProxyCertInfo拡張生成

 ProxyCertInfo拡張の生成はコンストラクタで行うようにしました。BouncyCastleライブラリの中でもコンストラクタの中で行っていることが多いようです。

 ProxyCertInfo拡張生成を行う場合は、以下のようになります。

ProxyCertInfo拡張生成テストコード(一部)
private static Asn1Object FromPath(int length,
            DerObjectIdentifier policyLanguageOID) {

ProxyPolicy policy = new ProxyPolicy(policyLanguageOID);
ProxyCertInfo info = new ProxyCertInfo(
                        new BigInteger(length.ToString()), policy);

return (Asn1Object)info.ToAsn1Object();

 ProxyCertInfo拡張生成の実装は以下の通りです。proxyPolicyがnullの時に例外にしているのは、そういった規約のためです。逆にpCPathLenConstraintが必須の場合は、ここでnullかどうかを確認する必要があります。

ProxyCertInfo
public ProxyCertInfo(
       BigInteger pCPathLenConstraint,ProxyPolicy proxyPolicy)
{
     if (proxyPolicy == null)
          throw new ArgumentNullException("proxyPolicy");

     this.pCPathLenConstraint
        = new DerInteger (pCPathLenConstraint); /* null accepts */
     this.proxyPolicy = proxyPolicy; /* null NOT accepts */
 
}

 以下ToAsn1Object関数は、バイナリデータ(DER形式)を生成する関数です。Asn1EncodableVectorクラスはBouncy Castleで規定されているクラスです。

ToAsn1Object
public override Asn1Object ToAsn1Object()
{
     Asn1EncodableVector v = new Asn1EncodableVector();

     if (pCPathLenConstraint != null)
     {
          v.Add(pCPathLenConstraint);
     }

     if (proxyPolicy != null)  // proxyPolicy always exists 
     {
          v.Add(proxyPolicy);
     }

     return new DerSequence(v);
}
ProxyCertInfo拡張解析

 BouncyCastleライブラリでは、X.509電子証明書等に使用されている、ASN.1の解析はGetInstance関数で実装しているようです。そのため、ProxyCertInfo拡張解析は、GetInstance関数の中で、引数により各データに分岐しました。

 ProxyCertInfo拡張解析を行う場合は、以下のテストコードのように使用します。

ProxyCertInfo拡張解析(テストコード)
X509Extensions ext = tbsCert.Extensions;
if (ext != null)
{
    foreach (DerObjectIdentifier oid in ext.ExtensionOids)
    {
    X509Extension extVal = ext.GetExtension(oid);
    Asn1Object extObj = Asn1Object.FromByteArray(extVal.Value.GetOctets());
        ProxyCertInfo pcinfo = null;

        if (oid.Equals(X509ObjectIdentifiers.idPEProxyCertInfo))
                        {
        pcinfo = ProxyCertInfo.GetInstance(extObj);
        }
}

 GetInstance関数はstaticですから、常に次の関数を呼びます。

GetInstance
public static ProxyCertInfo GetInstance(
    object obj)
{
    if (obj is DerSequence)
    {
        return new ProxyCertInfo((DerSequence)obj);
    }

 上記が動作し、最終的には、以下のコードが走ります。ProxyCertInfo(コンストラクタ)ではバイナリデータ(DER形式)の解析をしています。

 引数に入ってくるのはバイナリデータ(DER形式)です。seqのSEQUENCE構造体の1個目(seq[0])がINTEGERである場合は、pCPathLenConstraintが入っている(OPTIONALなので入っていない場合がある)ということで、場合分けをしています。

ProxyCertInfo(コンストラクタ)
public ProxyCertInfo(DerSequence seq)
{
     if (seq.Count > 0 && seq.Count < 3)
     {
         if (seq[0] is DerInteger)
         {
              this.pCPathLenConstraint
                 = DerInteger.GetInstance(seq[0]);
         }
         else
         {
              this.proxyPolicy = ProxyPolicy.GetInstance(seq[0]);
         }

         if (seq.Count > 1)
         {
              if (this.pCPathLenConstraint == null)
                  throw new ArgumentException(
                        "wrong sequence in constructor", "seq");
              this.proxyPolicy = ProxyPolicy.GetInstance(seq[1]);
         }
     }
}

まとめ

 セキュリティを実装する場合は、セキュリティ自体が難しいので、なかなか内容が分からず敬遠しがちです。しかし、どうしても「ここだけは変えたい」という要求があるのも事実です。

 そういった際、Bouncy Castleは変更しやすいオープンソースのライブラリとして重宝します。ぜひ参考にしてください。

参考資料

  1. Visual Studio 2008 Express Edition
  2. 『The Legion of the Bouncy Castle』
  3. 『RFC3820』
  4. 『グリッドにおけるセキュリティの概要と動向』(PDF)
  5. Proxy証明書の概要が分かりやすく記述されている(p.14)

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

torofield(トロフィールド)

某会社の従業員。仮の姿。真の姿があったりはしない。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー