summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-02-16 21:01:31 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-02-16 21:01:31 +0700
commitfd0d2d40b55157c88c7304a8fcfb4c8e947aa01f (patch)
treebef96e7e23e753599ccaadd6ba7439dfec8562fa
parentFix CSHAKETest (diff)
downloadBouncyCastle.NET-ed25519-fd0d2d40b55157c88c7304a8fcfb4c8e947aa01f.tar.xz
Use cached encodings for Equals/GetHashCode
-rw-r--r--crypto/src/x509/X509Certificate.cs945
-rw-r--r--crypto/src/x509/X509Crl.cs110
2 files changed, 595 insertions, 460 deletions
diff --git a/crypto/src/x509/X509Certificate.cs b/crypto/src/x509/X509Certificate.cs
index b4e1c17f8..4f4f1e991 100644
--- a/crypto/src/x509/X509Certificate.cs
+++ b/crypto/src/x509/X509Certificate.cs
@@ -23,31 +23,60 @@ namespace Org.BouncyCastle.X509
     /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects.
     /// </summary>
     public class X509Certificate
-		:	X509ExtensionBase
-//		, PKCS12BagAttributeCarrier
+        : X509ExtensionBase
+    //		, PKCS12BagAttributeCarrier
     {
+        private class CachedEncoding
+        {
+            private readonly byte[] encoding;
+            private readonly CertificateEncodingException exception;
+
+            internal CachedEncoding(byte[] encoding, CertificateEncodingException exception)
+            {
+                this.encoding = encoding;
+                this.exception = exception;
+            }
+
+            internal byte[] Encoding
+            {
+                get { return encoding; }
+            }
+
+            internal byte[] GetEncoded()
+            {
+                if (null != exception)
+                    throw exception;
+
+                if (null == encoding)
+                    throw new CertificateEncodingException();
+
+                return encoding;
+            }
+        }
+
         private readonly X509CertificateStructure c;
         //private Hashtable pkcs12Attributes = Platform.CreateHashtable();
         //private ArrayList pkcs12Ordering = Platform.CreateArrayList();
         private readonly string sigAlgName;
         private readonly byte[] sigAlgParams;
-		private readonly BasicConstraints basicConstraints;
-		private readonly bool[] keyUsage;
+        private readonly BasicConstraints basicConstraints;
+        private readonly bool[] keyUsage;
 
         private readonly object cacheLock = new object();
         private AsymmetricKeyParameter publicKeyValue;
+        private CachedEncoding cachedEncoding;
 
-		private volatile bool hashValueSet;
+        private volatile bool hashValueSet;
         private volatile int hashValue;
 
-		protected X509Certificate()
-		{
-		}
+        protected X509Certificate()
+        {
+        }
 
-		public X509Certificate(
-			X509CertificateStructure c)
-		{
-			this.c = c;
+        public X509Certificate(
+            X509CertificateStructure c)
+        {
+            this.c = c;
 
             try
             {
@@ -58,130 +87,129 @@ namespace Org.BouncyCastle.X509
             }
             catch (Exception e)
             {
-                throw new CrlException("Certificate contents invalid: " + e);
+                throw new CertificateParsingException("Certificate contents invalid: " + e);
+            }
+
+            try
+            {
+                Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.19"));
+
+                if (str != null)
+                {
+                    basicConstraints = BasicConstraints.GetInstance(
+                        X509ExtensionUtilities.FromExtensionValue(str));
+                }
+            }
+            catch (Exception e)
+            {
+                throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
             }
 
-			try
-			{
-				Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.19"));
-
-				if (str != null)
-				{
-					basicConstraints = BasicConstraints.GetInstance(
-						X509ExtensionUtilities.FromExtensionValue(str));
-				}
-			}
-			catch (Exception e)
-			{
-				throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
-			}
-
-			try
-			{
-				Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.15"));
-
-				if (str != null)
-				{
-					DerBitString bits = DerBitString.GetInstance(
-						X509ExtensionUtilities.FromExtensionValue(str));
-
-					byte[] bytes = bits.GetBytes();
-					int length = (bytes.Length * 8) - bits.PadBits;
-
-					keyUsage = new bool[(length < 9) ? 9 : length];
-
-					for (int i = 0; i != length; i++)
-					{
-//						keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
-						keyUsage[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0;
-					}
-				}
-				else
-				{
-					keyUsage = null;
-				}
-			}
-			catch (Exception e)
-			{
-				throw new CertificateParsingException("cannot construct KeyUsage: " + e);
-			}
-		}
-
-//		internal X509Certificate(
-//			Asn1Sequence seq)
-//        {
-//            this.c = X509CertificateStructure.GetInstance(seq);
-//        }
-
-//		/// <summary>
-//        /// Load certificate from byte array.
-//        /// </summary>
-//        /// <param name="encoded">Byte array containing encoded X509Certificate.</param>
-//        public X509Certificate(
-//            byte[] encoded)
-//			: this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject())
-//		{
-//        }
-//
-//        /// <summary>
-//        /// Load certificate from Stream.
-//        /// Must be positioned at start of certificate.
-//        /// </summary>
-//        /// <param name="input"></param>
-//        public X509Certificate(
-//            Stream input)
-//			: this((Asn1Sequence) new Asn1InputStream(input).ReadObject())
-//        {
-//        }
-
-		public virtual X509CertificateStructure CertificateStructure
-		{
-			get { return c; }
-		}
-
-		/// <summary>
+            try
+            {
+                Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.15"));
+
+                if (str != null)
+                {
+                    DerBitString bits = DerBitString.GetInstance(
+                        X509ExtensionUtilities.FromExtensionValue(str));
+
+                    byte[] bytes = bits.GetBytes();
+                    int length = (bytes.Length * 8) - bits.PadBits;
+
+                    keyUsage = new bool[(length < 9) ? 9 : length];
+
+                    for (int i = 0; i != length; i++)
+                    {
+                        keyUsage[i] = (bytes[i / 8] & (0x80 >> (i % 8))) != 0;
+                    }
+                }
+                else
+                {
+                    keyUsage = null;
+                }
+            }
+            catch (Exception e)
+            {
+                throw new CertificateParsingException("cannot construct KeyUsage: " + e);
+            }
+        }
+
+        //		internal X509Certificate(
+        //			Asn1Sequence seq)
+        //        {
+        //            this.c = X509CertificateStructure.GetInstance(seq);
+        //        }
+
+        //		/// <summary>
+        //        /// Load certificate from byte array.
+        //        /// </summary>
+        //        /// <param name="encoded">Byte array containing encoded X509Certificate.</param>
+        //        public X509Certificate(
+        //            byte[] encoded)
+        //			: this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject())
+        //		{
+        //        }
+        //
+        //        /// <summary>
+        //        /// Load certificate from Stream.
+        //        /// Must be positioned at start of certificate.
+        //        /// </summary>
+        //        /// <param name="input"></param>
+        //        public X509Certificate(
+        //            Stream input)
+        //			: this((Asn1Sequence) new Asn1InputStream(input).ReadObject())
+        //        {
+        //        }
+
+        public virtual X509CertificateStructure CertificateStructure
+        {
+            get { return c; }
+        }
+
+        /// <summary>
         /// Return true if the current time is within the start and end times nominated on the certificate.
         /// </summary>
         /// <returns>true id certificate is valid for the current time.</returns>
         public virtual bool IsValidNow
         {
-			get { return IsValid(DateTime.UtcNow); }
+            get { return IsValid(DateTime.UtcNow); }
         }
 
-		/// <summary>
+        /// <summary>
         /// Return true if the nominated time is within the start and end times nominated on the certificate.
         /// </summary>
         /// <param name="time">The time to test validity against.</param>
         /// <returns>True if certificate is valid for nominated time.</returns>
         public virtual bool IsValid(
-			DateTime time)
+            DateTime time)
         {
             return time.CompareTo(NotBefore) >= 0 && time.CompareTo(NotAfter) <= 0;
         }
 
-		/// <summary>
-		/// Checks if the current date is within certificate's validity period.
-		/// </summary>
-		public virtual void CheckValidity()
-		{
-			this.CheckValidity(DateTime.UtcNow);
-		}
+        /// <summary>
+        /// Checks if the current date is within certificate's validity period.
+        /// </summary>
+        public virtual void CheckValidity()
+        {
+            this.CheckValidity(DateTime.UtcNow);
+        }
 
-		/// <summary>
-		/// Checks if the given date is within certificate's validity period.
-		/// </summary>
-		/// <exception cref="CertificateExpiredException">if the certificate is expired by given date</exception>
-		/// <exception cref="CertificateNotYetValidException">if the certificate is not yet valid on given date</exception>
-		public virtual void CheckValidity(
-			DateTime time)
-		{
-			if (time.CompareTo(NotAfter) > 0)
-				throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime());
-			if (time.CompareTo(NotBefore) < 0)
-				throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime());
-		}
-
-		/// <summary>
+        /// <summary>
+        /// Checks if the given date is within certificate's validity period.
+        /// </summary>
+        /// <exception cref="CertificateExpiredException">if the certificate is expired by given date</exception>
+        /// <exception cref="CertificateNotYetValidException">if the certificate is not yet valid on given date</exception>
+        public virtual void CheckValidity(
+            DateTime time)
+        {
+            if (time.CompareTo(NotAfter) > 0)
+                throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime());
+            if (time.CompareTo(NotBefore) < 0)
+                throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime());
+        }
+
+        /// <summary>
         /// Return the certificate's version.
         /// </summary>
         /// <returns>An integer whose value Equals the version of the cerficate.</returns>
