ProxyCertInfo拡張の生成・解析実装方法
実装のコツ(私の場合)
Bouncy Castleは、膨大なライブラリなので、いきなり全部を見るのは不可能です。こういう場合は、同じような実装をしている部分をコピーすればいいと思います。
証明書拡張で、ちょっと難しい感じの場合、私は「AuthorityKeyIdentifier」か「SubjectKeyIdentifier」の実装を参考にします。
Bouncy Castleでは、
- csharp\crypto\src\asn1\x509\AuthorityKeyIdentifier.cs
に配置され、
namespace Org.BouncyCastle.Asn1.X509 public class AuthorityKeyIdentifier
として実装してあります。
ProxyCertInfo拡張の生成・解析のソースすべて
ProxyCertInfo拡張の生成・解析のソースは以下の通りです。
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拡張生成を行う場合は、以下のようになります。
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かどうかを確認する必要があります。
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で規定されているクラスです。
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拡張解析を行う場合は、以下のテストコードのように使用します。
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ですから、常に次の関数を呼びます。
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なので入っていない場合がある)ということで、場合分けをしています。
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は変更しやすいオープンソースのライブラリとして重宝します。ぜひ参考にしてください。
参考資料
- Visual Studio 2008 Express Edition
- 『The Legion of the Bouncy Castle』
- 『RFC3820』
- 『グリッドにおけるセキュリティの概要と動向』(PDF)