public String toString() {
   StringWriter str = new StringWriter();
   PrintWriter out = new PrintWriter(str);
   out.println(X509CertificateImpl.class.getName() + " {");
   out.println("  TBSCertificate {");
   out.println("    version = " + version + ";");
   out.println("    serialNo = " + serialNo + ";");
   out.println("    signature = {");
   out.println("      algorithm = " + getSigAlgName() + ";");
   out.print("      parameters =");
   if (sigAlgVal != null) {
     out.println();
     out.print(Util.hexDump(sigAlgVal, "        "));
   } else {
     out.println(" null;");
   }
   out.println("    }");
   out.println("    issuer = " + issuer.getName() + ";");
   out.println("    validity = {");
   out.println("      notBefore = " + notBefore + ";");
   out.println("      notAfter  = " + notAfter + ";");
   out.println("    }");
   out.println("    subject = " + subject.getName() + ";");
   out.println("    subjectPublicKeyInfo = {");
   out.println("      algorithm = " + subjectKey.getAlgorithm());
   out.println("      key =");
   out.print(Util.hexDump(subjectKey.getEncoded(), "        "));
   out.println("    };");
   out.println("    issuerUniqueId  = " + issuerUniqueId + ";");
   out.println("    subjectUniqueId = " + subjectUniqueId + ";");
   out.println("    extensions = {");
   for (Iterator it = extensions.values().iterator(); it.hasNext(); ) {
     out.println("      " + it.next());
   }
   out.println("    }");
   out.println("  }");
   out.println("  signatureAlgorithm = " + getSigAlgName() + ";");
   out.println("  signatureValue =");
   out.print(Util.hexDump(signature, "    "));
   out.println("}");
   return str.toString();
 }
  /**
   * Parse a DER stream into an X.509 certificate.
   *
   * @param encoded The encoded bytes.
   */
  private void parse(InputStream encoded) throws Exception {
    DERReader der = new DERReader(encoded);

    // Certificate ::= SEQUENCE {
    DERValue cert = der.read();
    debug("start Certificate  len == " + cert.getLength());

    this.encoded = cert.getEncoded();
    if (!cert.isConstructed()) {
      throw new IOException("malformed Certificate");
    }

    // TBSCertificate ::= SEQUENCE {
    DERValue tbsCert = der.read();
    if (tbsCert.getValue() != DER.CONSTRUCTED_VALUE) {
      throw new IOException("malformed TBSCertificate");
    }
    tbsCertBytes = tbsCert.getEncoded();
    debug("start TBSCertificate  len == " + tbsCert.getLength());

    // Version ::= INTEGER [0] { v1(0), v2(1), v3(2) }
    DERValue val = der.read();
    if (val.getTagClass() == DER.CONTEXT && val.getTag() == 0) {
      version = ((BigInteger) der.read().getValue()).intValue() + 1;
      val = der.read();
    } else {
      version = 1;
    }
    debug("read version == " + version);

    // SerialNumber ::= INTEGER
    serialNo = (BigInteger) val.getValue();
    debug("read serial number == " + serialNo);

    // AlgorithmIdentifier ::= SEQUENCE {
    val = der.read();
    if (!val.isConstructed()) {
      throw new IOException("malformed AlgorithmIdentifier");
    }
    int certAlgLen = val.getLength();
    debug("start AlgorithmIdentifier  len == " + certAlgLen);
    val = der.read();

    //   algorithm    OBJECT IDENTIFIER,
    algId = (OID) val.getValue();
    debug("read algorithm ID == " + algId);

    //   parameters   ANY DEFINED BY algorithm OPTIONAL }
    if (certAlgLen > val.getEncodedLength()) {
      val = der.read();
      if (val == null) {
        algVal = null;
      } else {
        algVal = val.getEncoded();
      }
      if (val.isConstructed()) {
        encoded.skip(val.getLength());
      }
      debug("read algorithm parameters == " + algVal);
    }

    // issuer   Name,
    val = der.read();
    issuer = new X500Name(val.getEncoded());
    der.skip(val.getLength());
    debug("read issuer == " + issuer);

    // Validity ::= SEQUENCE {
    //   notBefore   Time,
    //   notAfter    Time }
    if (!der.read().isConstructed()) {
      throw new IOException("malformed Validity");
    }
    notBefore = (Date) der.read().getValue();
    notAfter = (Date) der.read().getValue();
    debug("read notBefore == " + notBefore);
    debug("read notAfter == " + notAfter);

    // subject   Name,
    val = der.read();
    subject = new X500Name(val.getEncoded());
    der.skip(val.getLength());
    debug("read subject == " + subject);

    // SubjectPublicKeyInfo ::= SEQUENCE {
    //   algorithm         AlgorithmIdentifier,
    //   subjectPublicKey  BIT STRING }
    DERValue spki = der.read();
    if (!spki.isConstructed()) {
      throw new IOException("malformed SubjectPublicKeyInfo");
    }
    KeyFactory spkFac = KeyFactory.getInstance("X.509");
    subjectKey = spkFac.generatePublic(new X509EncodedKeySpec(spki.getEncoded()));
    der.skip(spki.getLength());
    debug("read subjectPublicKey == " + subjectKey);

    if (version > 1) {
      val = der.read();
    }
    if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 1) {
      byte[] b = (byte[]) val.getValue();
      issuerUniqueId = new BitString(b, 1, b.length - 1, b[0] & 0xFF);
      debug("read issuerUniqueId == " + issuerUniqueId);
      val = der.read();
    }
    if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 2) {
      byte[] b = (byte[]) val.getValue();
      subjectUniqueId = new BitString(b, 1, b.length - 1, b[0] & 0xFF);
      debug("read subjectUniqueId == " + subjectUniqueId);
      val = der.read();
    }
    if (version >= 3 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 3) {
      val = der.read();
      debug("start Extensions  len == " + val.getLength());
      int len = 0;
      while (len < val.getLength()) {
        DERValue ext = der.read();
        debug("start extension  len == " + ext.getLength());
        Extension e = new Extension(ext.getEncoded());
        extensions.put(e.getOid(), e);
        der.skip(ext.getLength());
        len += ext.getEncodedLength();
        debug("count == " + len);
      }
    }

    val = der.read();
    if (!val.isConstructed()) {
      throw new IOException("malformed AlgorithmIdentifier");
    }
    int sigAlgLen = val.getLength();
    debug("start AlgorithmIdentifier  len == " + sigAlgLen);
    val = der.read();
    sigAlgId = (OID) val.getValue();
    debug("read algorithm id == " + sigAlgId);
    if (sigAlgLen > val.getEncodedLength()) {
      val = der.read();
      if (val.getValue() == null) {
        if (subjectKey instanceof DSAPublicKey) {
          AlgorithmParameters params = AlgorithmParameters.getInstance("DSA");
          DSAParams dsap = ((DSAPublicKey) subjectKey).getParams();
          DSAParameterSpec spec = new DSAParameterSpec(dsap.getP(), dsap.getQ(), dsap.getG());
          params.init(spec);
          sigAlgVal = params.getEncoded();
        }
      } else {
        sigAlgVal = (byte[]) val.getEncoded();
      }
      if (val.isConstructed()) {
        encoded.skip(val.getLength());
      }
      debug("read parameters == " + sigAlgVal);
    }
    signature = ((BitString) der.read().getValue()).toByteArray();
    debug("read signature ==\n" + Util.hexDump(signature, ">>>> "));
  }