@@ -190,7 +218,7 @@ namespace Org.BouncyCastle.X509
             get { return c.Version; }
         }
 
-		/// <summary>
+        /// <summary>
         /// Return a <see cref="Org.BouncyCastle.Math.BigInteger">BigInteger</see> containing the serial number.
         /// </summary>
         /// <returns>The Serial number.</returns>
@@ -199,206 +227,206 @@ namespace Org.BouncyCastle.X509
             get { return c.SerialNumber.Value; }
         }
 
-		/// <summary>
+        /// <summary>
         /// Get the Issuer Distinguished Name. (Who signed the certificate.)
         /// </summary>
         /// <returns>And X509Object containing name and value pairs.</returns>
-//        public IPrincipal IssuerDN
-		public virtual X509Name IssuerDN
-		{
+        //        public IPrincipal IssuerDN
+        public virtual X509Name IssuerDN
+        {
             get { return c.Issuer; }
         }
 
-		/// <summary>
+        /// <summary>
         /// Get the subject of this certificate.
         /// </summary>
         /// <returns>An X509Name object containing name and value pairs.</returns>
-//        public IPrincipal SubjectDN
-		public virtual X509Name SubjectDN
-		{
+        //        public IPrincipal SubjectDN
+        public virtual X509Name SubjectDN
+        {
             get { return c.Subject; }
         }
 
-		/// <summary>
-		/// The time that this certificate is valid from.
-		/// </summary>
-		/// <returns>A DateTime object representing that time in the local time zone.</returns>
-		public virtual DateTime NotBefore
-		{
-			get { return c.StartDate.ToDateTime(); }
-		}
+        /// <summary>
+        /// The time that this certificate is valid from.
+        /// </summary>
+        /// <returns>A DateTime object representing that time in the local time zone.</returns>
+        public virtual DateTime NotBefore
+        {
+            get { return c.StartDate.ToDateTime(); }
+        }
 
-		/// <summary>
+        /// <summary>
         /// The time that this certificate is valid up to.
         /// </summary>
         /// <returns>A DateTime object representing that time in the local time zone.</returns>
         public virtual DateTime NotAfter
         {
-			get { return c.EndDate.ToDateTime(); }
+            get { return c.EndDate.ToDateTime(); }
         }
 
-		/// <summary>
-		/// Return the Der encoded TbsCertificate data.
-		/// This is the certificate component less the signature.
-		/// To Get the whole certificate call the GetEncoded() member.
-		/// </summary>
-		/// <returns>A byte array containing the Der encoded Certificate component.</returns>
-		public virtual byte[] GetTbsCertificate()
-		{
-			return c.TbsCertificate.GetDerEncoded();
-		}
-
-		/// <summary>
-		/// The signature.
-		/// </summary>
-		/// <returns>A byte array containg the signature of the certificate.</returns>
-		public virtual byte[] GetSignature()
-		{
-			return c.GetSignatureOctets();
-		}
+        /// <summary>
+        /// Return the Der encoded TbsCertificate data.
+        /// This is the certificate component less the signature.
+        /// To Get the whole certificate call the GetEncoded() member.
+        /// </summary>
+        /// <returns>A byte array containing the Der encoded Certificate component.</returns>
+        public virtual byte[] GetTbsCertificate()
+        {
+            return c.TbsCertificate.GetDerEncoded();
+        }
+
+        /// <summary>
+        /// The signature.
+        /// </summary>
+        /// <returns>A byte array containg the signature of the certificate.</returns>
+        public virtual byte[] GetSignature()
+        {
+            return c.GetSignatureOctets();
+        }
 
         /// <summary>
 		/// A meaningful version of the Signature Algorithm. (EG SHA1WITHRSA)
 		/// </summary>
 		/// <returns>A sting representing the signature algorithm.</returns>
 		public virtual string SigAlgName
-		{
+        {
             get { return sigAlgName; }
-		}
+        }
 
-		/// <summary>
-		/// Get the Signature Algorithms Object ID.
-		/// </summary>
-		/// <returns>A string containg a '.' separated object id.</returns>
-		public virtual string SigAlgOid
-		{
+        /// <summary>
+        /// Get the Signature Algorithms Object ID.
+        /// </summary>
+        /// <returns>A string containg a '.' separated object id.</returns>
+        public virtual string SigAlgOid
+        {
             get { return c.SignatureAlgorithm.Algorithm.Id; }
-		}
+        }
 
-		/// <summary>
-		/// Get the signature algorithms parameters. (EG DSA Parameters)
-		/// </summary>
-		/// <returns>A byte array containing the Der encoded version of the parameters or null if there are none.</returns>
-		public virtual byte[] GetSigAlgParams()
-		{
+        /// <summary>
+        /// Get the signature algorithms parameters. (EG DSA Parameters)
+        /// </summary>
+        /// <returns>A byte array containing the Der encoded version of the parameters or null if there are none.</returns>
+        public virtual byte[] GetSigAlgParams()
+        {
             return Arrays.Clone(sigAlgParams);
-		}
+        }
 
-		/// <summary>
-		/// Get the issuers UID.
-		/// </summary>
-		/// <returns>A DerBitString.</returns>
-		public virtual DerBitString IssuerUniqueID
-		{
-			get { return c.TbsCertificate.IssuerUniqueID; }
-		}
-
-		/// <summary>
-		/// Get the subjects UID.
-		/// </summary>
-		/// <returns>A DerBitString.</returns>
-		public virtual DerBitString SubjectUniqueID
-		{
-			get { return c.TbsCertificate.SubjectUniqueID; }
-		}
-
-		/// <summary>
-		/// Get a key usage guidlines.
-		/// </summary>
-		public virtual bool[] GetKeyUsage()
-		{
+        /// <summary>
+        /// Get the issuers UID.
+        /// </summary>
+        /// <returns>A DerBitString.</returns>
+        public virtual DerBitString IssuerUniqueID
+        {
+            get { return c.TbsCertificate.IssuerUniqueID; }
+        }
+
+        /// <summary>
+        /// Get the subjects UID.
+        /// </summary>
+        /// <returns>A DerBitString.</returns>
+        public virtual DerBitString SubjectUniqueID
+        {
+            get { return c.TbsCertificate.SubjectUniqueID; }
+        }
+
+        /// <summary>
+        /// Get a key usage guidlines.
+        /// </summary>
+        public virtual bool[] GetKeyUsage()
+        {
             return Arrays.Clone(keyUsage);
-		}
+        }
 
-		// TODO Replace with something that returns a list of DerObjectIdentifier
-		public virtual IList GetExtendedKeyUsage()
-		{
-			Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.37"));
+        // TODO Replace with something that returns a list of DerObjectIdentifier
+        public virtual IList GetExtendedKeyUsage()
+        {
+            Asn1OctetString str = this.GetExtensionValue(new DerObjectIdentifier("2.5.29.37"));
 
-			if (str == null)
-				return null;
+            if (str == null)
+                return null;
 
-			try
-			{
-				Asn1Sequence seq = Asn1Sequence.GetInstance(
-					X509ExtensionUtilities.FromExtensionValue(str));
+            try
+            {
+                Asn1Sequence seq = Asn1Sequence.GetInstance(
+                    X509ExtensionUtilities.FromExtensionValue(str));
 
                 IList list = Platform.CreateArrayList();
 
-				foreach (DerObjectIdentifier oid in seq)
-				{
-					list.Add(oid.Id);
-				}
+                foreach (DerObjectIdentifier oid in seq)
+                {
+                    list.Add(oid.Id);
+                }
 
-				return list;
-			}
-			catch (Exception e)
-			{
-				throw new CertificateParsingException("error processing extended key usage extension", e);
-			}
-		}
+                return list;
+            }
+            catch (Exception e)
+            {
+                throw new CertificateParsingException("error processing extended key usage extension", e);
+            }
+        }
 
-		public virtual int GetBasicConstraints()
-		{
-			if (basicConstraints != null && basicConstraints.IsCA())
-			{
-				if (basicConstraints.PathLenConstraint == null)
-				{
-					return int.MaxValue;
-				}
+        public virtual int GetBasicConstraints()
+        {
+            if (basicConstraints != null && basicConstraints.IsCA())
+            {
+                if (basicConstraints.PathLenConstraint == null)
+                {
+                    return int.MaxValue;
+                }
 
-				return basicConstraints.PathLenConstraint.IntValue;
-			}
+                return basicConstraints.PathLenConstraint.IntValue;
+            }
 
-			return -1;
-		}
+            return -1;
+        }
 
-		public virtual ICollection GetSubjectAlternativeNames()
-		{
-			return GetAlternativeNames("2.5.29.17");
-		}
+        public virtual ICollection GetSubjectAlternativeNames()
+        {
+            return GetAlternativeNames("2.5.29.17");
+        }
 
-		public virtual ICollection GetIssuerAlternativeNames()
-		{
-			return GetAlternativeNames("2.5.29.18");
-		}
+        public virtual ICollection GetIssuerAlternativeNames()
+        {
+            return GetAlternativeNames("2.5.29.18");
+        }
 
-		protected virtual ICollection GetAlternativeNames(
-			string oid)
-		{
-			Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid));
+        protected virtual ICollection GetAlternativeNames(
+            string oid)
+        {
+            Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid));
 
