summary refs log tree commit diff
path: root/crypto/src/asn1/cmp
diff options
context:
space:
mode:
authorOren Novotny <oren@novotny.org>2014-08-26 17:39:02 -0400
committerOren Novotny <oren@novotny.org>2014-08-26 17:39:02 -0400
commit6dbc12162af086bbcfbd583dcaa8144d049c7fcc (patch)
tree3cf9443720ad508eb5ff615130d57fc118cb7145 /crypto/src/asn1/cmp
parentrename Crypto dir to crypto to match bc-git (diff)
parentRework the nonce-random initialisation and avoid GenerateSeed (diff)
downloadBouncyCastle.NET-ed25519-6dbc12162af086bbcfbd583dcaa8144d049c7fcc.tar.xz
Merge in bc-git to this repo
Diffstat (limited to 'crypto/src/asn1/cmp')
-rw-r--r--crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs60
-rw-r--r--crypto/src/asn1/cmp/CertConfirmContent.cs47
-rw-r--r--crypto/src/asn1/cmp/CertOrEncCert.cs85
-rw-r--r--crypto/src/asn1/cmp/CertRepMessage.cs94
-rw-r--r--crypto/src/asn1/cmp/CertResponse.cs115
-rw-r--r--crypto/src/asn1/cmp/CertStatus.cs84
-rw-r--r--crypto/src/asn1/cmp/CertifiedKeyPair.cs114
-rw-r--r--crypto/src/asn1/cmp/Challenge.cs79
-rw-r--r--crypto/src/asn1/cmp/CmpCertificate.cs80
-rw-r--r--crypto/src/asn1/cmp/CmpObjectIdentifiers.cs106
-rw-r--r--crypto/src/asn1/cmp/CrlAnnContent.cs49
-rw-r--r--crypto/src/asn1/cmp/ErrorMsgContent.cs94
-rw-r--r--crypto/src/asn1/cmp/GenMsgContent.cs52
-rw-r--r--crypto/src/asn1/cmp/GenRepContent.cs52
-rw-r--r--crypto/src/asn1/cmp/InfoTypeAndValue.cs121
-rw-r--r--crypto/src/asn1/cmp/KeyRecRepContent.cs115
-rw-r--r--crypto/src/asn1/cmp/OobCertHash.cs87
-rw-r--r--crypto/src/asn1/cmp/PKIBody.cs186
-rw-r--r--crypto/src/asn1/cmp/PKIConfirmContent.cs34
-rw-r--r--crypto/src/asn1/cmp/PKIFailureInfo.cs73
-rw-r--r--crypto/src/asn1/cmp/PKIFreeText.cs97
-rw-r--r--crypto/src/asn1/cmp/PKIHeader.cs237
-rw-r--r--crypto/src/asn1/cmp/PKIHeaderBuilder.cs223
-rw-r--r--crypto/src/asn1/cmp/PKIMessage.cs140
-rw-r--r--crypto/src/asn1/cmp/PKIMessages.cs52
-rw-r--r--crypto/src/asn1/cmp/PKIStatus.cs62
-rw-r--r--crypto/src/asn1/cmp/PKIStatusInfo.cs165
-rw-r--r--crypto/src/asn1/cmp/PbmParameter.cs100
-rw-r--r--crypto/src/asn1/cmp/PollRepContent.cs66
-rw-r--r--crypto/src/asn1/cmp/PollReqContent.cs59
-rw-r--r--crypto/src/asn1/cmp/PopoDecKeyChallContent.cs47
-rw-r--r--crypto/src/asn1/cmp/PopoDecKeyRespContent.cs47
-rw-r--r--crypto/src/asn1/cmp/ProtectedPart.cs58
-rw-r--r--crypto/src/asn1/cmp/RevAnnContent.cs86
-rw-r--r--crypto/src/asn1/cmp/RevDetails.cs75
-rw-r--r--crypto/src/asn1/cmp/RevRepContent.cs112
-rw-r--r--crypto/src/asn1/cmp/RevRepContentBuilder.cs55
-rw-r--r--crypto/src/asn1/cmp/RevReqContent.cs52
38 files changed, 3460 insertions, 0 deletions
diff --git a/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs b/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs
new file mode 100644
index 000000000..3cdb128a6
--- /dev/null
+++ b/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CAKeyUpdAnnContent
+		: Asn1Encodable
+	{
+		private readonly CmpCertificate oldWithNew;
+		private readonly CmpCertificate newWithOld;
+		private readonly CmpCertificate newWithNew;
+
+		private CAKeyUpdAnnContent(Asn1Sequence seq)
+		{
+			oldWithNew = CmpCertificate.GetInstance(seq[0]);
+			newWithOld = CmpCertificate.GetInstance(seq[1]);
+			newWithNew = CmpCertificate.GetInstance(seq[2]);
+		}
+
+		public static CAKeyUpdAnnContent GetInstance(object obj)
+		{
+			if (obj is CAKeyUpdAnnContent)
+				return (CAKeyUpdAnnContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new CAKeyUpdAnnContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual CmpCertificate OldWithNew
+		{
+			get { return oldWithNew; }
+		}
+		
+		public virtual CmpCertificate NewWithOld
+		{
+			get { return newWithOld; }
+		}
+
+		public virtual CmpCertificate NewWithNew
+		{
+			get { return newWithNew; }
+		}
+
+		/**
+		 * <pre>
+		 * CAKeyUpdAnnContent ::= SEQUENCE {
+		 *                             oldWithNew   CmpCertificate, -- old pub signed with new priv
+		 *                             newWithOld   CmpCertificate, -- new pub signed with old priv
+		 *                             newWithNew   CmpCertificate  -- new pub signed with new priv
+		 *  }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return new DerSequence(oldWithNew, newWithOld, newWithNew);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CertConfirmContent.cs b/crypto/src/asn1/cmp/CertConfirmContent.cs
new file mode 100644
index 000000000..f4016d8d8
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertConfirmContent.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CertConfirmContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+
+		private CertConfirmContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static CertConfirmContent GetInstance(object obj)
+		{
+			if (obj is CertConfirmContent)
+				return (CertConfirmContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new CertConfirmContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual CertStatus[] ToCertStatusArray()
+		{
+			CertStatus[] result = new CertStatus[content.Count];
+			for (int i = 0; i != result.Length; i++)
+			{
+				result[i] = CertStatus.GetInstance(content[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * CertConfirmContent ::= SEQUENCE OF CertStatus
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CertOrEncCert.cs b/crypto/src/asn1/cmp/CertOrEncCert.cs
new file mode 100644
index 000000000..4c049c180
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertOrEncCert.cs
@@ -0,0 +1,85 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CertOrEncCert
+		: Asn1Encodable, IAsn1Choice
+	{
+		private readonly CmpCertificate certificate;
+		private readonly EncryptedValue encryptedCert;
+
+		private CertOrEncCert(Asn1TaggedObject tagged)
+		{
+			if (tagged.TagNo == 0)
+			{
+				certificate = CmpCertificate.GetInstance(tagged.GetObject());
+			}
+			else if (tagged.TagNo == 1)
+			{
+				encryptedCert = EncryptedValue.GetInstance(tagged.GetObject());
+			}
+			else
+			{
+				throw new ArgumentException("unknown tag: " + tagged.TagNo, "tagged");
+			}
+		}
+		
+		public static CertOrEncCert GetInstance(object obj)
+		{
+			if (obj is CertOrEncCert)
+				return (CertOrEncCert)obj;
+
+			if (obj is Asn1TaggedObject)
+				return new CertOrEncCert((Asn1TaggedObject)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public CertOrEncCert(CmpCertificate certificate)
+		{
+			if (certificate == null)
+				throw new ArgumentNullException("certificate");
+
+			this.certificate = certificate;
+		}
+
+		public CertOrEncCert(EncryptedValue encryptedCert)
+		{
+			if (encryptedCert == null)
+				throw new ArgumentNullException("encryptedCert");
+
+			this.encryptedCert = encryptedCert;
+		}
+
+		public virtual CmpCertificate Certificate
+		{
+			get { return certificate; }
+		}
+
+		public virtual EncryptedValue EncryptedCert
+		{
+			get { return encryptedCert; }
+		}
+
+		/**
+		 * <pre>
+		 * CertOrEncCert ::= CHOICE {
+		 *                      certificate     [0] CMPCertificate,
+		 *                      encryptedCert   [1] EncryptedValue
+		 *           }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			if (certificate != null)
+			{
+				return new DerTaggedObject(true, 0, certificate);
+			}
+
+			return new DerTaggedObject(true, 1, encryptedCert);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CertRepMessage.cs b/crypto/src/asn1/cmp/CertRepMessage.cs
new file mode 100644
index 000000000..c22b079c8
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertRepMessage.cs
@@ -0,0 +1,94 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CertRepMessage
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence caPubs;
+		private readonly Asn1Sequence response;
+		
+		private CertRepMessage(Asn1Sequence seq)
+		{
+			int index = 0;
+
+			if (seq.Count > 1)
+			{
+				caPubs = Asn1Sequence.GetInstance((Asn1TaggedObject)seq[index++], true);
+			}
+
+			response = Asn1Sequence.GetInstance(seq[index]);
+		}
+
+		public static CertRepMessage GetInstance(object obj)
+		{
+			if (obj is CertRepMessage)
+				return (CertRepMessage)obj;
+
+			if (obj is Asn1Sequence)
+				return new CertRepMessage((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public CertRepMessage(CmpCertificate[] caPubs, CertResponse[] response)
+		{
+			if (response == null)
+				throw new ArgumentNullException("response");
+
+			if (caPubs != null)
+			{
+				this.caPubs = new DerSequence(caPubs);
+			}
+
+			this.response = new DerSequence(response);
+		}
+
+		public virtual CmpCertificate[] GetCAPubs()
+		{
+			if (caPubs == null)
+				return null;
+
+			CmpCertificate[] results = new CmpCertificate[caPubs.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = CmpCertificate.GetInstance(caPubs[i]);
+			}
+			return results;
+		}
+
+		public virtual CertResponse[] GetResponse()
+		{
+			CertResponse[] results = new CertResponse[response.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = CertResponse.GetInstance(response[i]);
+			}
+			return results;
+		}
+
+		/**
+		 * <pre>
+		 * CertRepMessage ::= SEQUENCE {
+		 *                          caPubs       [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate
+		 *                                                                             OPTIONAL,
+		 *                          response         SEQUENCE OF CertResponse
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector();
+
+			if (caPubs != null)
+			{
+				v.Add(new DerTaggedObject(true, 1, caPubs));
+			}
+
+			v.Add(response);
+
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CertResponse.cs b/crypto/src/asn1/cmp/CertResponse.cs
new file mode 100644
index 000000000..246b8ce70
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertResponse.cs
@@ -0,0 +1,115 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CertResponse
+		: Asn1Encodable
+	{
+		private readonly DerInteger certReqId;
+		private readonly PkiStatusInfo status;
+		private readonly CertifiedKeyPair certifiedKeyPair;
+		private readonly Asn1OctetString rspInfo;
+
+		private CertResponse(Asn1Sequence seq)
+		{
+			certReqId = DerInteger.GetInstance(seq[0]);
+			status = PkiStatusInfo.GetInstance(seq[1]);
+
+			if (seq.Count >= 3)
+			{
+				if (seq.Count == 3)
+				{
+					Asn1Encodable o = seq[2];
+					if (o is Asn1OctetString)
+					{
+						rspInfo = Asn1OctetString.GetInstance(o);
+					}
+					else
+					{
+						certifiedKeyPair = CertifiedKeyPair.GetInstance(o);
+					}
+				}
+				else
+				{
+					certifiedKeyPair = CertifiedKeyPair.GetInstance(seq[2]);
+					rspInfo = Asn1OctetString.GetInstance(seq[3]);
+				}
+			}
+		}
+
+		public static CertResponse GetInstance(object obj)
+		{
+			if (obj is CertResponse)
+				return (CertResponse)obj;
+
+			if (obj is Asn1Sequence)
+				return new CertResponse((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public CertResponse(
+			DerInteger certReqId,
+			PkiStatusInfo status)
+			: this(certReqId, status, null, null)
+		{
+		}
+
+		public CertResponse(
+			DerInteger			certReqId,
+			PkiStatusInfo		status,
+			CertifiedKeyPair	certifiedKeyPair,
+			Asn1OctetString		rspInfo)
+		{
+			if (certReqId == null)
+				throw new ArgumentNullException("certReqId");
+
+			if (status == null)
+				throw new ArgumentNullException("status");
+
+			this.certReqId = certReqId;
+			this.status = status;
+			this.certifiedKeyPair = certifiedKeyPair;
+			this.rspInfo = rspInfo;
+		}
+
+		public virtual DerInteger CertReqID
+		{
+			get { return certReqId; }
+		}
+
+		public virtual PkiStatusInfo Status
+		{
+			get { return status; }
+		}
+
+		public virtual CertifiedKeyPair CertifiedKeyPair
+		{
+			get { return certifiedKeyPair; }
+		}
+
+		/**
+		 * <pre>
+		 * CertResponse ::= SEQUENCE {
+		 *                            certReqId           INTEGER,
+		 *                            -- to match this response with corresponding request (a value
+		 *                            -- of -1 is to be used if certReqId is not specified in the
+		 *                            -- corresponding request)
+		 *                            status              PKIStatusInfo,
+		 *                            certifiedKeyPair    CertifiedKeyPair    OPTIONAL,
+		 *                            rspInfo             OCTET STRING        OPTIONAL
+		 *                            -- analogous to the id-regInfo-utf8Pairs string defined
+		 *                            -- for regInfo in CertReqMsg [CRMF]
+		 *             }
+		 * </pre> 
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(certReqId, status);
+			v.AddOptional(certifiedKeyPair);
+			v.AddOptional(rspInfo);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CertStatus.cs b/crypto/src/asn1/cmp/CertStatus.cs
new file mode 100644
index 000000000..52d5ac504
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertStatus.cs
@@ -0,0 +1,84 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CertStatus
+		: Asn1Encodable
+	{
+		private readonly Asn1OctetString certHash;
+		private readonly DerInteger certReqId;
+		private readonly PkiStatusInfo statusInfo;
+
+		private CertStatus(Asn1Sequence seq)
+		{
+			certHash = Asn1OctetString.GetInstance(seq[0]);
+			certReqId = DerInteger.GetInstance(seq[1]);
+
+			if (seq.Count > 2)
+			{
+				statusInfo = PkiStatusInfo.GetInstance(seq[2]);
+			}
+		}
+
+		public CertStatus(byte[] certHash, BigInteger certReqId)
+		{
+			this.certHash = new DerOctetString(certHash);
+			this.certReqId = new DerInteger(certReqId);
+		}
+
+		public CertStatus(byte[] certHash, BigInteger certReqId, PkiStatusInfo statusInfo)
+		{
+			this.certHash = new DerOctetString(certHash);
+			this.certReqId = new DerInteger(certReqId);
+			this.statusInfo = statusInfo;
+		}
+
+		public static CertStatus GetInstance(object obj)
+		{
+			if (obj is CertStatus)
+				return (CertStatus)obj;
+
+			if (obj is Asn1Sequence)
+				return new CertStatus((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual Asn1OctetString CertHash
+		{
+			get { return certHash; }
+		}
+
+		public virtual DerInteger CertReqID
+		{
+			get { return certReqId; }
+		}
+
+		public virtual PkiStatusInfo StatusInfo
+		{
+			get { return statusInfo; }
+		}
+
+		/**
+		 * <pre>
+		 * CertStatus ::= SEQUENCE {
+		 *                   certHash    OCTET STRING,
+		 *                   -- the hash of the certificate, using the same hash algorithm
+		 *                   -- as is used to create and verify the certificate signature
+		 *                   certReqId   INTEGER,
+		 *                   -- to match this confirmation with the corresponding req/rep
+		 *                   statusInfo  PKIStatusInfo OPTIONAL
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(certHash, certReqId);
+			v.AddOptional(statusInfo);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CertifiedKeyPair.cs b/crypto/src/asn1/cmp/CertifiedKeyPair.cs
new file mode 100644
index 000000000..655dde0c5
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertifiedKeyPair.cs
@@ -0,0 +1,114 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CertifiedKeyPair
+		: Asn1Encodable
+	{
+		private readonly CertOrEncCert certOrEncCert;
+		private readonly EncryptedValue privateKey;
+		private readonly PkiPublicationInfo publicationInfo;
+
+		private CertifiedKeyPair(Asn1Sequence seq)
+		{
+			certOrEncCert = CertOrEncCert.GetInstance(seq[0]);
+
+			if (seq.Count >= 2)
+			{
+				if (seq.Count == 2)
+				{
+					Asn1TaggedObject tagged = Asn1TaggedObject.GetInstance(seq[1]);
+					if (tagged.TagNo == 0)
+					{
+						privateKey = EncryptedValue.GetInstance(tagged.GetObject());
+					}
+					else
+					{
+						publicationInfo = PkiPublicationInfo.GetInstance(tagged.GetObject());
+					}
+				}
+				else
+				{
+					privateKey = EncryptedValue.GetInstance(Asn1TaggedObject.GetInstance(seq[1]));
+					publicationInfo = PkiPublicationInfo.GetInstance(Asn1TaggedObject.GetInstance(seq[2]));
+				}
+			}
+		}
+
+		public static CertifiedKeyPair GetInstance(object obj)
+		{
+			if (obj is CertifiedKeyPair)
+				return (CertifiedKeyPair)obj;
+
+			if (obj is Asn1Sequence)
+				return new CertifiedKeyPair((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public CertifiedKeyPair(
+			CertOrEncCert certOrEncCert)
+			: this(certOrEncCert, null, null)
+		{
+		}
+
+		public CertifiedKeyPair(
+			CertOrEncCert		certOrEncCert,
+			EncryptedValue		privateKey,
+			PkiPublicationInfo	publicationInfo
+		)
+		{
+			if (certOrEncCert == null)
+				throw new ArgumentNullException("certOrEncCert");
+
+			this.certOrEncCert = certOrEncCert;
+			this.privateKey = privateKey;
+			this.publicationInfo = publicationInfo;
+		}
+
+		public virtual CertOrEncCert CertOrEncCert
+		{
+			get { return certOrEncCert; }
+		}
+
+		public virtual EncryptedValue PrivateKey
+		{
+			get { return privateKey; }
+		}
+
+		public virtual PkiPublicationInfo PublicationInfo
+		{
+			get { return publicationInfo; }
+		}
+
+		/**
+		 * <pre>
+		 * CertifiedKeyPair ::= SEQUENCE {
+		 *                                  certOrEncCert       CertOrEncCert,
+		 *                                  privateKey      [0] EncryptedValue      OPTIONAL,
+		 *                                  -- see [CRMF] for comment on encoding
+		 *                                  publicationInfo [1] PKIPublicationInfo  OPTIONAL
+		 *       }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(certOrEncCert);
+
+			if (privateKey != null)
+			{
+				v.Add(new DerTaggedObject(true, 0, privateKey));
+			}
+
+			if (publicationInfo != null)
+			{
+				v.Add(new DerTaggedObject(true, 1, publicationInfo));
+			}
+
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/Challenge.cs b/crypto/src/asn1/cmp/Challenge.cs
new file mode 100644
index 000000000..bee5f96f5
--- /dev/null
+++ b/crypto/src/asn1/cmp/Challenge.cs
@@ -0,0 +1,79 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class Challenge
+		: Asn1Encodable
+	{
+		private readonly AlgorithmIdentifier owf;
+		private readonly Asn1OctetString witness;
+		private readonly Asn1OctetString challenge;
+
+		private Challenge(Asn1Sequence seq)
+		{
+			int index = 0;
+
+			if (seq.Count == 3)
+			{
+				owf = AlgorithmIdentifier.GetInstance(seq[index++]);
+			}
+
+			witness = Asn1OctetString.GetInstance(seq[index++]);
+			challenge = Asn1OctetString.GetInstance(seq[index]);
+		}
+
+		public static Challenge GetInstance(object obj)
+		{
+			if (obj is Challenge)
+				return (Challenge)obj;
+
+			if (obj is Asn1Sequence)
+				return new Challenge((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual AlgorithmIdentifier Owf
+		{
+			get { return owf; }
+		}
+
+		/**
+		 * <pre>
+		 * Challenge ::= SEQUENCE {
+		 *                 owf                 AlgorithmIdentifier  OPTIONAL,
+		 *
+		 *                 -- MUST be present in the first Challenge; MAY be omitted in
+		 *                 -- any subsequent Challenge in POPODecKeyChallContent (if
+		 *                 -- omitted, then the owf used in the immediately preceding
+		 *                 -- Challenge is to be used).
+		 *
+		 *                 witness             OCTET STRING,
+		 *                 -- the result of applying the one-way function (owf) to a
+		 *                 -- randomly-generated INTEGER, A.  [Note that a different
+		 *                 -- INTEGER MUST be used for each Challenge.]
+		 *                 challenge           OCTET STRING
+		 *                 -- the encryption (under the public key for which the cert.
+		 *                 -- request is being made) of Rand, where Rand is specified as
+		 *                 --   Rand ::= SEQUENCE {
+		 *                 --      int      INTEGER,
+		 *                 --       - the randomly-generated INTEGER A (above)
+		 *                 --      sender   GeneralName
+		 *                 --       - the sender's name (as included in PKIHeader)
+		 *                 --   }
+		 *      }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector();
+			v.AddOptional(owf);
+			v.Add(witness);
+			v.Add(challenge);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/CmpCertificate.cs b/crypto/src/asn1/cmp/CmpCertificate.cs
new file mode 100644
index 000000000..16ee30059
--- /dev/null
+++ b/crypto/src/asn1/cmp/CmpCertificate.cs
@@ -0,0 +1,80 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    public class CmpCertificate
+        : Asn1Encodable, IAsn1Choice
+    {
+        private readonly X509CertificateStructure x509v3PKCert;
+        private readonly AttributeCertificate x509v2AttrCert;
+
+        /**
+         * Note: the addition of attribute certificates is a BC extension.
+         */
+        public CmpCertificate(AttributeCertificate x509v2AttrCert)
+        {
+            this.x509v2AttrCert = x509v2AttrCert;
+        }
+
+        public CmpCertificate(X509CertificateStructure x509v3PKCert)
+        {
+            if (x509v3PKCert.Version != 3)
+                throw new ArgumentException("only version 3 certificates allowed", "x509v3PKCert");
+
+            this.x509v3PKCert = x509v3PKCert;
+        }
+
+        public static CmpCertificate GetInstance(object obj)
+        {
+            if (obj is CmpCertificate)
+                return (CmpCertificate)obj;
+
+            if (obj is Asn1Sequence)
+                return new CmpCertificate(X509CertificateStructure.GetInstance(obj));
+
+            if (obj is Asn1TaggedObject)
+                return new CmpCertificate(AttributeCertificate.GetInstance(((Asn1TaggedObject)obj).GetObject()));
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+        }
+
+        public virtual bool IsX509v3PKCert
+        {
+            get { return x509v3PKCert != null; }
+        }
+
+        public virtual X509CertificateStructure X509v3PKCert
+        {
+            get { return x509v3PKCert; }
+        }
+
+        public virtual AttributeCertificate X509v2AttrCert
+        {
+            get { return x509v2AttrCert; }
+        }
+
+        /**
+         * <pre>
+         * CMPCertificate ::= CHOICE {
+         *            x509v3PKCert        Certificate
+         *            x509v2AttrCert      [1] AttributeCertificate
+         *  }
+         * </pre>
+         * Note: the addition of attribute certificates is a BC extension.
+         *
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            if (x509v2AttrCert != null)
+            {
+                // explicit following CMP conventions
+                return new DerTaggedObject(true, 1, x509v2AttrCert);
+            }
+
+            return x509v3PKCert.ToAsn1Object();
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
new file mode 100644
index 000000000..7e8274175
--- /dev/null
+++ b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
@@ -0,0 +1,106 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public abstract class CmpObjectIdentifiers
+	{
+		// RFC 4210
+
+		// id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13}
+		public static readonly DerObjectIdentifier passwordBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.13");
+
+		// id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30}
+		public static readonly DerObjectIdentifier dhBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.30");
+
+		// Example InfoTypeAndValue contents include, but are not limited
+		// to, the following (un-comment in this ASN.1 module and use as
+		// appropriate for a given environment):
+		//
+		// id-it-caProtEncCert OBJECT IDENTIFIER ::= {id-it 1}
+		// CAProtEncCertValue ::= CMPCertificate
+		// id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2}
+		// SignKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
+		// id-it-encKeyPairTypes OBJECT IDENTIFIER ::= {id-it 3}
+		// EncKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
+		// id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4}
+		// PreferredSymmAlgValue ::= AlgorithmIdentifier
+		// id-it-caKeyUpdateInfo OBJECT IDENTIFIER ::= {id-it 5}
+		// CAKeyUpdateInfoValue ::= CAKeyUpdAnnContent
+		// id-it-currentCRL OBJECT IDENTIFIER ::= {id-it 6}
+		// CurrentCRLValue ::= CertificateList
+		// id-it-unsupportedOIDs OBJECT IDENTIFIER ::= {id-it 7}
+		// UnsupportedOIDsValue ::= SEQUENCE OF OBJECT IDENTIFIER
+		// id-it-keyPairParamReq OBJECT IDENTIFIER ::= {id-it 10}
+		// KeyPairParamReqValue ::= OBJECT IDENTIFIER
+		// id-it-keyPairParamRep OBJECT IDENTIFIER ::= {id-it 11}
+		// KeyPairParamRepValue ::= AlgorithmIdentifer
+		// id-it-revPassphrase OBJECT IDENTIFIER ::= {id-it 12}
+		// RevPassphraseValue ::= EncryptedValue
+		// id-it-implicitConfirm OBJECT IDENTIFIER ::= {id-it 13}
+		// ImplicitConfirmValue ::= NULL
+		// id-it-confirmWaitTime OBJECT IDENTIFIER ::= {id-it 14}
+		// ConfirmWaitTimeValue ::= GeneralizedTime
+		// id-it-origPKIMessage OBJECT IDENTIFIER ::= {id-it 15}
+		// OrigPKIMessageValue ::= PKIMessages
+		// id-it-suppLangTags OBJECT IDENTIFIER ::= {id-it 16}
+		// SuppLangTagsValue ::= SEQUENCE OF UTF8String
+		//
+		// where
+		//
+		// id-pkix OBJECT IDENTIFIER ::= {
+		// iso(1) identified-organization(3)
+		// dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
+		// and
+		// id-it OBJECT IDENTIFIER ::= {id-pkix 4}
+		public static readonly DerObjectIdentifier it_caProtEncCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.1");
+		public static readonly DerObjectIdentifier it_signKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.2");
+		public static readonly DerObjectIdentifier it_encKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.3");
+		public static readonly DerObjectIdentifier it_preferredSymAlg = new DerObjectIdentifier("1.3.6.1.5.5.7.4.4");
+		public static readonly DerObjectIdentifier it_caKeyUpdateInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.4.5");
+		public static readonly DerObjectIdentifier it_currentCRL = new DerObjectIdentifier("1.3.6.1.5.5.7.4.6");
+		public static readonly DerObjectIdentifier it_unsupportedOIDs = new DerObjectIdentifier("1.3.6.1.5.5.7.4.7");
+		public static readonly DerObjectIdentifier it_keyPairParamReq = new DerObjectIdentifier("1.3.6.1.5.5.7.4.10");
+		public static readonly DerObjectIdentifier it_keyPairParamRep = new DerObjectIdentifier("1.3.6.1.5.5.7.4.11");
+		public static readonly DerObjectIdentifier it_revPassphrase = new DerObjectIdentifier("1.3.6.1.5.5.7.4.12");
+		public static readonly DerObjectIdentifier it_implicitConfirm = new DerObjectIdentifier("1.3.6.1.5.5.7.4.13");
+		public static readonly DerObjectIdentifier it_confirmWaitTime = new DerObjectIdentifier("1.3.6.1.5.5.7.4.14");
+		public static readonly DerObjectIdentifier it_origPKIMessage = new DerObjectIdentifier("1.3.6.1.5.5.7.4.15");
+		public static readonly DerObjectIdentifier it_suppLangTags = new DerObjectIdentifier("1.3.6.1.5.5.7.4.16");
+
+		// RFC 4211
+
+		// id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+		// dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
+		//
+		// arc for Internet X.509 PKI protocols and their components
+		// id-pkip OBJECT IDENTIFIER :: { id-pkix pkip(5) }
+		//
+		// arc for Registration Controls in CRMF
+		// id-regCtrl OBJECT IDENTIFIER ::= { id-pkip regCtrl(1) }
+		//
+		// arc for Registration Info in CRMF
+		// id-regInfo OBJECT IDENTIFIER ::= { id-pkip id-regInfo(2) }
+
+		public static readonly DerObjectIdentifier regCtrl_regToken = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.1");
+		public static readonly DerObjectIdentifier regCtrl_authenticator = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.2");
+		public static readonly DerObjectIdentifier regCtrl_pkiPublicationInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.3");
+		public static readonly DerObjectIdentifier regCtrl_pkiArchiveOptions = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.4");
+		public static readonly DerObjectIdentifier regCtrl_oldCertID = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.5");
+		public static readonly DerObjectIdentifier regCtrl_protocolEncrKey = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.6");
+
+		// From RFC4210:
+		// id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}
+		public static readonly DerObjectIdentifier regCtrl_altCertTemplate = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.7");
+
+		public static readonly DerObjectIdentifier regInfo_utf8Pairs = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.1");
+		public static readonly DerObjectIdentifier regInfo_certReq = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.2");
+
+		// id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+		// us(840) rsadsi(113549) pkcs(1) pkcs9(9) 16 }
+		//
+		// id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types
+		//
+		// id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
+		public static readonly DerObjectIdentifier ct_encKeyWithID = new DerObjectIdentifier("1.2.840.113549.1.9.16.1.21");
+	}
+}
diff --git a/crypto/src/asn1/cmp/CrlAnnContent.cs b/crypto/src/asn1/cmp/CrlAnnContent.cs
new file mode 100644
index 000000000..3dc11d32c
--- /dev/null
+++ b/crypto/src/asn1/cmp/CrlAnnContent.cs
@@ -0,0 +1,49 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class CrlAnnContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+
+		private CrlAnnContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static CrlAnnContent GetInstance(object obj)
+		{
+			if (obj is CrlAnnContent)
+				return (CrlAnnContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new CrlAnnContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual CertificateList[] ToCertificateListArray()
+		{
+			CertificateList[] result = new CertificateList[content.Count];
+			for (int i = 0; i != result.Length; ++ i)
+			{
+				result[i] = CertificateList.GetInstance(content[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * CrlAnnContent ::= SEQUENCE OF CertificateList
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/ErrorMsgContent.cs b/crypto/src/asn1/cmp/ErrorMsgContent.cs
new file mode 100644
index 000000000..f4dc584ea
--- /dev/null
+++ b/crypto/src/asn1/cmp/ErrorMsgContent.cs
@@ -0,0 +1,94 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class ErrorMsgContent
+		: Asn1Encodable
+	{
+		private readonly PkiStatusInfo pkiStatusInfo;
+		private readonly DerInteger errorCode;
+		private readonly PkiFreeText errorDetails;
+
+		private ErrorMsgContent(Asn1Sequence seq)
+		{
+			pkiStatusInfo = PkiStatusInfo.GetInstance(seq[0]);
+
+			for (int pos = 1; pos < seq.Count; ++pos)
+			{
+				Asn1Encodable ae = seq[pos];
+				if (ae is DerInteger)
+				{
+					errorCode = DerInteger.GetInstance(ae);
+				}
+				else
+				{
+					errorDetails = PkiFreeText.GetInstance(ae);
+				}
+			}
+		}
+
+		public static ErrorMsgContent GetInstance(object obj)
+		{
+			if (obj is ErrorMsgContent)
+				return (ErrorMsgContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new ErrorMsgContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public ErrorMsgContent(PkiStatusInfo pkiStatusInfo)
+			: this(pkiStatusInfo, null, null)
+		{
+		}
+
+		public ErrorMsgContent(
+			PkiStatusInfo	pkiStatusInfo,
+			DerInteger		errorCode,
+			PkiFreeText		errorDetails)
+		{
+			if (pkiStatusInfo == null)
+				throw new ArgumentNullException("pkiStatusInfo");
+
+			this.pkiStatusInfo = pkiStatusInfo;
+			this.errorCode = errorCode;
+			this.errorDetails = errorDetails;
+		}
+		
+		public virtual PkiStatusInfo PkiStatusInfo
+		{
+			get { return pkiStatusInfo; }
+		}
+
+		public virtual DerInteger ErrorCode
+		{
+			get { return errorCode; }
+		}
+
+		public virtual PkiFreeText ErrorDetails
+		{
+			get { return errorDetails; }
+		}
+
+		/**
+		 * <pre>
+		 * ErrorMsgContent ::= SEQUENCE {
+		 *                        pKIStatusInfo          PKIStatusInfo,
+		 *                        errorCode              INTEGER           OPTIONAL,
+		 *                        -- implementation-specific error codes
+		 *                        errorDetails           PKIFreeText       OPTIONAL
+		 *                        -- implementation-specific error details
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(pkiStatusInfo);
+			v.AddOptional(errorCode);
+			v.AddOptional(errorDetails);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/GenMsgContent.cs b/crypto/src/asn1/cmp/GenMsgContent.cs
new file mode 100644
index 000000000..9f042491c
--- /dev/null
+++ b/crypto/src/asn1/cmp/GenMsgContent.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class GenMsgContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+
+		private GenMsgContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static GenMsgContent GetInstance(object obj)
+		{
+			if (obj is GenMsgContent)
+				return (GenMsgContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new GenMsgContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public GenMsgContent(params InfoTypeAndValue[] itv)
+		{
+			content = new DerSequence(itv);
+		}
+
+		public virtual InfoTypeAndValue[] ToInfoTypeAndValueArray()
+		{
+			InfoTypeAndValue[] result = new InfoTypeAndValue[content.Count];
+			for (int i = 0; i != result.Length; ++i)
+			{
+				result[i] = InfoTypeAndValue.GetInstance(content[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * GenMsgContent ::= SEQUENCE OF InfoTypeAndValue
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/GenRepContent.cs b/crypto/src/asn1/cmp/GenRepContent.cs
new file mode 100644
index 000000000..5bdc5550a
--- /dev/null
+++ b/crypto/src/asn1/cmp/GenRepContent.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class GenRepContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+
+		private GenRepContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static GenRepContent GetInstance(object obj)
+		{
+			if (obj is GenRepContent)
+				return (GenRepContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new GenRepContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public GenRepContent(params InfoTypeAndValue[] itv)
+		{
+			content = new DerSequence(itv);
+		}
+
+		public virtual InfoTypeAndValue[] ToInfoTypeAndValueArray()
+		{
+			InfoTypeAndValue[] result = new InfoTypeAndValue[content.Count];
+			for (int i = 0; i != result.Length; ++i)
+			{
+				result[i] = InfoTypeAndValue.GetInstance(content[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * GenRepContent ::= SEQUENCE OF InfoTypeAndValue
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/InfoTypeAndValue.cs b/crypto/src/asn1/cmp/InfoTypeAndValue.cs
new file mode 100644
index 000000000..9b51dba02
--- /dev/null
+++ b/crypto/src/asn1/cmp/InfoTypeAndValue.cs
@@ -0,0 +1,121 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * Example InfoTypeAndValue contents include, but are not limited
+     * to, the following (un-comment in this ASN.1 module and use as
+     * appropriate for a given environment):
+     * <pre>
+     *   id-it-caProtEncCert    OBJECT IDENTIFIER ::= {id-it 1}
+     *      CAProtEncCertValue      ::= CMPCertificate
+     *   id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2}
+     *     SignKeyPairTypesValue   ::= SEQUENCE OF AlgorithmIdentifier
+     *   id-it-encKeyPairTypes  OBJECT IDENTIFIER ::= {id-it 3}
+     *     EncKeyPairTypesValue    ::= SEQUENCE OF AlgorithmIdentifier
+     *   id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4}
+     *      PreferredSymmAlgValue   ::= AlgorithmIdentifier
+     *   id-it-caKeyUpdateInfo  OBJECT IDENTIFIER ::= {id-it 5}
+     *      CAKeyUpdateInfoValue    ::= CAKeyUpdAnnContent
+     *   id-it-currentCRL       OBJECT IDENTIFIER ::= {id-it 6}
+     *      CurrentCRLValue         ::= CertificateList
+     *   id-it-unsupportedOIDs  OBJECT IDENTIFIER ::= {id-it 7}
+     *      UnsupportedOIDsValue    ::= SEQUENCE OF OBJECT IDENTIFIER
+     *   id-it-keyPairParamReq  OBJECT IDENTIFIER ::= {id-it 10}
+     *      KeyPairParamReqValue    ::= OBJECT IDENTIFIER
+     *   id-it-keyPairParamRep  OBJECT IDENTIFIER ::= {id-it 11}
+     *      KeyPairParamRepValue    ::= AlgorithmIdentifer
+     *   id-it-revPassphrase    OBJECT IDENTIFIER ::= {id-it 12}
+     *      RevPassphraseValue      ::= EncryptedValue
+     *   id-it-implicitConfirm  OBJECT IDENTIFIER ::= {id-it 13}
+     *      ImplicitConfirmValue    ::= NULL
+     *   id-it-confirmWaitTime  OBJECT IDENTIFIER ::= {id-it 14}
+     *      ConfirmWaitTimeValue    ::= GeneralizedTime
+     *   id-it-origPKIMessage   OBJECT IDENTIFIER ::= {id-it 15}
+     *      OrigPKIMessageValue     ::= PKIMessages
+     *   id-it-suppLangTags     OBJECT IDENTIFIER ::= {id-it 16}
+     *      SuppLangTagsValue       ::= SEQUENCE OF UTF8String
+     *
+     * where
+     *
+     *   id-pkix OBJECT IDENTIFIER ::= {
+     *      iso(1) identified-organization(3)
+     *      dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
+     * and
+     *      id-it   OBJECT IDENTIFIER ::= {id-pkix 4}
+     * </pre>
+     */
+    public class InfoTypeAndValue
+        : Asn1Encodable
+    {
+        private readonly DerObjectIdentifier infoType;
+        private readonly Asn1Encodable infoValue;
+
+        private InfoTypeAndValue(Asn1Sequence seq)
+        {
+            infoType = DerObjectIdentifier.GetInstance(seq[0]);
+
+            if (seq.Count > 1)
+            {
+                infoValue = (Asn1Encodable)seq[1];
+            }
+        }
+
+        public static InfoTypeAndValue GetInstance(object obj)
+        {
+            if (obj is InfoTypeAndValue)
+                return (InfoTypeAndValue)obj;
+
+            if (obj is Asn1Sequence)
+                return new InfoTypeAndValue((Asn1Sequence)obj);
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+        }
+
+        public InfoTypeAndValue(
+            DerObjectIdentifier infoType)
+        {
+            this.infoType = infoType;
+            this.infoValue = null;
+        }
+
+        public InfoTypeAndValue(
+            DerObjectIdentifier infoType,
+            Asn1Encodable       optionalValue)
+        {
+            this.infoType = infoType;
+            this.infoValue = optionalValue;
+        }
+
+        public virtual DerObjectIdentifier InfoType
+        {
+            get { return infoType; }
+        }
+
+        public virtual Asn1Encodable InfoValue
+        {
+            get { return infoValue; }
+        }
+
+        /**
+         * <pre>
+         * InfoTypeAndValue ::= SEQUENCE {
+         *                         infoType               OBJECT IDENTIFIER,
+         *                         infoValue              ANY DEFINED BY infoType  OPTIONAL
+         * }
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(infoType);
+
+            if (infoValue != null)
+            {
+                v.Add(infoValue);
+            }
+
+            return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/KeyRecRepContent.cs b/crypto/src/asn1/cmp/KeyRecRepContent.cs
new file mode 100644
index 000000000..b0352f048
--- /dev/null
+++ b/crypto/src/asn1/cmp/KeyRecRepContent.cs
@@ -0,0 +1,115 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class KeyRecRepContent
+		: Asn1Encodable
+	{
+		private readonly PkiStatusInfo status;
+		private readonly CmpCertificate newSigCert;
+		private readonly Asn1Sequence caCerts;
+		private readonly Asn1Sequence keyPairHist;
+
+		private KeyRecRepContent(Asn1Sequence seq)
+		{
+			status = PkiStatusInfo.GetInstance(seq[0]);
+
+			for (int pos = 1; pos < seq.Count; ++pos)
+			{
+				Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]);
+
+				switch (tObj.TagNo)
+				{
+					case 0:
+						newSigCert = CmpCertificate.GetInstance(tObj.GetObject());
+						break;
+					case 1:
+						caCerts = Asn1Sequence.GetInstance(tObj.GetObject());
+						break;
+					case 2:
+						keyPairHist = Asn1Sequence.GetInstance(tObj.GetObject());
+						break;
+					default:
+						throw new ArgumentException("unknown tag number: " + tObj.TagNo, "seq");
+				}
+			}
+		}
+
+		public static KeyRecRepContent GetInstance(object obj)
+		{
+			if (obj is KeyRecRepContent)
+				return (KeyRecRepContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new KeyRecRepContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual PkiStatusInfo Status
+		{
+			get { return status; }
+		}
+
+		public virtual CmpCertificate NewSigCert
+		{
+			get { return newSigCert; }
+		}
+
+		public virtual CmpCertificate[] GetCACerts()
+		{
+			if (caCerts == null)
+				return null;
+
+			CmpCertificate[] results = new CmpCertificate[caCerts.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = CmpCertificate.GetInstance(caCerts[i]);
+			}
+			return results;
+		}
+
+		public virtual CertifiedKeyPair[] GetKeyPairHist()
+		{
+			if (keyPairHist == null)
+				return null;
+
+			CertifiedKeyPair[] results = new CertifiedKeyPair[keyPairHist.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = CertifiedKeyPair.GetInstance(keyPairHist[i]);
+			}
+			return results;
+		}
+
+		/**
+		 * <pre>
+		 * KeyRecRepContent ::= SEQUENCE {
+		 *                         status                  PKIStatusInfo,
+		 *                         newSigCert          [0] CMPCertificate OPTIONAL,
+		 *                         caCerts             [1] SEQUENCE SIZE (1..MAX) OF
+		 *                                                           CMPCertificate OPTIONAL,
+		 *                         keyPairHist         [2] SEQUENCE SIZE (1..MAX) OF
+		 *                                                           CertifiedKeyPair OPTIONAL
+		 *              }
+		 * </pre> 
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(status);
+			AddOptional(v, 0, newSigCert);
+			AddOptional(v, 1, caCerts);
+			AddOptional(v, 2, keyPairHist);
+			return new DerSequence(v);
+		}
+
+		private void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj)
+		{
+			if (obj != null)
+			{
+				v.Add(new DerTaggedObject(true, tagNo, obj));
+			}
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/OobCertHash.cs b/crypto/src/asn1/cmp/OobCertHash.cs
new file mode 100644
index 000000000..63ddff7c4
--- /dev/null
+++ b/crypto/src/asn1/cmp/OobCertHash.cs
@@ -0,0 +1,87 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class OobCertHash
+		: Asn1Encodable
+	{
+		private readonly AlgorithmIdentifier hashAlg;
+		private readonly CertId certId;
+		private readonly DerBitString  hashVal;
+
+		private OobCertHash(Asn1Sequence seq)
+		{
+			int index = seq.Count - 1;
+
+			hashVal = DerBitString.GetInstance(seq[index--]);
+
+			for (int i = index; i >= 0; i--)
+			{
+				Asn1TaggedObject tObj = (Asn1TaggedObject)seq[i];
+
+				if (tObj.TagNo == 0)
+				{
+					hashAlg = AlgorithmIdentifier.GetInstance(tObj, true);
+				}
+				else
+				{
+					certId = CertId.GetInstance(tObj, true);
+				}
+			}
+		}
+
+		public static OobCertHash GetInstance(object obj)
+		{
+			if (obj is OobCertHash)
+				return (OobCertHash)obj;
+
+			if (obj is Asn1Sequence)
+				return new OobCertHash((Asn1Sequence)obj);
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+		
+		public virtual AlgorithmIdentifier HashAlg
+		{
+			get { return hashAlg; }
+		}
+		
+		public virtual CertId CertID
+		{
+			get { return certId; }
+		}
+		
+		/**
+		 * <pre>
+		 * OobCertHash ::= SEQUENCE {
+		 *                      hashAlg     [0] AlgorithmIdentifier     OPTIONAL,
+		 *                      certId      [1] CertId                  OPTIONAL,
+		 *                      hashVal         BIT STRING
+		 *                      -- hashVal is calculated over the Der encoding of the
+		 *                      -- self-signed certificate with the identifier certID.
+		 *       }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector();
+			AddOptional(v, 0, hashAlg);
+			AddOptional(v, 1, certId);
+			v.Add(hashVal);
+			return new DerSequence(v);
+		}
+
+		private void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj)
+		{
+			if (obj != null)
+			{
+				v.Add(new DerTaggedObject(true, tagNo, obj));
+			}
+		}
+	}
+}
+
diff --git a/crypto/src/asn1/cmp/PKIBody.cs b/crypto/src/asn1/cmp/PKIBody.cs
new file mode 100644
index 000000000..3205a907e
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIBody.cs
@@ -0,0 +1,186 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.Pkcs;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    public class PkiBody
+        : Asn1Encodable, IAsn1Choice
+    {
+        public const int TYPE_INIT_REQ = 0;
+        public const int TYPE_INIT_REP = 1;
+        public const int TYPE_CERT_REQ = 2;
+        public const int TYPE_CERT_REP = 3;
+        public const int TYPE_P10_CERT_REQ = 4;
+        public const int TYPE_POPO_CHALL = 5;
+        public const int TYPE_POPO_REP = 6;
+        public const int TYPE_KEY_UPDATE_REQ = 7;
+        public const int TYPE_KEY_UPDATE_REP = 8;
+        public const int TYPE_KEY_RECOVERY_REQ = 9;
+        public const int TYPE_KEY_RECOVERY_REP = 10;
+        public const int TYPE_REVOCATION_REQ = 11;
+        public const int TYPE_REVOCATION_REP = 12;
+        public const int TYPE_CROSS_CERT_REQ = 13;
+        public const int TYPE_CROSS_CERT_REP = 14;
+        public const int TYPE_CA_KEY_UPDATE_ANN = 15;
+        public const int TYPE_CERT_ANN = 16;
+        public const int TYPE_REVOCATION_ANN = 17;
+        public const int TYPE_CRL_ANN = 18;
+        public const int TYPE_CONFIRM = 19;
+        public const int TYPE_NESTED = 20;
+        public const int TYPE_GEN_MSG = 21;
+        public const int TYPE_GEN_REP = 22;
+        public const int TYPE_ERROR = 23;
+        public const int TYPE_CERT_CONFIRM = 24;
+        public const int TYPE_POLL_REQ = 25;
+        public const int TYPE_POLL_REP = 26;
+
+        private int tagNo;
+        private Asn1Encodable body;
+
+        public static PkiBody GetInstance(object obj)
+        {
+            if (obj is PkiBody)
+                return (PkiBody)obj;
+
+            if (obj is Asn1TaggedObject)
+                return new PkiBody((Asn1TaggedObject)obj);
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+        }
+
+        private PkiBody(Asn1TaggedObject tagged)
+        {
+            tagNo = tagged.TagNo;
+            body = GetBodyForType(tagNo, tagged.GetObject());
+        }
+
+        /**
+         * Creates a new PkiBody.
+         * @param type one of the TYPE_* constants
+         * @param content message content
+         */
+        public PkiBody(
+            int type,
+            Asn1Encodable content)
+        {
+            tagNo = type;
+            body = GetBodyForType(type, content);
+        }
+
+        private static Asn1Encodable GetBodyForType(
+            int type,
+            Asn1Encodable o)
+        {
+            switch (type)
+            {
+                case TYPE_INIT_REQ:
+                    return CertReqMessages.GetInstance(o);
+	            case TYPE_INIT_REP:
+	                return CertRepMessage.GetInstance(o);
+                case TYPE_CERT_REQ:
+                    return CertReqMessages.GetInstance(o);
+	            case TYPE_CERT_REP:
+	                return CertRepMessage.GetInstance(o);
+	            case TYPE_P10_CERT_REQ:
+	                return CertificationRequest.GetInstance(o);
+	            case TYPE_POPO_CHALL:
+	                return PopoDecKeyChallContent.GetInstance(o);
+	            case TYPE_POPO_REP:
+	                return PopoDecKeyRespContent.GetInstance(o);
+                case TYPE_KEY_UPDATE_REQ:
+                    return CertReqMessages.GetInstance(o);
+	            case TYPE_KEY_UPDATE_REP:
+	                return CertRepMessage.GetInstance(o);
+                case TYPE_KEY_RECOVERY_REQ:
+                    return CertReqMessages.GetInstance(o);
+	            case TYPE_KEY_RECOVERY_REP:
+	                return KeyRecRepContent.GetInstance(o);
+	            case TYPE_REVOCATION_REQ:
+	                return RevReqContent.GetInstance(o);
+	            case TYPE_REVOCATION_REP:
+	                return RevRepContent.GetInstance(o);
+                case TYPE_CROSS_CERT_REQ:
+                    return CertReqMessages.GetInstance(o);
+	            case TYPE_CROSS_CERT_REP:
+	                return CertRepMessage.GetInstance(o);
+	            case TYPE_CA_KEY_UPDATE_ANN:
+	                return CAKeyUpdAnnContent.GetInstance(o);
+	            case TYPE_CERT_ANN:
+	                return CmpCertificate.GetInstance(o);
+	            case TYPE_REVOCATION_ANN:
+	                return RevAnnContent.GetInstance(o);
+	            case TYPE_CRL_ANN:
+	                return CrlAnnContent.GetInstance(o);
+	            case TYPE_CONFIRM:
+	                return PkiConfirmContent.GetInstance(o);
+                case TYPE_NESTED:
+                    return PkiMessages.GetInstance(o);
+	            case TYPE_GEN_MSG:
+	                return GenMsgContent.GetInstance(o);
+	            case TYPE_GEN_REP:
+	                return GenRepContent.GetInstance(o);
+	            case TYPE_ERROR:
+	                return ErrorMsgContent.GetInstance(o);
+	            case TYPE_CERT_CONFIRM:
+	                return CertConfirmContent.GetInstance(o);
+	            case TYPE_POLL_REQ:
+	                return PollReqContent.GetInstance(o);
+	            case TYPE_POLL_REP:
+	                return PollRepContent.GetInstance(o);
+	            default:
+	                throw new ArgumentException("unknown tag number: " + type, "type");
+            }
+        }
+
+        public virtual int Type
+        {
+            get { return tagNo; }
+        }
+
+        public virtual Asn1Encodable Content
+        {
+            get { return body; }
+        }
+
+        /**
+         * <pre>
+         * PkiBody ::= CHOICE {       -- message-specific body elements
+         *        ir       [0]  CertReqMessages,        --Initialization Request
+         *        ip       [1]  CertRepMessage,         --Initialization Response
+         *        cr       [2]  CertReqMessages,        --Certification Request
+         *        cp       [3]  CertRepMessage,         --Certification Response
+         *        p10cr    [4]  CertificationRequest,   --imported from [PKCS10]
+         *        popdecc  [5]  POPODecKeyChallContent, --pop Challenge
+         *        popdecr  [6]  POPODecKeyRespContent,  --pop Response
+         *        kur      [7]  CertReqMessages,        --Key Update Request
+         *        kup      [8]  CertRepMessage,         --Key Update Response
+         *        krr      [9]  CertReqMessages,        --Key Recovery Request
+         *        krp      [10] KeyRecRepContent,       --Key Recovery Response
+         *        rr       [11] RevReqContent,          --Revocation Request
+         *        rp       [12] RevRepContent,          --Revocation Response
+         *        ccr      [13] CertReqMessages,        --Cross-Cert. Request
+         *        ccp      [14] CertRepMessage,         --Cross-Cert. Response
+         *        ckuann   [15] CAKeyUpdAnnContent,     --CA Key Update Ann.
+         *        cann     [16] CertAnnContent,         --Certificate Ann.
+         *        rann     [17] RevAnnContent,          --Revocation Ann.
+         *        crlann   [18] CRLAnnContent,          --CRL Announcement
+         *        pkiconf  [19] PKIConfirmContent,      --Confirmation
+         *        nested   [20] NestedMessageContent,   --Nested Message
+         *        genm     [21] GenMsgContent,          --General Message
+         *        genp     [22] GenRepContent,          --General Response
+         *        error    [23] ErrorMsgContent,        --Error Message
+         *        certConf [24] CertConfirmContent,     --Certificate confirm
+         *        pollReq  [25] PollReqContent,         --Polling request
+         *        pollRep  [26] PollRepContent          --Polling response
+         * }
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerTaggedObject(true, tagNo, body);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/PKIConfirmContent.cs b/crypto/src/asn1/cmp/PKIConfirmContent.cs
new file mode 100644
index 000000000..98645766a
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIConfirmContent.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PkiConfirmContent
+		: Asn1Encodable
+	{
+		public static PkiConfirmContent GetInstance(object obj)
+		{
+			if (obj is PkiConfirmContent)
+				return (PkiConfirmContent)obj;
+
+			if (obj is Asn1Null)
+				return new PkiConfirmContent();
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public PkiConfirmContent()
+		{
+		}
+
+		/**
+		 * <pre>
+		 * PkiConfirmContent ::= NULL
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return DerNull.Instance;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PKIFailureInfo.cs b/crypto/src/asn1/cmp/PKIFailureInfo.cs
new file mode 100644
index 000000000..1df0e0693
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIFailureInfo.cs
@@ -0,0 +1,73 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	/**
+	 * <pre>
+	 * PKIFailureInfo ::= BIT STRING {
+	 * badAlg               (0),
+	 *   -- unrecognized or unsupported Algorithm Identifier
+	 * badMessageCheck      (1), -- integrity check failed (e.g., signature did not verify)
+	 * badRequest           (2),
+	 *   -- transaction not permitted or supported
+	 * badTime              (3), -- messageTime was not sufficiently close to the system time, as defined by local policy
+	 * badCertId            (4), -- no certificate could be found matching the provided criteria
+	 * badDataFormat        (5),
+	 *   -- the data submitted has the wrong format
+	 * wrongAuthority       (6), -- the authority indicated in the request is different from the one creating the response token
+	 * incorrectData        (7), -- the requester's data is incorrect (for notary services)
+	 * missingTimeStamp     (8), -- when the timestamp is missing but should be there (by policy)
+	 * badPOP               (9)  -- the proof-of-possession failed
+	 * timeNotAvailable    (14),
+	 *   -- the TSA's time source is not available
+	 * unacceptedPolicy    (15),
+	 *   -- the requested TSA policy is not supported by the TSA
+	 * unacceptedExtension (16),
+	 *   -- the requested extension is not supported by the TSA
+	 *  addInfoNotAvailable (17)
+	 *    -- the additional information requested could not be understood
+	 *    -- or is not available
+	 *  systemFailure       (25)
+	 *    -- the request cannot be handled due to system failure
+	 * </pre>
+	 */
+	public class PkiFailureInfo
+		: DerBitString
+	{
+		public const int BadAlg               = (1 << 7); // unrecognized or unsupported Algorithm Identifier
+		public const int BadMessageCheck      = (1 << 6); // integrity check failed (e.g., signature did not verify)
+		public const int BadRequest           = (1 << 5);
+		public const int BadTime              = (1 << 4); // -- messageTime was not sufficiently close to the system time, as defined by local policy
+		public const int BadCertId            = (1 << 3); // no certificate could be found matching the provided criteria
+		public const int BadDataFormat        = (1 << 2);
+		public const int WrongAuthority       = (1 << 1); // the authority indicated in the request is different from the one creating the response token
+		public const int IncorrectData        = 1;        // the requester's data is incorrect (for notary services)
+		public const int MissingTimeStamp     = (1 << 15); // when the timestamp is missing but should be there (by policy)
+		public const int BadPop               = (1 << 14); // the proof-of-possession failed
+		public const int TimeNotAvailable     = (1 << 9); // the TSA's time source is not available
+		public const int UnacceptedPolicy     = (1 << 8); // the requested TSA policy is not supported by the TSA
+		public const int UnacceptedExtension  = (1 << 23); //the requested extension is not supported by the TSA
+		public const int AddInfoNotAvailable  = (1 << 22); //the additional information requested could not be understood or is not available
+		public const int SystemFailure        = (1 << 30); //the request cannot be handled due to system failure
+
+		/**
+		 * Basic constructor.
+		 */
+		public PkiFailureInfo(
+			int info)
+			:	base(GetBytes(info), GetPadBits(info))
+		{
+		}
+
+		public PkiFailureInfo(
+			DerBitString info)
+			:	base(info.GetBytes(), info.PadBits)
+		{
+		}
+
+		public override string ToString()
+		{
+			return "PkiFailureInfo: 0x" + this.IntValue.ToString("X");
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PKIFreeText.cs b/crypto/src/asn1/cmp/PKIFreeText.cs
new file mode 100644
index 000000000..571c8d93a
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIFreeText.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PkiFreeText
+		: Asn1Encodable
+	{
+		internal Asn1Sequence strings;
+
+		public static PkiFreeText GetInstance(
+			Asn1TaggedObject	obj,
+			bool				isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+
+		public static PkiFreeText GetInstance(
+			object obj)
+		{
+			if (obj is PkiFreeText)
+			{
+				return (PkiFreeText)obj;
+			}
+			else if (obj is Asn1Sequence)
+			{
+				return new PkiFreeText((Asn1Sequence)obj);
+			}
+
+			throw new ArgumentException("Unknown object in factory: " + obj.GetType().Name, "obj");
+		}
+
+		public PkiFreeText(
+			Asn1Sequence seq)
+		{
+			foreach (object o in seq)
+			{
+				if (!(o is DerUtf8String))
+				{
+					throw new ArgumentException("attempt to insert non UTF8 STRING into PkiFreeText");
+				}
+			}
+
+			this.strings = seq;
+		}
+
+		public PkiFreeText(
+			DerUtf8String p)
+		{
+			strings = new DerSequence(p);
+		}
+
+		/**
+		 * Return the number of string elements present.
+		 *
+		 * @return number of elements present.
+		 */
+		[Obsolete("Use 'Count' property instead")]
+		public int Size
+		{
+			get { return strings.Count; }
+		}
+
+		public int Count
+		{
+			get { return strings.Count; }
+		}
+
+		/**
+		 * Return the UTF8STRING at index.
+		 *
+		 * @param index index of the string of interest
+		 * @return the string at index.
+		 */
+		public DerUtf8String this[int index]
+		{
+			get { return (DerUtf8String) strings[index]; }
+		}
+
+		[Obsolete("Use 'object[index]' syntax instead")]
+		public DerUtf8String GetStringAt(
+			int index)
+		{
+			return this[index];
+		}
+
+		/**
+		 * <pre>
+		 * PkiFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String
+		 * </pre>
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return strings;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PKIHeader.cs b/crypto/src/asn1/cmp/PKIHeader.cs
new file mode 100644
index 000000000..e758e9f16
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIHeader.cs
@@ -0,0 +1,237 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    public class PkiHeader
+        : Asn1Encodable
+    {
+        /**
+         * Value for a "null" recipient or sender.
+         */
+        public static readonly GeneralName NULL_NAME = new GeneralName(X509Name.GetInstance(new DerSequence()));
+
+        public static readonly int CMP_1999 = 1;
+        public static readonly int CMP_2000 = 2;
+
+        private readonly DerInteger pvno;
+        private readonly GeneralName sender;
+        private readonly GeneralName recipient;
+        private readonly DerGeneralizedTime messageTime;
+        private readonly AlgorithmIdentifier protectionAlg;
+        private readonly Asn1OctetString senderKID;       // KeyIdentifier
+        private readonly Asn1OctetString recipKID;        // KeyIdentifier
+        private readonly Asn1OctetString transactionID;
+        private readonly Asn1OctetString senderNonce;
+        private readonly Asn1OctetString recipNonce;
+        private readonly PkiFreeText freeText;
+        private readonly Asn1Sequence generalInfo;
+
+        private PkiHeader(Asn1Sequence seq)
+        {
+            pvno = DerInteger.GetInstance(seq[0]);
+            sender = GeneralName.GetInstance(seq[1]);
+            recipient = GeneralName.GetInstance(seq[2]);
+
+            for (int pos = 3; pos < seq.Count; ++pos)
+            {
+                Asn1TaggedObject tObj = (Asn1TaggedObject)seq[pos];
+
+                switch (tObj.TagNo)
+                {
+                    case 0:
+                        messageTime = DerGeneralizedTime.GetInstance(tObj, true);
+                        break;
+                    case 1:
+                        protectionAlg = AlgorithmIdentifier.GetInstance(tObj, true);
+                        break;
+                    case 2:
+                        senderKID = Asn1OctetString.GetInstance(tObj, true);
+                        break;
+                    case 3:
+                        recipKID = Asn1OctetString.GetInstance(tObj, true);
+                        break;
+                    case 4:
+                        transactionID = Asn1OctetString.GetInstance(tObj, true);
+                        break;
+                    case 5:
+                        senderNonce = Asn1OctetString.GetInstance(tObj, true);
+                        break;
+                    case 6:
+                        recipNonce = Asn1OctetString.GetInstance(tObj, true);
+                        break;
+                    case 7:
+                        freeText = PkiFreeText.GetInstance(tObj, true);
+                        break;
+                    case 8:
+                        generalInfo = Asn1Sequence.GetInstance(tObj, true);
+                        break;
+                    default:
+                        throw new ArgumentException("unknown tag number: " + tObj.TagNo, "seq");
+                }
+            }
+        }
+
+        public static PkiHeader GetInstance(object obj)
+        {
+            if (obj is PkiHeader)
+                return (PkiHeader)obj;
+
+            if (obj is Asn1Sequence)
+                return new PkiHeader((Asn1Sequence)obj);
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+        }
+
+        public PkiHeader(
+            int pvno,
+            GeneralName sender,
+            GeneralName recipient)
+            : this(new DerInteger(pvno), sender, recipient)
+        {
+        }
+
+        private PkiHeader(
+            DerInteger pvno,
+            GeneralName sender,
+            GeneralName recipient)
+        {
+            this.pvno = pvno;
+            this.sender = sender;
+            this.recipient = recipient;
+        }
+
+        public virtual DerInteger Pvno
+        {
+            get { return pvno; }
+        }
+
+        public virtual GeneralName Sender
+        {
+            get { return sender; }
+        }
+
+        public virtual GeneralName Recipient
+        {
+            get { return recipient; }
+        }
+
+        public virtual DerGeneralizedTime MessageTime
+        {
+            get { return messageTime; }
+        }
+
+        public virtual AlgorithmIdentifier ProtectionAlg
+        {
+            get { return protectionAlg; }
+        }
+
+        public virtual Asn1OctetString SenderKID
+        {   
+            get { return senderKID; }
+        }
+
+        public virtual Asn1OctetString RecipKID
+        {   
+            get { return recipKID; }
+        }
+
+        public virtual Asn1OctetString TransactionID
+        {   
+            get { return transactionID; }
+        }
+
+        public virtual Asn1OctetString SenderNonce
+        {   
+            get { return senderNonce; }
+        }
+
+        public virtual Asn1OctetString RecipNonce
+        {   
+            get { return recipNonce; }
+        }
+
+        public virtual PkiFreeText FreeText
+        {
+            get { return freeText; }
+        }
+
+        public virtual InfoTypeAndValue[] GetGeneralInfo()
+        {
+            if (generalInfo == null)
+            {
+                return null;
+            }
+            InfoTypeAndValue[] results = new InfoTypeAndValue[generalInfo.Count];
+            for (int i = 0; i < results.Length; i++)
+            {
+                results[i] = InfoTypeAndValue.GetInstance(generalInfo[i]);
+            }
+            return results;
+        }
+
+        /**
+         * <pre>
+         *  PkiHeader ::= SEQUENCE {
+         *            pvno                INTEGER     { cmp1999(1), cmp2000(2) },
+         *            sender              GeneralName,
+         *            -- identifies the sender
+         *            recipient           GeneralName,
+         *            -- identifies the intended recipient
+         *            messageTime     [0] GeneralizedTime         OPTIONAL,
+         *            -- time of production of this message (used when sender
+         *            -- believes that the transport will be "suitable"; i.e.,
+         *            -- that the time will still be meaningful upon receipt)
+         *            protectionAlg   [1] AlgorithmIdentifier     OPTIONAL,
+         *            -- algorithm used for calculation of protection bits
+         *            senderKID       [2] KeyIdentifier           OPTIONAL,
+         *            recipKID        [3] KeyIdentifier           OPTIONAL,
+         *            -- to identify specific keys used for protection
+         *            transactionID   [4] OCTET STRING            OPTIONAL,
+         *            -- identifies the transaction; i.e., this will be the same in
+         *            -- corresponding request, response, certConf, and PKIConf
+         *            -- messages
+         *            senderNonce     [5] OCTET STRING            OPTIONAL,
+         *            recipNonce      [6] OCTET STRING            OPTIONAL,
+         *            -- nonces used to provide replay protection, senderNonce
+         *            -- is inserted by the creator of this message; recipNonce
+         *            -- is a nonce previously inserted in a related message by
+         *            -- the intended recipient of this message
+         *            freeText        [7] PKIFreeText             OPTIONAL,
+         *            -- this may be used to indicate context-specific instructions
+         *            -- (this field is intended for human consumption)
+         *            generalInfo     [8] SEQUENCE SIZE (1..MAX) OF
+         *                                 InfoTypeAndValue     OPTIONAL
+         *            -- this may be used to convey context-specific information
+         *            -- (this field not primarily intended for human consumption)
+         * }
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(pvno, sender, recipient);
+
+            AddOptional(v, 0, messageTime);
+            AddOptional(v, 1, protectionAlg);
+            AddOptional(v, 2, senderKID);
+            AddOptional(v, 3, recipKID);
+            AddOptional(v, 4, transactionID);
+            AddOptional(v, 5, senderNonce);
+            AddOptional(v, 6, recipNonce);
+            AddOptional(v, 7, freeText);
+            AddOptional(v, 8, generalInfo);
+
+            return new DerSequence(v);
+        }
+
+        private static void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj)
+        {
+            if (obj != null)
+            {
+                v.Add(new DerTaggedObject(true, tagNo, obj));
+            }
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
new file mode 100644
index 000000000..00073c062
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
@@ -0,0 +1,223 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PkiHeaderBuilder
+	{
+		private DerInteger pvno;
+		private GeneralName sender;
+		private GeneralName recipient;
+		private DerGeneralizedTime messageTime;
+		private AlgorithmIdentifier protectionAlg;
+		private Asn1OctetString senderKID;       // KeyIdentifier
+		private Asn1OctetString recipKID;        // KeyIdentifier
+		private Asn1OctetString transactionID;
+		private Asn1OctetString senderNonce;
+		private Asn1OctetString recipNonce;
+		private PkiFreeText     freeText;
+		private Asn1Sequence    generalInfo;
+
+		public PkiHeaderBuilder(
+			int			pvno,
+			GeneralName	sender,
+			GeneralName	recipient)
+			: this(new DerInteger(pvno), sender, recipient)
+		{
+		}
+
+		private PkiHeaderBuilder(
+			DerInteger	pvno,
+			GeneralName	sender,
+			GeneralName	recipient)
+		{
+			this.pvno = pvno;
+			this.sender = sender;
+			this.recipient = recipient;
+		}
+
+		public virtual PkiHeaderBuilder SetMessageTime(DerGeneralizedTime time)
+		{
+			messageTime = time;
+			return this;
+		}
+
+		public virtual PkiHeaderBuilder SetProtectionAlg(AlgorithmIdentifier aid)
+		{
+			protectionAlg = aid;
+			return this;
+		}
+
+		public virtual PkiHeaderBuilder SetSenderKID(byte[] kid)
+		{
+            return SetSenderKID(kid == null ? null : new DerOctetString(kid));
+		}
+
+		public virtual PkiHeaderBuilder SetSenderKID(Asn1OctetString kid)
+		{
+			senderKID = kid;
+			return this;
+		}
+
+		public virtual PkiHeaderBuilder SetRecipKID(byte[] kid)
+		{
+            return SetRecipKID(kid == null ? null : new DerOctetString(kid));
+		}
+		
+		public virtual PkiHeaderBuilder SetRecipKID(DerOctetString kid)
+		{
+			recipKID = kid;
+			return this;
+		}
+
+		public virtual PkiHeaderBuilder SetTransactionID(byte[] tid)
+		{
+			return SetTransactionID(tid == null ? null : new DerOctetString(tid));
+		}
+
+		public virtual PkiHeaderBuilder SetTransactionID(Asn1OctetString tid)
+		{
+			transactionID = tid;
+			return this;
+		}
+		
+		public virtual PkiHeaderBuilder SetSenderNonce(byte[] nonce)
+		{
+            return SetSenderNonce(nonce == null ? null : new DerOctetString(nonce));
+		}
+
+		public virtual PkiHeaderBuilder SetSenderNonce(Asn1OctetString nonce)
+		{
+			senderNonce = nonce;
+			return this;
+		}
+
+		public virtual PkiHeaderBuilder SetRecipNonce(byte[] nonce)
+		{
+            return SetRecipNonce(nonce == null ? null : new DerOctetString(nonce));
+		}
+
+		public virtual PkiHeaderBuilder SetRecipNonce(Asn1OctetString nonce)
+		{
+			recipNonce = nonce;
+			return this;
+		}
+
+		public virtual PkiHeaderBuilder SetFreeText(PkiFreeText text)
+		{
+			freeText = text;
+			return this;
+		}
+		
+		public virtual PkiHeaderBuilder SetGeneralInfo(InfoTypeAndValue genInfo)
+		{
+			return SetGeneralInfo(MakeGeneralInfoSeq(genInfo));
+		}
+		
+		public virtual PkiHeaderBuilder SetGeneralInfo(InfoTypeAndValue[] genInfos)
+		{
+			return SetGeneralInfo(MakeGeneralInfoSeq(genInfos));
+		}
+		
+		public virtual PkiHeaderBuilder SetGeneralInfo(Asn1Sequence seqOfInfoTypeAndValue)
+		{
+			generalInfo = seqOfInfoTypeAndValue;
+			return this;
+		}
+
+		private static Asn1Sequence MakeGeneralInfoSeq(
+			InfoTypeAndValue generalInfo)
+		{
+			return new DerSequence(generalInfo);
+		}
+		
+		private static Asn1Sequence MakeGeneralInfoSeq(
+			InfoTypeAndValue[] generalInfos)
+		{
+			Asn1Sequence genInfoSeq = null;
+			if (generalInfos != null)
+			{
+				Asn1EncodableVector v = new Asn1EncodableVector();
+				for (int i = 0; i < generalInfos.Length; ++i)
+				{
+					v.Add(generalInfos[i]);
+				}
+				genInfoSeq = new DerSequence(v);
+			}
+			return genInfoSeq;
+		}
+
+		/**
+		 * <pre>
+		 *  PKIHeader ::= SEQUENCE {
+		 *            pvno                INTEGER     { cmp1999(1), cmp2000(2) },
+		 *            sender              GeneralName,
+		 *            -- identifies the sender
+		 *            recipient           GeneralName,
+		 *            -- identifies the intended recipient
+		 *            messageTime     [0] GeneralizedTime         OPTIONAL,
+		 *            -- time of production of this message (used when sender
+		 *            -- believes that the transport will be "suitable"; i.e.,
+		 *            -- that the time will still be meaningful upon receipt)
+		 *            protectionAlg   [1] AlgorithmIdentifier     OPTIONAL,
+		 *            -- algorithm used for calculation of protection bits
+		 *            senderKID       [2] KeyIdentifier           OPTIONAL,
+		 *            recipKID        [3] KeyIdentifier           OPTIONAL,
+		 *            -- to identify specific keys used for protection
+		 *            transactionID   [4] OCTET STRING            OPTIONAL,
+		 *            -- identifies the transaction; i.e., this will be the same in
+		 *            -- corresponding request, response, certConf, and PKIConf
+		 *            -- messages
+		 *            senderNonce     [5] OCTET STRING            OPTIONAL,
+		 *            recipNonce      [6] OCTET STRING            OPTIONAL,
+		 *            -- nonces used to provide replay protection, senderNonce
+		 *            -- is inserted by the creator of this message; recipNonce
+		 *            -- is a nonce previously inserted in a related message by
+		 *            -- the intended recipient of this message
+		 *            freeText        [7] PKIFreeText             OPTIONAL,
+		 *            -- this may be used to indicate context-specific instructions
+		 *            -- (this field is intended for human consumption)
+		 *            generalInfo     [8] SEQUENCE SIZE (1..MAX) OF
+		 *                                 InfoTypeAndValue     OPTIONAL
+		 *            -- this may be used to convey context-specific information
+		 *            -- (this field not primarily intended for human consumption)
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public virtual PkiHeader Build()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(pvno, sender, recipient);
+			AddOptional(v, 0, messageTime);
+			AddOptional(v, 1, protectionAlg);
+			AddOptional(v, 2, senderKID);
+			AddOptional(v, 3, recipKID);
+			AddOptional(v, 4, transactionID);
+			AddOptional(v, 5, senderNonce);
+			AddOptional(v, 6, recipNonce);
+			AddOptional(v, 7, freeText);
+			AddOptional(v, 8, generalInfo);
+
+			messageTime = null;
+			protectionAlg = null;
+			senderKID = null;
+			recipKID = null;
+			transactionID = null;
+			senderNonce = null;
+			recipNonce = null;
+			freeText = null;
+			generalInfo = null;
+
+			return PkiHeader.GetInstance(new DerSequence(v));
+		}
+
+		private void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj)
+		{
+			if (obj != null)
+			{
+				v.Add(new DerTaggedObject(true, tagNo, obj));
+			}
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PKIMessage.cs b/crypto/src/asn1/cmp/PKIMessage.cs
new file mode 100644
index 000000000..086a2d938
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIMessage.cs
@@ -0,0 +1,140 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    public class PkiMessage
+        : Asn1Encodable
+    {
+        private readonly PkiHeader header;
+        private readonly PkiBody body;
+        private readonly DerBitString protection;
+        private readonly Asn1Sequence extraCerts;
+
+        private PkiMessage(Asn1Sequence seq)
+        {
+            header = PkiHeader.GetInstance(seq[0]);
+            body = PkiBody.GetInstance(seq[1]);
+
+            for (int pos = 2; pos < seq.Count; ++pos)
+            {
+                Asn1TaggedObject tObj = (Asn1TaggedObject)seq[pos].ToAsn1Object();
+
+                if (tObj.TagNo == 0)
+                {
+                    protection = DerBitString.GetInstance(tObj, true);
+                }
+                else
+                {
+                    extraCerts = Asn1Sequence.GetInstance(tObj, true);
+                }
+            }
+        }
+
+        public static PkiMessage GetInstance(object obj)
+        {
+            if (obj is PkiMessage)
+                return (PkiMessage)obj;
+
+            if (obj != null)
+                return new PkiMessage(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        /**
+         * Creates a new PkiMessage.
+         *
+         * @param header message header
+         * @param body message body
+         * @param protection message protection (may be null)
+         * @param extraCerts extra certificates (may be null)
+         */
+        public PkiMessage(
+            PkiHeader header,
+            PkiBody body,
+            DerBitString protection,
+            CmpCertificate[] extraCerts)
+        {
+            this.header = header;
+            this.body = body;
+            this.protection = protection;
+            if (extraCerts != null)
+            {
+                this.extraCerts = new DerSequence(extraCerts);
+            }
+        }
+
+        public PkiMessage(
+            PkiHeader header,
+            PkiBody body,
+            DerBitString protection)
+            : this(header, body, protection, null)
+        {
+        }
+
+        public PkiMessage(
+            PkiHeader header,
+            PkiBody body)
+            : this(header, body, null, null)
+        {
+        }
+
+        public virtual PkiHeader Header
+        {
+            get { return header; }
+        }
+
+        public virtual PkiBody Body
+        {
+            get { return body; }
+        }
+
+        public virtual DerBitString Protection
+        {
+            get { return protection; }
+        }
+
+        public virtual CmpCertificate[] GetExtraCerts()
+        {
+            if (extraCerts == null)
+                return null;
+
+            CmpCertificate[] results = new CmpCertificate[extraCerts.Count];
+            for (int i = 0; i < results.Length; ++i)
+            {
+                results[i] = CmpCertificate.GetInstance(extraCerts[i]);
+            }
+            return results;
+        }
+
+        /**
+         * <pre>
+         * PkiMessage ::= SEQUENCE {
+         *                  header           PKIHeader,
+         *                  body             PKIBody,
+         *                  protection   [0] PKIProtection OPTIONAL,
+         *                  extraCerts   [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate
+         *                                                                     OPTIONAL
+         * }
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(header, body);
+
+            AddOptional(v, 0, protection);
+            AddOptional(v, 1, extraCerts);
+
+            return new DerSequence(v);
+        }
+
+        private static void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj)
+        {
+            if (obj != null)
+            {
+                v.Add(new DerTaggedObject(true, tagNo, obj));
+            }
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/PKIMessages.cs b/crypto/src/asn1/cmp/PKIMessages.cs
new file mode 100644
index 000000000..ddabdf4ae
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIMessages.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    public class PkiMessages
+        : Asn1Encodable
+    {
+        private Asn1Sequence content;
+
+        private PkiMessages(Asn1Sequence seq)
+        {
+            content = seq;
+        }
+
+        public static PkiMessages GetInstance(object obj)
+        {
+            if (obj is PkiMessages)
+                return (PkiMessages)obj;
+
+            if (obj is Asn1Sequence)
+                return new PkiMessages((Asn1Sequence)obj);
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+        }
+
+		public PkiMessages(params PkiMessage[] msgs)
+        {
+            content = new DerSequence(msgs);
+        }
+
+        public virtual PkiMessage[] ToPkiMessageArray()
+        {
+            PkiMessage[] result = new PkiMessage[content.Count];
+            for (int i = 0; i != result.Length; ++i)
+            {
+                result[i] = PkiMessage.GetInstance(content[i]);
+            }
+            return result;
+        }
+
+        /**
+         * <pre>
+         * PkiMessages ::= SEQUENCE SIZE (1..MAX) OF PkiMessage
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return content;
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/PKIStatus.cs b/crypto/src/asn1/cmp/PKIStatus.cs
new file mode 100644
index 000000000..b03dd3d62
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIStatus.cs
@@ -0,0 +1,62 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public enum PkiStatus
+	{
+		Granted					= 0,
+		GrantedWithMods			= 1,
+		Rejection				= 2,
+		Waiting					= 3,
+		RevocationWarning		= 4,
+		RevocationNotification	= 5,
+    	KeyUpdateWarning		= 6,
+	}
+
+	public class PkiStatusEncodable
+		: Asn1Encodable
+	{
+		public static readonly PkiStatusEncodable granted = new PkiStatusEncodable(PkiStatus.Granted);
+		public static readonly PkiStatusEncodable grantedWithMods = new PkiStatusEncodable(PkiStatus.GrantedWithMods);
+		public static readonly PkiStatusEncodable rejection = new PkiStatusEncodable(PkiStatus.Rejection);
+		public static readonly PkiStatusEncodable waiting = new PkiStatusEncodable(PkiStatus.Waiting);
+		public static readonly PkiStatusEncodable revocationWarning = new PkiStatusEncodable(PkiStatus.RevocationWarning);
+		public static readonly PkiStatusEncodable revocationNotification = new PkiStatusEncodable(PkiStatus.RevocationNotification);
+		public static readonly PkiStatusEncodable keyUpdateWaiting = new PkiStatusEncodable(PkiStatus.KeyUpdateWarning);
+
+		private readonly DerInteger status;
+
+		private PkiStatusEncodable(PkiStatus status)
+			: this(new DerInteger((int)status))
+		{
+		}
+
+		private PkiStatusEncodable(DerInteger status)
+		{
+			this.status = status;
+		}
+
+		public static PkiStatusEncodable GetInstance(object obj)
+		{
+			if (obj is PkiStatusEncodable)
+				return (PkiStatusEncodable)obj;
+
+			if (obj is DerInteger)
+				return new PkiStatusEncodable((DerInteger)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual BigInteger Value
+		{
+			get { return status.Value; }
+		}
+
+		public override Asn1Object ToAsn1Object()
+		{
+			return status;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PKIStatusInfo.cs b/crypto/src/asn1/cmp/PKIStatusInfo.cs
new file mode 100644
index 000000000..2463e0081
--- /dev/null
+++ b/crypto/src/asn1/cmp/PKIStatusInfo.cs
@@ -0,0 +1,165 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PkiStatusInfo
+		: Asn1Encodable
+	{
+		DerInteger      status;
+		PkiFreeText     statusString;
+		DerBitString    failInfo;
+
+		public static PkiStatusInfo GetInstance(
+			Asn1TaggedObject obj,
+			bool isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+
+		public static PkiStatusInfo GetInstance(
+			object obj)
+		{
+			if (obj is PkiStatusInfo)
+			{
+				return (PkiStatusInfo)obj;
+			}
+			else if (obj is Asn1Sequence)
+			{
+				return new PkiStatusInfo((Asn1Sequence)obj);
+			}
+
+			throw new ArgumentException("Unknown object in factory: " + obj.GetType().Name, "obj");
+		}
+
+		public PkiStatusInfo(
+			Asn1Sequence seq)
+		{
+			this.status = DerInteger.GetInstance(seq[0]);
+
+			this.statusString = null;
+			this.failInfo = null;
+
+			if (seq.Count > 2)
+			{
+				this.statusString = PkiFreeText.GetInstance(seq[1]);
+				this.failInfo = DerBitString.GetInstance(seq[2]);
+			}
+			else if (seq.Count > 1)
+			{
+				object obj = seq[1];
+				if (obj is DerBitString)
+				{
+					this.failInfo = DerBitString.GetInstance(obj);
+				}
+				else
+				{
+					this.statusString = PkiFreeText.GetInstance(obj);
+				}
+			}
+		}
+
+		/**
+		 * @param status
+		 */
+		public PkiStatusInfo(int status)
+		{
+			this.status = new DerInteger(status);
+		}
+
+		/**
+		 * @param status
+		 * @param statusString
+		 */
+		public PkiStatusInfo(
+			int			status,
+			PkiFreeText	statusString)
+		{
+			this.status = new DerInteger(status);
+			this.statusString = statusString;
+		}
+
+		public PkiStatusInfo(
+			int				status,
+			PkiFreeText		statusString,
+			PkiFailureInfo	failInfo)
+		{
+			this.status = new DerInteger(status);
+			this.statusString = statusString;
+			this.failInfo = failInfo;
+		}
+
+		public BigInteger Status
+		{
+			get
+			{
+				return status.Value;
+			}
+		}
+
+		public PkiFreeText StatusString
+		{
+			get
+			{
+				return statusString;
+			}
+		}
+
+		public DerBitString FailInfo
+		{
+			get
+			{
+				return failInfo;
+			}
+		}
+
+		/**
+		 * <pre>
+		 * PkiStatusInfo ::= SEQUENCE {
+		 *     status        PKIStatus,                (INTEGER)
+		 *     statusString  PkiFreeText     OPTIONAL,
+		 *     failInfo      PkiFailureInfo  OPTIONAL  (BIT STRING)
+		 * }
+		 *
+		 * PKIStatus:
+		 *   granted                (0), -- you got exactly what you asked for
+		 *   grantedWithMods        (1), -- you got something like what you asked for
+		 *   rejection              (2), -- you don't get it, more information elsewhere in the message
+		 *   waiting                (3), -- the request body part has not yet been processed, expect to hear more later
+		 *   revocationWarning      (4), -- this message contains a warning that a revocation is imminent
+		 *   revocationNotification (5), -- notification that a revocation has occurred
+		 *   keyUpdateWarning       (6)  -- update already done for the oldCertId specified in CertReqMsg
+		 *
+		 * PkiFailureInfo:
+		 *   badAlg           (0), -- unrecognized or unsupported Algorithm Identifier
+		 *   badMessageCheck  (1), -- integrity check failed (e.g., signature did not verify)
+		 *   badRequest       (2), -- transaction not permitted or supported
+		 *   badTime          (3), -- messageTime was not sufficiently close to the system time, as defined by local policy
+		 *   badCertId        (4), -- no certificate could be found matching the provided criteria
+		 *   badDataFormat    (5), -- the data submitted has the wrong format
+		 *   wrongAuthority   (6), -- the authority indicated in the request is different from the one creating the response token
+		 *   incorrectData    (7), -- the requester's data is incorrect (for notary services)
+		 *   missingTimeStamp (8), -- when the timestamp is missing but should be there (by policy)
+		 *   badPOP           (9)  -- the proof-of-possession failed
+		 *
+		 * </pre>
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(status);
+
+			if (statusString != null)
+			{
+				v.Add(statusString);
+			}
+
+			if (failInfo!= null)
+			{
+				v.Add(failInfo);
+			}
+
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PbmParameter.cs b/crypto/src/asn1/cmp/PbmParameter.cs
new file mode 100644
index 000000000..59b1bd7bb
--- /dev/null
+++ b/crypto/src/asn1/cmp/PbmParameter.cs
@@ -0,0 +1,100 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    public class PbmParameter
+        : Asn1Encodable
+    {
+        private Asn1OctetString salt;
+        private AlgorithmIdentifier owf;
+        private DerInteger iterationCount;
+        private AlgorithmIdentifier mac;
+
+        private PbmParameter(Asn1Sequence seq)
+        {
+            salt = Asn1OctetString.GetInstance(seq[0]);
+            owf = AlgorithmIdentifier.GetInstance(seq[1]);
+            iterationCount = DerInteger.GetInstance(seq[2]);
+            mac = AlgorithmIdentifier.GetInstance(seq[3]);
+        }
+
+        public static PbmParameter GetInstance(object obj)
+        {
+            if (obj is PbmParameter)
+                return (PbmParameter)obj;
+
+            if (obj is Asn1Sequence)
+                return new PbmParameter((Asn1Sequence)obj);
+
+            throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+        }
+
+        public PbmParameter(
+            byte[] salt,
+            AlgorithmIdentifier owf,
+            int iterationCount,
+            AlgorithmIdentifier mac)
+            : this(new DerOctetString(salt), owf, new DerInteger(iterationCount), mac)
+        {
+        }
+
+        public PbmParameter(
+            Asn1OctetString salt,
+            AlgorithmIdentifier owf,
+            DerInteger iterationCount,
+            AlgorithmIdentifier mac)
+        {
+            this.salt = salt;
+            this.owf = owf;
+            this.iterationCount = iterationCount;
+            this.mac = mac;
+        }
+
+        public virtual Asn1OctetString Salt
+        {
+            get { return salt; }
+        }
+
+        public virtual AlgorithmIdentifier Owf
+        {
+            get { return owf; }
+        }
+
+        public virtual DerInteger IterationCount
+        {
+            get { return iterationCount; }
+        }
+
+        public virtual AlgorithmIdentifier Mac
+        {
+            get { return mac; }
+        }
+
+        /**
+         * <pre>
+         *  PbmParameter ::= SEQUENCE {
+         *                        salt                OCTET STRING,
+         *                        -- note:  implementations MAY wish to limit acceptable sizes
+         *                        -- of this string to values appropriate for their environment
+         *                        -- in order to reduce the risk of denial-of-service attacks
+         *                        owf                 AlgorithmIdentifier,
+         *                        -- AlgId for a One-Way Function (SHA-1 recommended)
+         *                        iterationCount      INTEGER,
+         *                        -- number of times the OWF is applied
+         *                        -- note:  implementations MAY wish to limit acceptable sizes
+         *                        -- of this integer to values appropriate for their environment
+         *                        -- in order to reduce the risk of denial-of-service attacks
+         *                        mac                 AlgorithmIdentifier
+         *                        -- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11],
+         *    }   -- or HMAC [RFC2104, RFC2202])
+         * </pre>
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerSequence(salt, owf, iterationCount, mac);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/PollRepContent.cs b/crypto/src/asn1/cmp/PollRepContent.cs
new file mode 100644
index 000000000..4045ac7ed
--- /dev/null
+++ b/crypto/src/asn1/cmp/PollRepContent.cs
@@ -0,0 +1,66 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PollRepContent
+		: Asn1Encodable
+	{
+		private readonly DerInteger certReqId;
+		private readonly DerInteger checkAfter;
+		private readonly PkiFreeText reason;
+
+		private PollRepContent(Asn1Sequence seq)
+		{
+			certReqId = DerInteger.GetInstance(seq[0]);
+			checkAfter = DerInteger.GetInstance(seq[1]);
+
+			if (seq.Count > 2)
+			{
+				reason = PkiFreeText.GetInstance(seq[2]);
+			}
+		}
+
+		public static PollRepContent GetInstance(object obj)
+		{
+			if (obj is PollRepContent)
+				return (PollRepContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new PollRepContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual DerInteger CertReqID
+		{
+			get { return certReqId; }
+		}
+
+		public virtual DerInteger CheckAfter
+		{
+			get { return checkAfter; }
+		}
+
+		public virtual PkiFreeText Reason
+		{
+			get { return reason; }
+		}
+
+		/**
+		 * <pre>
+		 * PollRepContent ::= SEQUENCE OF SEQUENCE {
+		 *         certReqId              INTEGER,
+		 *         checkAfter             INTEGER,  -- time in seconds
+		 *         reason                 PKIFreeText OPTIONAL
+		 *     }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(certReqId, checkAfter);
+			v.AddOptional(reason);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PollReqContent.cs b/crypto/src/asn1/cmp/PollReqContent.cs
new file mode 100644
index 000000000..ca2164151
--- /dev/null
+++ b/crypto/src/asn1/cmp/PollReqContent.cs
@@ -0,0 +1,59 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PollReqContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+
+		private PollReqContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static PollReqContent GetInstance(object obj)
+		{
+			if (obj is PollReqContent)
+				return (PollReqContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new PollReqContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual DerInteger[][] GetCertReqIDs()
+		{
+			DerInteger[][] result = new DerInteger[content.Count][];
+			for (int i = 0; i != result.Length; ++i)
+			{
+				result[i] = SequenceToDerIntegerArray((Asn1Sequence)content[i]);
+			}
+			return result;
+		}
+
+		private static DerInteger[] SequenceToDerIntegerArray(Asn1Sequence seq)
+		{
+			DerInteger[] result = new DerInteger[seq.Count];
+			for (int i = 0; i != result.Length; ++i)
+			{
+				result[i] = DerInteger.GetInstance(seq[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * PollReqContent ::= SEQUENCE OF SEQUENCE {
+		 *                        certReqId              INTEGER
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs b/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs
new file mode 100644
index 000000000..20b173b85
--- /dev/null
+++ b/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PopoDecKeyChallContent
+	    : Asn1Encodable
+	{
+	    private readonly Asn1Sequence content;
+
+	    private PopoDecKeyChallContent(Asn1Sequence seq)
+	    {
+	        content = seq;
+	    }
+
+	    public static PopoDecKeyChallContent GetInstance(object obj)
+	    {
+	        if (obj is PopoDecKeyChallContent)
+	            return (PopoDecKeyChallContent)obj;
+
+			if (obj is Asn1Sequence)
+	            return new PopoDecKeyChallContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+	    }
+
+	    public virtual Challenge[] ToChallengeArray()
+	    {
+	        Challenge[] result = new Challenge[content.Count];
+	        for (int i = 0; i != result.Length; ++i)
+	        {
+	            result[i] = Challenge.GetInstance(content[i]);
+	        }
+	        return result;
+	    }
+
+	    /**
+	     * <pre>
+	     * PopoDecKeyChallContent ::= SEQUENCE OF Challenge
+	     * </pre>
+	     * @return a basic ASN.1 object representation.
+	     */
+	    public override Asn1Object ToAsn1Object()
+	    {
+	        return content;
+	    }
+	}
+}
diff --git a/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs b/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs
new file mode 100644
index 000000000..8c322e4ec
--- /dev/null
+++ b/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class PopoDecKeyRespContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+
+		private PopoDecKeyRespContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static PopoDecKeyRespContent GetInstance(object obj)
+		{
+			if (obj is PopoDecKeyRespContent)
+				return (PopoDecKeyRespContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new PopoDecKeyRespContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual DerInteger[] ToDerIntegerArray()
+		{
+			DerInteger[] result = new DerInteger[content.Count];
+			for (int i = 0; i != result.Length; ++i)
+			{
+				result[i] = DerInteger.GetInstance(content[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * PopoDecKeyRespContent ::= SEQUENCE OF INTEGER
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/ProtectedPart.cs b/crypto/src/asn1/cmp/ProtectedPart.cs
new file mode 100644
index 000000000..db6798fee
--- /dev/null
+++ b/crypto/src/asn1/cmp/ProtectedPart.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class ProtectedPart
+		: Asn1Encodable
+	{
+		private readonly PkiHeader header;
+		private readonly PkiBody body;
+		
+		private ProtectedPart(Asn1Sequence seq)
+		{
+			header = PkiHeader.GetInstance(seq[0]);
+			body = PkiBody.GetInstance(seq[1]);
+		}
+
+		public static ProtectedPart GetInstance(object obj)
+		{
+			if (obj is ProtectedPart)
+				return (ProtectedPart)obj;
+
+			if (obj is Asn1Sequence)
+				return new ProtectedPart((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public ProtectedPart(PkiHeader header, PkiBody body)
+		{
+			this.header = header;
+			this.body = body;
+		}
+
+		public virtual PkiHeader Header
+		{
+			get { return header; }
+		}
+
+		public virtual PkiBody Body
+		{
+			get { return body; }
+		}
+
+		/**
+		 * <pre>
+		 * ProtectedPart ::= SEQUENCE {
+		 *                    header    PKIHeader,
+		 *                    body      PKIBody
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return new DerSequence(header, body);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/RevAnnContent.cs b/crypto/src/asn1/cmp/RevAnnContent.cs
new file mode 100644
index 000000000..2c3bd5f77
--- /dev/null
+++ b/crypto/src/asn1/cmp/RevAnnContent.cs
@@ -0,0 +1,86 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class RevAnnContent
+		: Asn1Encodable
+	{
+		private readonly PkiStatusEncodable status;
+		private readonly CertId certId;
+		private readonly DerGeneralizedTime willBeRevokedAt;
+		private readonly DerGeneralizedTime badSinceDate;
+		private readonly X509Extensions crlDetails;
+
+		private RevAnnContent(Asn1Sequence seq)
+		{
+			status = PkiStatusEncodable.GetInstance(seq[0]);
+			certId = CertId.GetInstance(seq[1]);
+			willBeRevokedAt = DerGeneralizedTime.GetInstance(seq[2]);
+			badSinceDate = DerGeneralizedTime.GetInstance(seq[3]);
+
+			if (seq.Count > 4)
+			{
+				crlDetails = X509Extensions.GetInstance(seq[4]);
+			}
+		}
+
+		public static RevAnnContent GetInstance(object obj)
+		{
+			if (obj is RevAnnContent)
+				return (RevAnnContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new RevAnnContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public virtual PkiStatusEncodable Status
+		{
+			get { return status; }
+		}
+
+		public virtual CertId CertID
+		{
+			get { return certId; }
+		}
+
+		public virtual DerGeneralizedTime WillBeRevokedAt
+		{
+			get { return willBeRevokedAt; }
+		}
+
+		public virtual DerGeneralizedTime BadSinceDate
+		{
+			get { return badSinceDate; }
+		}
+
+		public virtual X509Extensions CrlDetails
+		{
+			get { return crlDetails; }
+		}
+
+		/**
+		 * <pre>
+		 * RevAnnContent ::= SEQUENCE {
+		 *       status              PKIStatus,
+		 *       certId              CertId,
+		 *       willBeRevokedAt     GeneralizedTime,
+		 *       badSinceDate        GeneralizedTime,
+		 *       crlDetails          Extensions  OPTIONAL
+		 *        -- extra CRL details (e.g., crl number, reason, location, etc.)
+		 * }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(status, certId, willBeRevokedAt, badSinceDate);
+			v.AddOptional(crlDetails);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/RevDetails.cs b/crypto/src/asn1/cmp/RevDetails.cs
new file mode 100644
index 000000000..1bd95f1db
--- /dev/null
+++ b/crypto/src/asn1/cmp/RevDetails.cs
@@ -0,0 +1,75 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class RevDetails
+		: Asn1Encodable
+	{
+		private readonly CertTemplate certDetails;
+		private readonly X509Extensions crlEntryDetails;
+
+		private RevDetails(Asn1Sequence seq)
+		{
+			certDetails = CertTemplate.GetInstance(seq[0]);
+
+			if  (seq.Count > 1)
+			{
+				crlEntryDetails = X509Extensions.GetInstance(seq[1]);
+			}
+		}
+
+		public static RevDetails GetInstance(object obj)
+		{
+			if (obj is RevDetails)
+				return (RevDetails)obj;
+
+			if (obj is Asn1Sequence)
+				return new RevDetails((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public RevDetails(CertTemplate certDetails)
+		{
+			this.certDetails = certDetails;
+		}
+		
+		public RevDetails(CertTemplate certDetails, X509Extensions crlEntryDetails)
+		{
+			this.crlEntryDetails = crlEntryDetails;
+		}
+
+		public virtual CertTemplate CertDetails
+		{
+			get { return certDetails; }
+		}
+
+		public virtual X509Extensions CrlEntryDetails
+		{
+			get { return crlEntryDetails; }
+		}
+
+		/**
+		* <pre>
+		* RevDetails ::= SEQUENCE {
+		*                  certDetails         CertTemplate,
+		*                   -- allows requester to specify as much as they can about
+		*                   -- the cert. for which revocation is requested
+		*                   -- (e.g., for cases in which serialNumber is not available)
+		*                   crlEntryDetails     Extensions       OPTIONAL
+		*                   -- requested crlEntryExtensions
+		*             }
+		* </pre>
+		* @return a basic ASN.1 object representation.
+		*/
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(certDetails);
+			v.AddOptional(crlEntryDetails);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/RevRepContent.cs b/crypto/src/asn1/cmp/RevRepContent.cs
new file mode 100644
index 000000000..47987265a
--- /dev/null
+++ b/crypto/src/asn1/cmp/RevRepContent.cs
@@ -0,0 +1,112 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class RevRepContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence status;
+		private readonly Asn1Sequence revCerts;
+		private readonly Asn1Sequence crls;
+
+		private RevRepContent(Asn1Sequence seq)
+		{
+			status = Asn1Sequence.GetInstance(seq[0]);
+
+			for (int pos = 1; pos < seq.Count; ++pos)
+			{
+				Asn1TaggedObject tObj = Asn1TaggedObject.GetInstance(seq[pos]);
+
+				if (tObj.TagNo == 0)
+				{
+					revCerts = Asn1Sequence.GetInstance(tObj, true);
+				}
+				else
+				{
+					crls = Asn1Sequence.GetInstance(tObj, true);
+				}
+			}
+		}
+
+		public static RevRepContent GetInstance(object obj)
+		{
+			if (obj is RevRepContent)
+				return (RevRepContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new RevRepContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+		
+		public virtual PkiStatusInfo[] GetStatus()
+		{
+			PkiStatusInfo[] results = new PkiStatusInfo[status.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = PkiStatusInfo.GetInstance(status[i]);
+			}
+			return results;
+		}
+
+		public virtual CertId[] GetRevCerts()
+		{
+			if (revCerts == null)
+				return null;
+
+			CertId[] results = new CertId[revCerts.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = CertId.GetInstance(revCerts[i]);
+			}
+			return results;
+		}
+
+		public virtual CertificateList[] GetCrls()
+		{
+			if (crls == null)
+				return null;
+
+			CertificateList[] results = new CertificateList[crls.Count];
+			for (int i = 0; i != results.Length; ++i)
+			{
+				results[i] = CertificateList.GetInstance(crls[i]);
+			}
+			return results;
+		}
+
+		/**
+		 * <pre>
+		 * RevRepContent ::= SEQUENCE {
+		 *        status       SEQUENCE SIZE (1..MAX) OF PKIStatusInfo,
+		 *        -- in same order as was sent in RevReqContent
+		 *        revCerts [0] SEQUENCE SIZE (1..MAX) OF CertId OPTIONAL,
+		 *        -- IDs for which revocation was requested
+		 *        -- (same order as status)
+		 *        crls     [1] SEQUENCE SIZE (1..MAX) OF CertificateList OPTIONAL
+		 *        -- the resulting CRLs (there may be more than one)
+		 *   }
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(status);
+			AddOptional(v, 0, revCerts);
+			AddOptional(v, 1, crls);
+			return new DerSequence(v);
+		}
+
+		private void AddOptional(Asn1EncodableVector v, int tagNo, Asn1Encodable obj)
+		{
+			if (obj != null)
+			{
+				v.Add(new DerTaggedObject(true, tagNo, obj));
+			}
+		}
+	}
+}
+
diff --git a/crypto/src/asn1/cmp/RevRepContentBuilder.cs b/crypto/src/asn1/cmp/RevRepContentBuilder.cs
new file mode 100644
index 000000000..cc17d1d4c
--- /dev/null
+++ b/crypto/src/asn1/cmp/RevRepContentBuilder.cs
@@ -0,0 +1,55 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class RevRepContentBuilder
+	{
+		private readonly Asn1EncodableVector status = new Asn1EncodableVector();
+		private readonly Asn1EncodableVector revCerts = new Asn1EncodableVector();
+		private readonly Asn1EncodableVector crls = new Asn1EncodableVector();
+
+		public virtual RevRepContentBuilder Add(PkiStatusInfo status)
+		{
+			this.status.Add(status);
+			return this;
+		}
+
+		public virtual RevRepContentBuilder Add(PkiStatusInfo status, CertId certId)
+		{
+			if (this.status.Count != this.revCerts.Count)
+				throw new InvalidOperationException("status and revCerts sequence must be in common order");
+
+			this.status.Add(status);
+			this.revCerts.Add(certId);
+			return this;
+		}
+
+		public virtual RevRepContentBuilder AddCrl(CertificateList crl)
+		{
+			this.crls.Add(crl);
+			return this;
+		}
+
+		public virtual RevRepContent Build()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector();
+
+			v.Add(new DerSequence(status));
+
+			if (revCerts.Count != 0)
+			{
+				v.Add(new DerTaggedObject(true, 0, new DerSequence(revCerts)));
+			}
+
+			if (crls.Count != 0)
+			{
+				v.Add(new DerTaggedObject(true, 1, new DerSequence(crls)));
+			}
+
+			return RevRepContent.GetInstance(new DerSequence(v));
+		}
+	}
+}
diff --git a/crypto/src/asn1/cmp/RevReqContent.cs b/crypto/src/asn1/cmp/RevReqContent.cs
new file mode 100644
index 000000000..fbf869203
--- /dev/null
+++ b/crypto/src/asn1/cmp/RevReqContent.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+	public class RevReqContent
+		: Asn1Encodable
+	{
+		private readonly Asn1Sequence content;
+		
+		private RevReqContent(Asn1Sequence seq)
+		{
+			content = seq;
+		}
+
+		public static RevReqContent GetInstance(object obj)
+		{
+			if (obj is RevReqContent)
+				return (RevReqContent)obj;
+
+			if (obj is Asn1Sequence)
+				return new RevReqContent((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid object: " + obj.GetType().Name, "obj");
+		}
+
+		public RevReqContent(params RevDetails[] revDetails)
+		{
+			this.content = new DerSequence(revDetails);
+		}
+
+		public virtual RevDetails[] ToRevDetailsArray()
+		{
+			RevDetails[] result = new RevDetails[content.Count];
+			for (int i = 0; i != result.Length; ++i)
+			{
+				result[i] = RevDetails.GetInstance(content[i]);
+			}
+			return result;
+		}
+
+		/**
+		 * <pre>
+		 * RevReqContent ::= SEQUENCE OF RevDetails
+		 * </pre>
+		 * @return a basic ASN.1 object representation.
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return content;
+		}
+	}
+}