using System; using System.Collections; using System.IO; using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Misc; using Org.BouncyCastle.Asn1.Utilities; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; using Org.BouncyCastle.Security.Certificates; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.X509.Extension; namespace Org.BouncyCastle.X509 { /// /// An Object representing an X509 Certificate. /// Has static methods for loading Certificates encoded in many forms that return X509Certificate Objects. /// public class X509Certificate : X509ExtensionBase // , PKCS12BagAttributeCarrier { private readonly X509CertificateStructure c; // private Hashtable pkcs12Attributes = new Hashtable(); // private ArrayList pkcs12Ordering = new ArrayList(); private readonly BasicConstraints basicConstraints; private readonly bool[] keyUsage; private bool hashValueSet; private int hashValue; protected X509Certificate() { } public X509Certificate( X509CertificateStructure c) { this.c = c; 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); // } // /// // /// Load certificate from byte array. // /// // /// Byte array containing encoded X509Certificate. // public X509Certificate( // byte[] encoded) // : this((Asn1Sequence) new Asn1InputStream(encoded).ReadObject()) // { // } // // /// // /// Load certificate from Stream. // /// Must be positioned at start of certificate. // /// // /// // public X509Certificate( // Stream input) // : this((Asn1Sequence) new Asn1InputStream(input).ReadObject()) // { // } public virtual X509CertificateStructure CertificateStructure { get { return c; } } /// /// Return true if the current time is within the start and end times nominated on the certificate. /// /// true id certificate is valid for the current time. public virtual bool IsValidNow { get { return IsValid(DateTime.UtcNow); } } /// /// Return true if the nominated time is within the start and end times nominated on the certificate. /// /// The time to test validity against. /// True if certificate is valid for nominated time. public virtual bool IsValid( DateTime time) { return time.CompareTo(NotBefore) >= 0 && time.CompareTo(NotAfter) <= 0; } /// /// Checks if the current date is within certificate's validity period. /// public virtual void CheckValidity() { this.CheckValidity(DateTime.UtcNow); } /// /// Checks if the given date is within certificate's validity period. /// /// if the certificate is expired by given date /// if the certificate is not yet valid on given date 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()); } /// /// Return the certificate's version. /// /// An integer whose value Equals the version of the cerficate. public virtual int Version { get { return c.Version; } } /// /// Return a BigInteger containing the serial number. /// /// The Serial number. public virtual BigInteger SerialNumber { get { return c.SerialNumber.Value; } } /// /// Get the Issuer Distinguished Name. (Who signed the certificate.) /// /// And X509Object containing name and value pairs. // public IPrincipal IssuerDN public virtual X509Name IssuerDN { get { return c.Issuer; } } /// /// Get the subject of this certificate. /// /// An X509Name object containing name and value pairs. // public IPrincipal SubjectDN public virtual X509Name SubjectDN { get { return c.Subject; } } /// /// The time that this certificate is valid from. /// /// A DateTime object representing that time in the local time zone. public virtual DateTime NotBefore { get { return c.StartDate.ToDateTime(); } } /// /// The time that this certificate is valid up to. /// /// A DateTime object representing that time in the local time zone. public virtual DateTime NotAfter { get { return c.EndDate.ToDateTime(); } } /// /// Return the Der encoded TbsCertificate data. /// This is the certificate component less the signature. /// To Get the whole certificate call the GetEncoded() member. /// /// A byte array containing the Der encoded Certificate component. public virtual byte[] GetTbsCertificate() { return c.TbsCertificate.GetDerEncoded(); } /// /// The signature. /// /// A byte array containg the signature of the certificate. public virtual byte[] GetSignature() { return c.Signature.GetBytes(); } /// /// A meaningful version of the Signature Algorithm. (EG SHA1WITHRSA) /// /// A sting representing the signature algorithm. public virtual string SigAlgName { get { return SignerUtilities.GetEncodingName(c.SignatureAlgorithm.ObjectID); } } /// /// Get the Signature Algorithms Object ID. /// /// A string containg a '.' separated object id. public virtual string SigAlgOid { get { return c.SignatureAlgorithm.ObjectID.Id; } } /// /// Get the signature algorithms parameters. (EG DSA Parameters) /// /// A byte array containing the Der encoded version of the parameters or null if there are none. public virtual byte[] GetSigAlgParams() { if (c.SignatureAlgorithm.Parameters != null) { return c.SignatureAlgorithm.Parameters.GetDerEncoded(); } return null; } /// /// Get the issuers UID. /// /// A DerBitString. public virtual DerBitString IssuerUniqueID { get { return c.TbsCertificate.IssuerUniqueID; } } /// /// Get the subjects UID. /// /// A DerBitString. public virtual DerBitString SubjectUniqueID { get { return c.TbsCertificate.SubjectUniqueID; } } /// /// Get a key usage guidlines. /// public virtual bool[] GetKeyUsage() { return keyUsage == null ? null : (bool[]) keyUsage.Clone(); } // 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; try { Asn1Sequence seq = Asn1Sequence.GetInstance( X509ExtensionUtilities.FromExtensionValue(str)); IList list = Platform.CreateArrayList(); foreach (DerObjectIdentifier oid in seq) { list.Add(oid.Id); } 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; } return basicConstraints.PathLenConstraint.IntValue; } return -1; } public virtual ICollection GetSubjectAlternativeNames() { return GetAlternativeNames("2.5.29.17"); } public virtual ICollection GetIssuerAlternativeNames() { return GetAlternativeNames("2.5.29.18"); } protected virtual ICollection GetAlternativeNames( string oid) { Asn1OctetString altNames = GetExtensionValue(new DerObjectIdentifier(oid)); if (altNames == null) return null; Asn1Object asn1Object = X509ExtensionUtilities.FromExtensionValue(altNames); GeneralNames gns = GeneralNames.GetInstance(asn1Object); IList result = Platform.CreateArrayList(); 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; } /// /// Get the public key of the subject of the certificate. /// /// The public key parameters. public virtual AsymmetricKeyParameter GetPublicKey() { return PublicKeyFactory.CreateKey(c.SubjectPublicKeyInfo); } /// /// Return a Der encoded version of this certificate. /// /// A byte array. public virtual byte[] GetEncoded() { return c.GetDerEncoded(); } public override bool Equals( object obj) { if (obj == this) return true; X509Certificate other = obj as X509Certificate; if (other == null) return false; return c.Equals(other.c); // NB: May prefer this implementation of Equals if more than one certificate implementation in play // return Arrays.AreEqual(this.GetEncoded(), other.GetEncoded()); } public override int GetHashCode() { lock (this) { if (!hashValueSet) { hashValue = c.GetHashCode(); 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) { byte[] octs = ext.Value.GetOctets(); Asn1Object obj = Asn1Object.FromByteArray(octs); 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(); } /// /// Verify the certificate's signature using the nominated public key. /// /// An appropriate public key parameter object, RsaPublicKeyParameters, DsaPublicKeyParameters or ECDsaPublicKeyParameters /// True if the signature is valid. /// If key submitted is not of the above nominated types. public virtual void Verify( AsymmetricKeyParameter key) { string sigName = X509SignatureUtilities.GetSignatureName(c.SignatureAlgorithm); ISigner signature = SignerUtilities.GetSigner(sigName); CheckSignature(key, signature); } protected virtual void CheckSignature( AsymmetricKeyParameter publicKey, ISigner signature) { 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; X509SignatureUtilities.SetSignatureParameters(signature, parameters); signature.Init(false, publicKey); byte[] b = this.GetTbsCertificate(); signature.BlockUpdate(b, 0, b.Length); byte[] sig = this.GetSignature(); if (!signature.VerifySignature(sig)) { throw new InvalidKeyException("Public key presented not for certificate signature"); } } private static bool IsAlgIDEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) { if (!id1.ObjectID.Equals(id2.ObjectID)) return false; Asn1Encodable p1 = id1.Parameters; Asn1Encodable p2 = id2.Parameters; 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; } } }