-			if (altNames == null)
-				return null;
+            if (altNames == null)
+                return null;
 
-			Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames);
+            Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames);
 
-			GeneralNames gns = GeneralNames.GetInstance(asn1Object);
+            GeneralNames gns = GeneralNames.GetInstance(asn1Object);
 
             IList result = Platform.CreateArrayList();
-			foreach (GeneralName gn in gns.GetNames())
-			{
+            foreach (GeneralName gn in gns.GetNames())
+            {
                 IList entry = Platform.CreateArrayList();
-				entry.Add(gn.TagNo);
-				entry.Add(gn.Name.ToString());
-				result.Add(entry);
-			}
-			return result;
-		}
-
-		protected override X509Extensions GetX509Extensions()
-		{
-			return c.Version >= 3
-				?	c.TbsCertificate.Extensions
-				:	null;
-		}
-
-		/// <summary>
-		/// Get the public key of the subject of the certificate.
-		/// </summary>
-		/// <returns>The public key parameters.</returns>
-		public virtual AsymmetricKeyParameter GetPublicKey()
-		{
+                entry.Add(gn.TagNo);
+                entry.Add(gn.Name.ToString());
+                result.Add(entry);
+            }
+            return result;
+        }
+
+        protected override X509Extensions GetX509Extensions()
+        {
+            return c.Version >= 3
+                ? c.TbsCertificate.Extensions
+                : null;
+        }
+
+        /// <summary>
+        /// Get the public key of the subject of the certificate.
+        /// </summary>
+        /// <returns>The public key parameters.</returns>
+        public virtual AsymmetricKeyParameter GetPublicKey()
+        {
             // Cache the public key to support repeated-use optimizations
             lock (cacheLock)
             {
@@ -406,7 +434,7 @@ namespace Org.BouncyCastle.X509
                     return publicKeyValue;
             }
 
-			AsymmetricKeyParameter temp = PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo);
+            AsymmetricKeyParameter temp = PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo);
 
             lock (cacheLock)
             {
@@ -417,172 +445,179 @@ namespace Org.BouncyCastle.X509
 
                 return publicKeyValue;
             }
