]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Add.
authorSimon Josefsson <simon@josefsson.org>
Thu, 8 Feb 2007 16:42:49 +0000 (16:42 +0000)
committerSimon Josefsson <simon@josefsson.org>
Thu, 8 Feb 2007 16:42:49 +0000 (16:42 +0000)
NEWS
lib/x509/output.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 84f2a4599e5a898e38025b8b870671e715a2ce56..5befff9eb2bd27b0b8f547caa211793a4eb35471 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -35,9 +35,13 @@ The function is gnutls_x509_crt_get_signature.
 If you don't specify a proxy policy in batch mode, it will use
 id-ppl-inheritALL.
 
+** Add API to print information about X.509 certificates.
+The function is gnutls_x509_crt_print.
+
 ** Doc fixes.
 
 ** API and ABI modifications:
+gnutls_x509_crt_print: ADD
 gnutls_x509_crt_get_signature: ADD.
 gnutls_x509_crt_get_extension_data: ADD.
 gnutls_x509_crt_get_extension_info: ADD.
diff --git a/lib/x509/output.c b/lib/x509/output.c
new file mode 100644 (file)
index 0000000..d7f82bd
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+/* Functions for printing X.509 Certificate structures
+ */
+
+#include <gnutls_int.h>
+#include <common.h>
+#include <gnutls_x509.h>
+#include <x509.h>
+#include <gnutls_errors.h>
+
+#define addf _gnutls_string_append_printf
+#define adds _gnutls_string_append_str
+
+static void
+hexdump (gnutls_string * str, const char *data, size_t len, const char *spc)
+{
+  size_t j;
+
+  if (spc)
+    adds (str, spc);
+  for (j = 0; j < len; j++)
+    {
+      if (((j + 1) % 16) == 0)
+       {
+         addf (str, "%.2x\n", (unsigned char) data[j]);
+         if (spc && j != (len - 1))
+           adds (str, spc);
+       }
+      else if (j == (len - 1))
+       addf (str, "%.2x", (unsigned char) data[j]);
+      else
+       addf (str, "%.2x:", (unsigned char) data[j]);
+    }
+  if ((j % 16) != 0)
+    adds (str, "\n");
+}
+
+static void
+hexprint (gnutls_string * str, const char *data, size_t len)
+{
+  size_t j;
+
+  for (j = 0; j < len; j++)
+    addf (str, "%.2x", (unsigned char) data[j]);
+}
+
+
+static void
+asciiprint (gnutls_string * str, const char *data, size_t len)
+{
+  size_t j;
+
+  for (j = 0; j < len; j++)
+    if (isprint (data[j]))
+      addf (str, "%c", (unsigned char) data[j]);
+    else
+      addf (str, ".");
+}
+
+static void
+print_proxy (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  int pathlen;
+  char *policyLanguage;
+  char *policy;
+  size_t npolicy;
+  int err;
+
+  err = gnutls_x509_crt_get_proxy (cert, NULL,
+                                  &pathlen, &policyLanguage,
+                                  &policy, &npolicy);
+  if (err < 0)
+    {
+      addf (str, "error: get proxy: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  if (pathlen >= 0)
+    addf (str, "\t\t\tPath Length Constraint: %d\n", pathlen);
+  adds (str, "\t\t\tPolicy Language: ");
+  if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.1") == 0)
+    addf (str, "id-ppl-inheritALL\n");
+  else if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.2") == 0)
+    addf (str, "id-ppl-independent\n");
+  else
+    addf (str, "%s\n", policyLanguage);
+  if (npolicy)
+    addf (str, "\t\t\tPolicy Language: %.*s\n", npolicy, policy);
+}
+
+static void
+print_ski (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  char *buffer = NULL;
+  size_t size = 0;
+  int err;
+
+  err = gnutls_x509_crt_get_subject_key_id (cert, buffer, &size, NULL);
+  if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      addf (str, "error: get_subject_key_id: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  buffer = gnutls_malloc (size);
+  if (!buffer)
+    {
+      addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  err = gnutls_x509_crt_get_subject_key_id (cert, buffer, &size, NULL);
+  if (err < 0)
+    {
+      gnutls_free (buffer);
+      addf (str, "error: get_subject_key_id: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  adds (str, "\t\t\t");
+  hexprint (str, buffer, size);
+  adds (str, "\n");
+
+  gnutls_free (buffer);
+}
+
+static void
+print_aki (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  char *buffer = NULL;
+  size_t size = 0;
+  int err;
+
+  err = gnutls_x509_crt_get_authority_key_id (cert, buffer, &size, NULL);
+  if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      addf (str, "error: get_authority_key_id: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  buffer = gnutls_malloc (size);
+  if (!buffer)
+    {
+      addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  err = gnutls_x509_crt_get_authority_key_id (cert, buffer, &size, NULL);
+  if (err < 0)
+    {
+      gnutls_free (buffer);
+      addf (str, "error: get_authority_key_id: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  adds (str, "\t\t\t");
+  hexprint (str, buffer, size);
+  adds (str, "\n");
+
+  gnutls_free (buffer);
+}
+
+static void
+print_key_usage (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  unsigned int key_usage;
+  int err;
+
+  err = gnutls_x509_crt_get_key_usage (cert, &key_usage, NULL);
+  if (err < 0)
+    {
+      addf (str, "error: get_key_usage: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  if (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE)
+    addf (str, "\t\t\tDigital signature.\n");
+  if (key_usage & GNUTLS_KEY_NON_REPUDIATION)
+    addf (str, "\t\t\tNon repudiation.\n");
+  if (key_usage & GNUTLS_KEY_KEY_ENCIPHERMENT)
+    addf (str, "\t\t\tKey encipherment.\n");
+  if (key_usage & GNUTLS_KEY_DATA_ENCIPHERMENT)
+    addf (str, "\t\t\tData encipherment.\n");
+  if (key_usage & GNUTLS_KEY_KEY_AGREEMENT)
+    addf (str, "\t\t\tKey agreement.\n");
+  if (key_usage & GNUTLS_KEY_KEY_CERT_SIGN)
+    addf (str, "\t\t\tCertificate signing.\n");
+  if (key_usage & GNUTLS_KEY_CRL_SIGN)
+    addf (str, "\t\t\tCRL signing.\n");
+  if (key_usage & GNUTLS_KEY_ENCIPHER_ONLY)
+    addf (str, "\t\t\tKey encipher only.\n");
+  if (key_usage & GNUTLS_KEY_DECIPHER_ONLY)
+    addf (str, "\t\t\tKey decipher only.\n");
+}
+
+static void
+print_crldist (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  char *buffer;
+  size_t size;
+  int err;
+  int indx;
+
+  for (indx = 0;; indx++)
+    {
+      size = 0;
+      err = gnutls_x509_crt_get_crl_dist_points (cert, indx, buffer, &size,
+                                                NULL, NULL);
+      if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       return;
+      if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+       {
+         addf (str, "error: get_crl_dist_points: %s\n",
+               gnutls_strerror (err));
+         return;
+       }
+
+      buffer = gnutls_malloc (size);
+      if (!buffer)
+       {
+         addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+         return;
+       }
+
+      err = gnutls_x509_crt_get_crl_dist_points (cert, indx, buffer, &size,
+                                                NULL, NULL);
+      if (err < 0)
+       {
+         gnutls_free (buffer);
+         addf (str, "error: get_crl_dist_points2: %s\n",
+               gnutls_strerror (err));
+         return;
+       }
+
+      switch (err)
+       {
+       case GNUTLS_SAN_DNSNAME:
+         addf (str, "\t\t\tDNSname: %s\n", buffer);
+         break;
+
+       case GNUTLS_SAN_RFC822NAME:
+         addf (str, "\t\t\tRFC822name: %s\n", buffer);
+         break;
+
+       case GNUTLS_SAN_URI:
+         addf (str, "\t\t\tURI: %s\n", buffer);
+         break;
+
+       case GNUTLS_SAN_IPADDRESS:
+         addf (str, "\t\t\tIPAddress: %s\n", buffer);
+         break;
+
+       default:
+         addf (str, "error: unknown SAN\n");
+         break;
+       }
+      gnutls_free (buffer);
+    }
+}
+
+static void
+print_key_purpose (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  int indx;
+  char *buffer;
+  size_t size;
+  int err;
+
+  for (indx = 0;; indx++)
+    {
+      size = 0;
+      err = gnutls_x509_crt_get_key_purpose_oid (cert, indx, buffer,
+                                                &size, NULL);
+      if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       return;
+      if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+       {
+         addf (str, "error: get_key_purpose: %s\n", gnutls_strerror (err));
+         return;
+       }
+
+      buffer = gnutls_malloc (size);
+      if (!buffer)
+       {
+         addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+         return;
+       }
+
+      err = gnutls_x509_crt_get_key_purpose_oid (cert, indx, buffer,
+                                                &size, NULL);
+      if (err < 0)
+       {
+         gnutls_free (buffer);
+         addf (str, "error: get_key_purpose2: %s\n", gnutls_strerror (err));
+         return;
+       }
+
+      if (strcmp (buffer, GNUTLS_KP_TLS_WWW_SERVER) == 0)
+       addf (str, "\t\t\tTLS WWW Server.\n");
+      else if (strcmp (buffer, GNUTLS_KP_TLS_WWW_CLIENT) == 0)
+       addf (str, "\t\t\tTLS WWW Client.\n");
+      else if (strcmp (buffer, GNUTLS_KP_CODE_SIGNING) == 0)
+       addf (str, "\t\t\tCode signing.\n");
+      else if (strcmp (buffer, GNUTLS_KP_EMAIL_PROTECTION) == 0)
+       addf (str, "\t\t\tEmail protection.\n");
+      else if (strcmp (buffer, GNUTLS_KP_TIME_STAMPING) == 0)
+       addf (str, "\t\t\tTime stamping.\n");
+      else if (strcmp (buffer, GNUTLS_KP_OCSP_SIGNING) == 0)
+       addf (str, "\t\t\tOCSP signing.\n");
+      else if (strcmp (buffer, GNUTLS_KP_ANY) == 0)
+       addf (str, "\t\t\tAny purpose.\n");
+      else
+       addf (str, "\t\t\t%s\n", buffer);
+
+      gnutls_free (buffer);
+    }
+}
+
+static void
+print_basic (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  int pathlen;
+  int err;
+
+  err = gnutls_x509_crt_get_basic_constraints (cert, NULL, NULL, &pathlen);
+  if (err < 0)
+    {
+      addf (str, "error: get basic constraints: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  if (err == 0)
+    addf (str, "\t\t\tCertificate Authority (CA): FALSE\n");
+  else
+    addf (str, "\t\t\tCertificate Authority (CA): TRUE\n");
+
+  if (pathlen >= 0)
+    addf (str, "\t\t\tPath Length Constraint: %d\n", pathlen);
+}
+
+static void
+print_san (gnutls_string * str, gnutls_x509_crt_t cert, int san_idx)
+{
+  char *buffer = NULL;
+  size_t size = 0;
+  int err;
+
+  err = gnutls_x509_crt_get_subject_alt_name (cert, san_idx, buffer, &size,
+                                             NULL);
+  if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      addf (str, "error: get subject alt name: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  buffer = gnutls_malloc (size);
+  if (!buffer)
+    {
+      addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  err = gnutls_x509_crt_get_subject_alt_name (cert, san_idx,
+                                             buffer, &size, NULL);
+  if (err < 0)
+    {
+      gnutls_free (buffer);
+      addf (str, "error: get subject alt name: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  switch (err)
+    {
+    case GNUTLS_SAN_DNSNAME:
+      addf (str, "\t\t\tDNSname: %s\n", buffer);
+      break;
+
+    case GNUTLS_SAN_RFC822NAME:
+      addf (str, "\t\t\tRFC822name: %s\n", buffer);
+      break;
+
+    case GNUTLS_SAN_URI:
+      addf (str, "\t\t\tURI: %s\n", buffer);
+      break;
+
+    case GNUTLS_SAN_IPADDRESS:
+      addf (str, "\t\t\tIPAddress: %s\n", buffer);
+      break;
+
+    case GNUTLS_SAN_OTHERNAME:
+      addf (str, "\t\t\totherName:\n\t\t\tDER: ");
+      hexprint (str, buffer, size);
+      addf (str, "\n\t\t\tASCII: ");
+      asciiprint (str, buffer, size);
+      addf (str, "\n");
+
+      gnutls_free (buffer);
+
+      size = 0;
+      err = gnutls_x509_crt_get_subject_alt_othername_oid
+       (cert, san_idx, buffer, &size);
+      if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+       {
+         addf (str, "error: decoding otherName OID: %s\n",
+               gnutls_strerror (err));
+         return;
+       }
+
+      buffer = gnutls_malloc (size);
+      if (!buffer)
+       {
+         addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+         return;
+       }
+
+      err = gnutls_x509_crt_get_subject_alt_othername_oid
+       (cert, san_idx, buffer, &size);
+      if (err < 0)
+       {
+         gnutls_free (buffer);
+         addf (str, "error: decoding otherName OID: %s\n",
+               gnutls_strerror (err));
+         return;
+       }
+
+      addf (str, "\t\t\t\tOID: %.*s", size, buffer);
+      if (err == GNUTLS_SAN_OTHERNAME_XMPP)
+       addf (str, " (id-on-xmppAddr)");
+      addf (str, "\n");
+      break;
+
+    default:
+      addf (str, "error: unknown SAN\n");
+      break;
+    }
+
+  gnutls_free (buffer);
+}
+
+static void
+print_cert (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  /* Version. */
+  {
+    int version = gnutls_x509_crt_get_version (cert);
+    if (version < 0)
+      addf (str, "error: version: %s\n", gnutls_strerror (version));
+    else
+      addf (str, "\tVersion: %d\n", version);
+  }
+
+  /* Serial. */
+  {
+    char serial[128];
+    size_t serial_size = sizeof (serial);
+    int err;
+
+    err = gnutls_x509_crt_get_serial (cert, serial, &serial_size);
+    if (err < 0)
+      addf (str, "error: serial: %s\n", gnutls_strerror (err));
+    else if (serial_size == 0)
+      addf (str, "\tSerial Number (hex): 00\n");
+    else
+      {
+       size_t i;
+       addf (str, "\tSerial Number (hex): %02x", (unsigned char) serial[0]);
+       for (i = 1; i < serial_size; i++)
+         addf (str, ":%02x", (unsigned char) serial[i]);
+       addf (str, "\n");
+      }
+  }
+
+  /* Issuer. */
+  {
+    char dn[1024];
+    size_t dn_size = sizeof (dn);
+    int err;
+
+    err = gnutls_x509_crt_get_issuer_dn (cert, dn, &dn_size);
+    if (err < 0)
+      addf (str, "error: issuer: %s\n", gnutls_strerror (err));
+    else
+      addf (str, "\tIssuer: %s\n", dn);
+  }
+
+  /* Validity. */
+  {
+    time_t tim;
+
+    addf (str, "\tValidity:\n");
+
+    tim = gnutls_x509_crt_get_activation_time (cert);
+    {
+      char s[42];
+      size_t max = sizeof (s);
+      struct tm t;
+
+      if (gmtime_r (&tim, &t) == NULL)
+       addf (str, "error: gmtime_r failed (%d)\n", t);
+      if (strftime (s, max, "%a %b %e %H:%M:%S %Z %Y", &t) == 0)
+       strcpy (s, "strftime failed");
+      addf (str, "\t\tNot Before: %s\n", s);
+    }
+
+    tim = gnutls_x509_crt_get_expiration_time (cert);
+    {
+      char s[42];
+      size_t max = sizeof (s);
+      struct tm t;
+
+      if (gmtime_r (&tim, &t) == NULL)
+       addf (str, "error: gmtime_r failed (%d)\n", t);
+      if (strftime (s, max, "%a %b %e %H:%M:%S %Z %Y", &t) == 0)
+       strcpy (s, "strftime failed");
+      addf (str, "\t\tNot After: %s\n", s);
+    }
+  }
+
+  /* Subject. */
+  {
+    char dn[1024];
+    size_t dn_size = sizeof (dn);
+    int err;
+
+    err = gnutls_x509_crt_get_dn (cert, dn, &dn_size);
+    if (err < 0)
+      addf (str, "error: subject: %s\n", gnutls_strerror (err));
+    else
+      addf (str, "\tSubject: %s\n", dn);
+  }
+
+  /* SubjectPublicKeyInfo. */
+  {
+    int err;
+    unsigned int bits;
+
+    err = gnutls_x509_crt_get_pk_algorithm (cert, &bits);
+    if (err < 0)
+      addf (str, "error: public key info: %s\n", gnutls_strerror (err));
+    else
+      {
+       const char *name = gnutls_pk_algorithm_get_name (err);
+       if (name == NULL)
+         name = "Unknown";
+
+       addf (str, "\tSubject Public Key Algorithm: %s\n", name);
+       switch (err)
+         {
+         case GNUTLS_PK_RSA:
+           {
+             gnutls_datum m, e;
+
+             err = gnutls_x509_crt_get_pk_rsa_raw (cert, &m, &e);
+             if (err < 0)
+               addf (str,
+                     "error: RSA public key: %s\n", gnutls_strerror (err));
+             else
+               {
+                 addf (str, "\t\tModulus (bits %d):\n", bits);
+                 hexdump (str, m.data, m.size, "\t\t\t");
+                 addf (str, "\t\tExponent:\n");
+                 hexdump (str, e.data, e.size, "\t\t\t");
+               }
+           }
+           break;
+
+         case GNUTLS_PK_DSA:
+           {
+             gnutls_datum p, q, g, y;
+
+             err = gnutls_x509_crt_get_pk_dsa_raw (cert, &p, &q, &g, &y);
+             if (err < 0)
+               addf (str,
+                     "error: DSA public key: %s\n", gnutls_strerror (err));
+             else
+               {
+                 addf (str, "\t\tPublic key (bits %d):\n", bits);
+                 hexdump (str, y.data, y.size, "\t\t\t");
+                 addf (str, "\t\tP:\n");
+                 hexdump (str, p.data, p.size, "\t\t\t");
+                 addf (str, "\t\tQ:\n");
+                 hexdump (str, q.data, q.size, "\t\t\t");
+                 addf (str, "\t\tG:\n");
+                 hexdump (str, g.data, g.size, "\t\t\t");
+               }
+           }
+           break;
+
+         default:
+           addf (str, "error: unknown algorithm: %d\n", err);
+           break;
+         }
+      }
+  }
+
+  /* Extensions. */
+  if (gnutls_x509_crt_get_version (cert) >= 3)
+    {
+      size_t i;
+      int err = 0;
+
+      for (i = 0;; i++)
+       {
+         char oid[128] = "";
+         size_t sizeof_oid = sizeof (oid);
+         int critical;
+         size_t san_idx = 0;
+         size_t proxy_idx = 0;
+         size_t basic_idx = 0;
+         size_t keyusage_idx = 0;
+         size_t keypurpose_idx = 0;
+         size_t ski_idx = 0;
+         size_t aki_idx = 0;
+         size_t crldist_idx = 0;
+
+         err = gnutls_x509_crt_get_extension_info (cert, i,
+                                                   oid, &sizeof_oid,
+                                                   &critical);
+         if (err < 0)
+           {
+             if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+               break;
+             addf (str, "error: get extension: %s\n", gnutls_strerror (err));
+             continue;
+           }
+
+         if (i == 0)
+           addf (str, "\tExtensions:\n");
+
+         if (strcmp (oid, "2.5.29.19") == 0)
+           {
+             if (basic_idx)
+               {
+                 addf (str, "error: more than one basic constraint\n");
+                 continue;
+               }
+
+             addf (str, "\t\tBasic Constraints (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_basic (str, cert);
+
+             basic_idx++;
+           }
+         else if (strcmp (oid, "2.5.29.14") == 0)
+           {
+             if (ski_idx)
+               {
+                 addf (str, "error: more than one SKI extension\n");
+                 continue;
+               }
+
+             addf (str, "\t\tSubject Key Identifier (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_ski (str, cert);
+
+             ski_idx++;
+           }
+         else if (strcmp (oid, "2.5.29.35") == 0)
+           {
+             if (aki_idx)
+               {
+                 addf (str, "error: more than one AKI extension\n");
+                 continue;
+               }
+
+             addf (str, "\t\tAuthority Key Identifier (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_aki (str, cert);
+
+             aki_idx++;
+           }
+         else if (strcmp (oid, "2.5.29.15") == 0)
+           {
+             if (keyusage_idx)
+               {
+                 addf (str, "error: more than one key usage extension\n");
+                 continue;
+               }
+
+             addf (str, "\t\tKey Usage (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_key_usage (str, cert);
+
+             keyusage_idx++;
+           }
+         else if (strcmp (oid, "2.5.29.37") == 0)
+           {
+             if (keypurpose_idx)
+               {
+                 addf (str, "error: more than one key purpose extension\n");
+                 continue;
+               }
+
+             addf (str, "\t\tKey Purpose (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_key_purpose (str, cert);
+
+             keypurpose_idx++;
+           }
+         else if (strcmp (oid, "2.5.29.17") == 0)
+           {
+             addf (str, "\t\tSubject Alternative Name (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_san (str, cert, san_idx);
+
+             san_idx++;
+           }
+         else if (strcmp (oid, "2.5.29.31") == 0)
+           {
+             if (crldist_idx)
+               {
+                 addf (str, "error: more than one CRL distribution point\n");
+                 continue;
+               }
+
+             addf (str, "\t\tCRL Distribution points (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_crldist (str, cert);
+
+             crldist_idx++;
+           }
+         else if (strcmp (oid, "1.3.6.1.5.5.7.1.14") == 0)
+           {
+             if (proxy_idx)
+               {
+                 addf (str, "error: more than one proxy extension\n");
+                 continue;
+               }
+
+             addf (str, "\t\tProxy Certificate Information (%scritical):\n",
+                   critical ? "" : "not ");
+
+             print_proxy (str, cert);
+
+             proxy_idx++;
+           }
+         else
+           {
+             char *buffer;
+             size_t extlen = 0;
+             size_t j;
+
+             addf (str, "\t\tUnknown extension %s (%scritical):\n", oid,
+                   critical ? "" : "not ");
+
+             err = gnutls_x509_crt_get_extension_data (cert, i,
+                                                       NULL, &extlen);
+             if (err < 0)
+               {
+                 addf (str, "error: get extension oid: %s\n",
+                       gnutls_strerror (err));
+                 continue;
+               }
+
+             buffer = gnutls_malloc (extlen);
+             if (!buffer)
+               {
+                 addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+                 continue;
+               }
+
+             err = gnutls_x509_crt_get_extension_data (cert, i,
+                                                       buffer, &extlen);
+             if (err < 0)
+               {
+                 gnutls_free (buffer);
+                 addf (str, "error: get extension oid: %s\n",
+                       gnutls_strerror (err));
+                 continue;
+               }
+
+             addf (str, "\t\t\tASCII: ");
+             for (j = 0; j < extlen; j++)
+               {
+                 if (isprint (buffer[j]))
+                   addf (str, "%c", (unsigned char) buffer[j]);
+                 else
+                   addf (str, ".");
+               }
+             addf (str, "\n");
+
+             addf (str, "\t\t\tHexdump:\n");
+             hexdump (str, buffer, extlen, "\t\t");
+
+             gnutls_free (buffer);
+           }
+       }
+    }
+
+  /* Signature. */
+  {
+    int err;
+    size_t size = 0;
+    char *buffer;
+
+    err = gnutls_x509_crt_get_signature_algorithm (cert);
+    if (err < 0)
+      addf (str, "error: signature: %s\n", gnutls_strerror (err));
+    else
+      {
+       const char *name = gnutls_sign_algorithm_get_name (err);
+       if (name == NULL)
+         name = "Unknown";
+       addf (str, "\tSignature Algorithm: %s\n", name);
+      }
+    if (err == GNUTLS_SIGN_RSA_MD5 || err == GNUTLS_SIGN_RSA_MD2)
+      {
+       addf (str, "warning: Certificate signed using a "
+             "broken signature algorithm that can be forged.");
+      }
+
+    err = gnutls_x509_crt_get_signature (cert, buffer, &size);
+    if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+      {
+       addf (str, "error: get_signature: %s\n", gnutls_strerror (err));
+       return;
+      }
+
+    buffer = gnutls_malloc (size);
+    if (!buffer)
+      {
+       addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+       return;
+      }
+
+    err = gnutls_x509_crt_get_signature (cert, buffer, &size);
+    if (err < 0)
+      {
+       gnutls_free (buffer);
+       addf (str, "error: get_signature2: %s\n", gnutls_strerror (err));
+       return;
+      }
+
+    addf (str, "\tSignature:\n");
+    hexdump (str, buffer, size, "\t\t");
+  }
+}
+
+static void
+print_fingerprint (gnutls_string * str, gnutls_x509_crt_t cert,
+                  gnutls_digest_algorithm_t algo)
+{
+  int err;
+  size_t size = 0;
+  char *buffer;
+
+  err = gnutls_x509_crt_get_fingerprint (cert, algo, buffer, &size);
+  if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      addf (str, "error: get_fingerprint: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  buffer = gnutls_malloc (size);
+  if (!buffer)
+    {
+      addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  err = gnutls_x509_crt_get_fingerprint (cert, algo, buffer, &size);
+  if (err < 0)
+    {
+      gnutls_free (buffer);
+      addf (str, "error: get_fingerprint: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  addf (str, "\t%s fingerprint:\n\t\t",
+       ((algo == GNUTLS_DIG_MD5) ? "MD5" :
+        ((algo == GNUTLS_DIG_SHA1) ? "SHA-1" :
+         "Unknown")));
+  hexprint (str, buffer, size);
+  adds (str, "\n");
+
+  gnutls_free (buffer);
+}
+
+static void
+print_keyid (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  int err;
+  size_t size = 0;
+  char *buffer;
+
+  err = gnutls_x509_crt_get_key_id (cert, 0, buffer, &size);
+  if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      addf (str, "error: get_key_id: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  buffer = gnutls_malloc (size);
+  if (!buffer)
+    {
+      addf (str, "error: malloc: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  err = gnutls_x509_crt_get_key_id (cert, 0, buffer, &size);
+  if (err < 0)
+    {
+      gnutls_free (buffer);
+      addf (str, "error: get_key_id: %s\n", gnutls_strerror (err));
+      return;
+    }
+
+  addf (str, "\tPublic Key Id:\n\t\t");
+  hexprint (str, buffer, size);
+  adds (str, "\n");
+
+  gnutls_free (buffer);
+}
+
+static void
+print_other (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+  print_fingerprint (str, cert, GNUTLS_DIG_MD5);
+  print_fingerprint (str, cert, GNUTLS_DIG_SHA1);
+  print_keyid (str, cert);
+}
+
+static void
+print_oneline (gnutls_string * str, gnutls_x509_crt_t cert)
+{
+
+  /* Subject. */
+  {
+    char dn[1024];
+    size_t dn_size = sizeof (dn);
+    int err;
+
+    err = gnutls_x509_crt_get_dn (cert, dn, &dn_size);
+    if (err < 0)
+      addf (str, "unknown subject (%s), ", gnutls_strerror (err));
+    else
+      addf (str, "subject `%s', ", dn);
+  }
+
+  /* Issuer. */
+  {
+    char dn[1024];
+    size_t dn_size = sizeof (dn);
+    int err;
+
+    err = gnutls_x509_crt_get_issuer_dn (cert, dn, &dn_size);
+    if (err < 0)
+      addf (str, "unknown issuer (%s), ", gnutls_strerror (err));
+    else
+      addf (str, "issuer `%s', ", dn);
+  }
+
+  {
+    int bits;
+    const char *name = gnutls_pk_algorithm_get_name
+      (gnutls_x509_crt_get_pk_algorithm (cert, &bits));
+    if (name == NULL)
+      name = "Unknown";
+    addf (str, "%s key %d bits, ", name, bits);
+  }
+
+  /* Validity. */
+  {
+    time_t tim;
+
+    tim = gnutls_x509_crt_get_activation_time (cert);
+    {
+      char s[42];
+      size_t max = sizeof (s);
+      struct tm t;
+
+      if (gmtime_r (&tim, &t) == NULL)
+       addf (str, "unknown activation (%d), ", t);
+      else if (strftime (s, max, "%Y-%m-%d %H:%M:%S UTC", &t) == 0)
+       addf (str, "failed activation (%d), ", t);
+      else
+       addf (str, "activated `%s', ", s);
+    }
+
+    tim = gnutls_x509_crt_get_expiration_time (cert);
+    {
+      char s[42];
+      size_t max = sizeof (s);
+      struct tm t;
+
+      if (gmtime_r (&tim, &t) == NULL)
+       addf (str, "unknown expiry (%d), ", t);
+      else if (strftime (s, max, "%Y-%m-%d %H:%M:%S UTC", &t) == 0)
+       addf (str, "failed expiry (%d), ", t);
+      else
+       addf (str, "expires `%s', ", s);
+    }
+  }
+
+  {
+    int pathlen;
+    char *policyLanguage;
+    int err;
+
+    err = gnutls_x509_crt_get_proxy (cert, NULL,
+                                    &pathlen, &policyLanguage,
+                                    NULL, NULL);
+    if (err == 0)
+      {
+       addf (str, "proxy certificate (policy=");
+       if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.1") == 0)
+         addf (str, "id-ppl-inheritALL");
+       else if (strcmp (policyLanguage, "1.3.6.1.5.5.7.21.2") == 0)
+         addf (str, "id-ppl-independent");
+       else
+         addf (str, "%s", policyLanguage);
+       if (pathlen >= 0)
+         addf (str, ", pathlen=%d), ", pathlen);
+       else
+         addf (str, "), ");
+       gnutls_free (policyLanguage);
+      }
+  }
+
+  {
+    char buffer[20];
+    size_t size = sizeof (buffer);
+    int err;
+
+    err = gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1,
+                                          buffer, &size);
+    if (err < 0)
+      {
+       addf (str, "unknown fingerprint (%s)", gnutls_strerror (err));
+      }
+    else
+      {
+       addf (str, "SHA-1 fingerprint `");
+       hexprint (str, buffer, size);
+       adds (str, "'");
+      }
+  }
+
+}
+
+/**
+ * gnutls_x509_crt_print - Pretty print X.509 certificates
+ * @cert: The structure to be initialized
+ * @format: Indicate the format to use
+ * @out: Newly allocated zero terminated string with information.
+ *
+ * This function will pretty print a X.509 certificate, suitable for
+ * displaying to a human.
+ *
+ * Returns 0 on success.
+ **/
+int
+gnutls_x509_crt_print (gnutls_x509_crt_t cert,
+                      gnutls_certificate_print_formats_t format,
+                      char **out)
+{
+  gnutls_string str;
+
+  if (format == GNUTLS_X509_CRT_FULL)
+    {
+      _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free);
+
+      _gnutls_string_append_str (&str, "X.509 Certificate Information:\n");
+
+      print_cert (&str, cert);
+
+      _gnutls_string_append_str (&str, "Other Information:\n");
+
+      print_other (&str, cert);
+
+      _gnutls_string_append_data (&str, "\0", 1);
+      *out = str.data;
+    }
+  else if (format == GNUTLS_X509_CRT_ONELINE)
+    {
+      _gnutls_string_init (&str, gnutls_malloc, gnutls_realloc, gnutls_free);
+
+      print_oneline (&str, cert);
+
+      _gnutls_string_append_data (&str, "\0", 1);
+      *out = str.data;
+    }
+  else
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  return 0;
+}