-		}
+        }
 
-		/// <summary>
-		/// Return a Der encoded version of this certificate.
-		/// </summary>
-		/// <returns>A byte array.</returns>
-		public virtual byte[] GetEncoded()
-		{
-			return c.GetDerEncoded();
-		}
+        /// <summary>
+        /// Return the DER encoding of this certificate.
+        /// </summary>
+        /// <returns>A byte array containing the DER encoding of this certificate.</returns>
+        /// <exception cref="CertificateEncodingException">If there is an error encoding the certificate.</exception>
+        public virtual byte[] GetEncoded()
+        {
+            return Arrays.Clone(GetCachedEncoding().GetEncoded());
+        }
 
         public override bool Equals(object other)
-		{
-			if (this == other)
-				return true;
+        {
+            if (this == other)
+                return true;
 
-			X509Certificate that = other as X509Certificate;
-			if (null == that)
-				return false;
+            X509Certificate that = other as X509Certificate;
+            if (null == that)
+                return false;
 
             if (this.hashValueSet && that.hashValueSet)
             {
                 if (this.hashValue != that.hashValue)
                     return false;
             }
-            else if (!this.c.Signature.Equals(that.c.Signature))
+            else if (null == this.cachedEncoding || null == that.cachedEncoding)
             {
-                return false;
+                DerBitString signature = c.Signature;
+                if (null != signature && !signature.Equals(that.c.Signature))
+                    return false;
             }
 
-			return this.c.Equals(that.c);
+            byte[] thisEncoding = this.GetCachedEncoding().Encoding;
+            byte[] thatEncoding = that.GetCachedEncoding().Encoding;
+
+            return null != thisEncoding
+                && null != thatEncoding
+                && Arrays.AreEqual(thisEncoding, thatEncoding);
+        }
 
-			// NB: May prefer this implementation of Equals if more than one certificate implementation in play
-            //return Arrays.AreEqual(this.GetEncoded(), that.GetEncoded());
-		}
+        public override int GetHashCode()
+        {
+            if (!hashValueSet)
+            {
+                byte[] thisEncoding = this.GetCachedEncoding().Encoding;
 
-		public override int GetHashCode()
-		{
-			if (!hashValueSet)
-			{
-				hashValue = this.c.GetHashCode();
-				hashValueSet = true;
-			}
+                hashValue = Arrays.GetHashCode(thisEncoding);
+                hashValueSet = true;
+            }
 
             return hashValue;
-		}
-
-//		public void setBagAttribute(
-//			DERObjectIdentifier oid,
-//			DEREncodable        attribute)
-//		{
-//			pkcs12Attributes.put(oid, attribute);
-//			pkcs12Ordering.addElement(oid);
-//		}
-//
-//		public DEREncodable getBagAttribute(
-//			DERObjectIdentifier oid)
-//		{
-//			return (DEREncodable)pkcs12Attributes.get(oid);
-//		}
-//
-//		public Enumeration getBagAttributeKeys()
-//		{
-//			return pkcs12Ordering.elements();
-//		}
-
-		public override string ToString()
-		{
-			StringBuilder buf = new StringBuilder();
-			string nl = Platform.NewLine;
-
-			buf.Append("  [0]         Version: ").Append(this.Version).Append(nl);
-			buf.Append("         SerialNumber: ").Append(this.SerialNumber).Append(nl);
-			buf.Append("             IssuerDN: ").Append(this.IssuerDN).Append(nl);
-			buf.Append("           Start Date: ").Append(this.NotBefore).Append(nl);
-			buf.Append("           Final Date: ").Append(this.NotAfter).Append(nl);
-			buf.Append("            SubjectDN: ").Append(this.SubjectDN).Append(nl);
-			buf.Append("           Public Key: ").Append(this.GetPublicKey()).Append(nl);
-			buf.Append("  Signature Algorithm: ").Append(this.SigAlgName).Append(nl);
-
-			byte[] sig = this.GetSignature();
-			buf.Append("            Signature: ").Append(Hex.ToHexString(sig, 0, 20)).Append(nl);
-
-			for (int i = 20; i < sig.Length; i += 20)
-			{
-				int len = System.Math.Min(20, sig.Length - i);
-				buf.Append("                       ").Append(Hex.ToHexString(sig, i, len)).Append(nl);
-			}
-
-			X509Extensions extensions = c.TbsCertificate.Extensions;
-
-			if (extensions != null)
-			{
-				IEnumerator e = extensions.ExtensionOids.GetEnumerator();
-
-				if (e.MoveNext())
-				{
-					buf.Append("       Extensions: \n");
-				}
-
-				do
-				{
-					DerObjectIdentifier oid = (DerObjectIdentifier)e.Current;
-					X509Extension ext = extensions.GetExtension(oid);
-
-					if (ext.Value != null)
-					{
+        }
+
+        //		public void setBagAttribute(
+        //			DERObjectIdentifier oid,
+        //			DEREncodable        attribute)
+        //		{
+        //			pkcs12Attributes.put(oid, attribute);
+        //			pkcs12Ordering.addElement(oid);
+        //		}
+        //
+        //		public DEREncodable getBagAttribute(
+        //			DERObjectIdentifier oid)
+        //		{
+        //			return (DEREncodable)pkcs12Attributes.get(oid);
+        //		}
+        //
+        //		public Enumeration getBagAttributeKeys()
+        //		{
+        //			return pkcs12Ordering.elements();
+        //		}
+
+        public override string ToString()
+        {
+            StringBuilder buf = new StringBuilder();
+            string nl = Platform.NewLine;
+
+            buf.Append("  [0]         Version: ").Append(this.Version).Append(nl);
+            buf.Append("         SerialNumber: ").Append(this.SerialNumber).Append(nl);
+            buf.Append("             IssuerDN: ").Append(this.IssuerDN).Append(nl);
+            buf.Append("           Start Date: ").Append(this.NotBefore).Append(nl);
+            buf.Append("           Final Date: ").Append(this.NotAfter).Append(nl);
+            buf.Append("            SubjectDN: ").Append(this.SubjectDN).Append(nl);
+            buf.Append("           Public Key: ").Append(this.GetPublicKey()).Append(nl);
+            buf.Append("  Signature Algorithm: ").Append(this.SigAlgName).Append(nl);
+
+            byte[] sig = this.GetSignature();
+            buf.Append("            Signature: ").Append(Hex.ToHexString(sig, 0, 20)).Append(nl);
+
+            for (int i = 20; i < sig.Length; i += 20)
+            {
+                int len = System.Math.Min(20, sig.Length - i);
+                buf.Append("                       ").Append(Hex.ToHexString(sig, i, len)).Append(nl);
+            }
+
+            X509Extensions extensions = c.TbsCertificate.Extensions;
+
+            if (extensions != null)
+            {
+                IEnumerator e = extensions.ExtensionOids.GetEnumerator();
+
+                if (e.MoveNext())
+                {
+                    buf.Append("       Extensions: \n");
+                }
+
+                do
+                {
+                    DerObjectIdentifier oid = (DerObjectIdentifier)e.Current;
+                    X509Extension ext = extensions.GetExtension(oid);
+
+                    if (ext.Value != null)
+                    {
                         Asn1Object obj = X509ExtensionUtilities.FromExtensionValue(ext.Value);
 
                         buf.Append("                       critical(").Append(ext.IsCritical).Append(") ");
-						try
-						{
-							if (oid.Equals(X509Extensions.BasicConstraints))
-							{
-								buf.Append(BasicConstraints.GetInstance(obj));
-							}
-							else if (oid.Equals(X509Extensions.KeyUsage))
-							{
-								buf.Append(KeyUsage.GetInstance(obj));
-							}
-							else if (oid.Equals(MiscObjectIdentifiers.NetscapeCertType))
-							{
-								buf.Append(new NetscapeCertType((DerBitString) obj));
-							}
-							else if (oid.Equals(MiscObjectIdentifiers.NetscapeRevocationUrl))
-							{
-								buf.Append(new NetscapeRevocationUrl((DerIA5String) obj));
-							}
-							else if (oid.Equals(MiscObjectIdentifiers.VerisignCzagExtension))
-							{
-								buf.Append(new VerisignCzagExtension((DerIA5String) obj));
-							}
-							else
-							{
-								buf.Append(oid.Id);
-								buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj));
-								//buf.Append(" value = ").Append("*****").Append(nl);
-							}
-						}
-						catch (Exception)
-						{
-							buf.Append(oid.Id);
-							//buf.Append(" value = ").Append(new string(Hex.encode(ext.getValue().getOctets()))).Append(nl);
-							buf.Append(" value = ").Append("*****");
-						}
-					}
-
-					buf.Append(nl);
-				}
-				while (e.MoveNext());
-			}
-
-			return buf.ToString();
-		}
-
-		/// <summary>
-		/// Verify the certificate's signature using the nominated public key.
-		/// </summary>
-		/// <param name="key">An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters</param>
-		/// <returns>True if the signature is valid.</returns>
-		/// <exception cref="Exception">If key submitted is not of the above nominated types.</exception>
-		public virtual void Verify(
-			AsymmetricKeyParameter key)
-		{
-			CheckSignature(new Asn1VerifierFactory(c.SignatureAlgorithm, key));
-		}
+                        try
+                        {
+                            if (oid.Equals(X509Extensions.BasicConstraints))
+                            {
+                                buf.Append(BasicConstraints.GetInstance(obj));
+                            }
+                            else if (oid.Equals(X509Extensions.KeyUsage))
+                            {
+                                buf.Append(KeyUsage.GetInstance(obj));
+                            }
+                            else if (oid.Equals(MiscObjectIdentifiers.NetscapeCertType))
+                            {
+                                buf.Append(new NetscapeCertType((DerBitString)obj));
+                            }
+                            else if (oid.Equals(MiscObjectIdentifiers.NetscapeRevocationUrl))
+                            {
+                                buf.Append(new NetscapeRevocationUrl((DerIA5String)obj));
+                            }
+                            else if (oid.Equals(MiscObjectIdentifiers.VerisignCzagExtension))
+                            {
+                                buf.Append(new VerisignCzagExtension((DerIA5String)obj));
+                            }
+                            else
+                            {
+                                buf.Append(oid.Id);
+                                buf.Append(" value = ").Append(Asn1Dump.DumpAsString(obj));
+                                //buf.Append(" value = ").Append("*****").Append(nl);
+                            }
+                        }
+                        catch (Exception)
+                        {
+                            buf.Append(oid.Id);
+                            //buf.Append(" value = ").Append(new string(Hex.encode(ext.getValue().getOctets()))).Append(nl);
+                            buf.Append(" value = ").Append("*****");
+                        }
+                    }
+
+                    buf.Append(nl);
+                }
+                while (e.MoveNext());
+            }
+
+            return buf.ToString();
+        }
+
+        /// <summary>
+        /// Verify the certificate's signature using the nominated public key.
+        /// </summary>
+        /// <param name="key">An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters</param>
+        /// <returns>True if the signature is valid.</returns>
+        /// <exception cref="Exception">If key submitted is not of the above nominated types.</exception>
+        public virtual void Verify(
+            AsymmetricKeyParameter key)
+        {
+            CheckSignature(new Asn1VerifierFactory(c.SignatureAlgorithm, key));
+        }
 
         /// <summary>
         /// Verify the certificate's signature using a verifier created using the passed in verifier provider.
@@ -593,46 +628,78 @@ namespace Org.BouncyCastle.X509
         public virtual void Verify(
             IVerifierFactoryProvider verifierProvider)
         {
-            CheckSignature(verifierProvider.CreateVerifierFactory (c.SignatureAlgorithm));
+            CheckSignature(verifierProvider.CreateVerifierFactory(c.SignatureAlgorithm));
         }
 
         protected virtual void CheckSignature(
-			IVerifierFactory verifier)
-		{
-			if (!IsAlgIDEqual(c.SignatureAlgorithm, c.TbsCertificate.Signature))
-				throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
+            IVerifierFactory verifier)
+        {
+            if (!IsAlgIDEqual(c.SignatureAlgorithm, c.TbsCertificate.Signature))
+                throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
 
-			Asn1Encodable parameters = c.SignatureAlgorithm.Parameters;
+            Asn1Encodable parameters = c.SignatureAlgorithm.Parameters;
 
             IStreamCalculator streamCalculator = verifier.CreateCalculator();
 
-			byte[] b = this.GetTbsCertificate();
+            byte[] b = this.GetTbsCertificate();
 
-			streamCalculator.Stream.Write(b, 0, b.Length);
+            streamCalculator.Stream.Write(b, 0, b.Length);
 
             Platform.Dispose(streamCalculator.Stream);
 
             if (!((IVerifier)streamCalculator.GetResult()).IsVerified(this.GetSignature()))
-			{
-				throw new InvalidKeyException("Public key presented not for certificate signature");
-			}
-		}
+            {
+                throw new InvalidKeyException("Public key presented not for certificate signature");
+            }
+        }
+
+        private CachedEncoding GetCachedEncoding()
+        {
+            lock (cacheLock)
+            {
+                if (null != cachedEncoding)
+                    return cachedEncoding;
+            }
+
+            byte[] encoding = null;
+            CertificateEncodingException exception = null;
+            try
+            {
+                encoding = c.GetEncoded(Asn1Encodable.Der);
+            }
+            catch (IOException e)
+            {
+                exception = new CertificateEncodingException("Failed to DER-encode certificate", e);
+            }
+
+            CachedEncoding temp = new CachedEncoding(encoding, exception);
+
+            lock (cacheLock)
+            {
+                if (null == cachedEncoding)
+                {
+                    cachedEncoding = temp;
+                }
 
-		private static bool IsAlgIDEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
-		{
+                return cachedEncoding;
+            }
+        }
+
+        private static bool IsAlgIDEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
+        {
             if (!id1.Algorithm.Equals(id2.Algorithm))
-				return false;
+                return false;
 
-			Asn1Encodable p1 = id1.Parameters;
-			Asn1Encodable p2 = id2.Parameters;
+            Asn1Encodable p1 = id1.Parameters;
+            Asn1Encodable p2 = id2.Parameters;
 
-			if ((p1 == null) == (p2 == null))
-				return Platform.Equals(p1, p2);
+            if ((p1 == null) == (p2 == null))
+                return Platform.Equals(p1, p2);
 
-			// Exactly one of p1, p2 is null at this point
-			return p1 == null
-				?	p2.ToAsn1Object() is Asn1Null
-				:	p1.ToAsn1Object() is Asn1Null;
-		}
-	}
-}
+            // Exactly one of p1, p2 is null at this point
+            return p1 == null
+                ? p2.ToAsn1Object() is Asn1Null
+                : p1.ToAsn1Object() is Asn1Null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/crypto/src/x509/X509Crl.cs b/crypto/src/x509/X509Crl.cs
index 7b841599f..5cf4bd53c 100644
--- a/crypto/src/x509/X509Crl.cs
+++ b/crypto/src/x509/X509Crl.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Collections;
+using System.IO;
 using System.Text;
 
 using Org.BouncyCastle.Asn1;
@@ -31,11 +32,42 @@ namespace Org.BouncyCastle.X509
 		: X509ExtensionBase
 		// TODO Add interface Crl?
 	{
-		private readonly CertificateList c;
+        private class CachedEncoding
+        {
+            private readonly byte[] encoding;
+            private readonly CrlException exception;
+
+            internal CachedEncoding(byte[] encoding, CrlException exception)
+            {
+                this.encoding = encoding;
+                this.exception = exception;
+            }
+
+            internal byte[] Encoding
+            {
+                get { return encoding; }
+            }
+
+            internal byte[] GetEncoded()
+            {
+                if (null != exception)
+                    throw exception;
+
+                if (null == encoding)
+                    throw new CrlException();
+
+                return encoding;
+            }
+        }
+
+        private readonly CertificateList c;
 		private readonly string sigAlgName;
 		private readonly byte[] sigAlgParams;
 		private readonly bool isIndirect;
 
+        private readonly object cacheLock = new object();
+        private CachedEncoding cachedEncoding;
+
         private volatile bool hashValueSet;
         private volatile int hashValue;
 
@@ -66,18 +98,6 @@ namespace Org.BouncyCastle.X509
 				:	null;
 		}
 
-		public virtual byte[] GetEncoded()
-		{
-			try
-			{
-				return c.GetDerEncoded();
-			}
-			catch (Exception e)
-			{
-				throw new CrlException(e.ToString());
-			}
-		}
-
 		public virtual void Verify(
 			AsymmetricKeyParameter publicKey)
 		{
@@ -226,7 +246,17 @@ namespace Org.BouncyCastle.X509
 			return Arrays.Clone(sigAlgParams);
 		}
 
-		public override bool Equals(object other)
+        /// <summary>
+        /// Return the DER encoding of this CRL.
+        /// </summary>
+        /// <returns>A byte array containing the DER encoding of this CRL.</returns>
+        /// <exception cref="CrlException">If there is an error encoding the CRL.</exception>
+        public virtual byte[] GetEncoded()
+        {
+            return Arrays.Clone(GetCachedEncoding().GetEncoded());
+        }
+
+        public override bool Equals(object other)
 		{
             if (this == other)
                 return true;
@@ -240,22 +270,28 @@ namespace Org.BouncyCastle.X509
                 if (this.hashValue != that.hashValue)
                     return false;
             }
-            else if (!this.c.Signature.Equals(that.c.Signature))
+            else if (null == this.cachedEncoding || null == that.cachedEncoding)
             {
-                return false;
+                DerBitString signature = c.Signature;
+                if (null != signature && !signature.Equals(that.c.Signature))
+                    return false;
             }
 
-            return this.c.Equals(that.c);
+            byte[] thisEncoding = this.GetCachedEncoding().Encoding;
+            byte[] thatEncoding = that.GetCachedEncoding().Encoding;
 
-            // NB: May prefer this implementation of Equals if more than one CRL implementation in play
-			//return Arrays.AreEqual(this.GetEncoded(), that.GetEncoded());
+            return null != thisEncoding
+                && null != thatEncoding
+                && Arrays.AreEqual(thisEncoding, thatEncoding);
 		}
 
         public override int GetHashCode()
         {
             if (!hashValueSet)
             {
-                hashValue = this.c.GetHashCode();
+                byte[] thisEncoding = this.GetCachedEncoding().Encoding;
+
+                hashValue = Arrays.GetHashCode(thisEncoding);
                 hashValueSet = true;
             }
 
@@ -433,5 +469,37 @@ namespace Org.BouncyCastle.X509
 				return isIndirect;
 			}
 		}
-	}
+
+        private CachedEncoding GetCachedEncoding()
+        {
+            lock (cacheLock)
+            {
+                if (null != cachedEncoding)
+                    return cachedEncoding;
+            }
+
+            byte[] encoding = null;
+            CrlException exception = null;
+            try
+            {
+                encoding = c.GetEncoded(Asn1Encodable.Der);
+            }
+            catch (IOException e)
+            {
+                exception = new CrlException("Failed to DER-encode CRL", e);
+            }
+
+            CachedEncoding temp = new CachedEncoding(encoding, exception);
+
+            lock (cacheLock)
+            {
+                if (null == cachedEncoding)
+                {
+                    cachedEncoding = temp;
+                }
+
+                return cachedEncoding;
+            }
+        }
+    }
 }