]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Add X509 Issuer Altname functions
authorBrad Hards <bradh@frogmouth.net>
Wed, 9 Sep 2009 11:57:06 +0000 (21:57 +1000)
committerSimon Josefsson <simon@josefsson.org>
Thu, 10 Sep 2009 06:21:10 +0000 (08:21 +0200)
Signed-off-by: Simon Josefsson <simon@josefsson.org>
doc/manpages/Makefile.am
lib/includes/gnutls/x509.h
lib/libgnutls.map
lib/x509/output.c
lib/x509/x509.c
tests/Makefile.am
tests/x509_altname.c [new file with mode: 0644]

index 4c0ff227625e49b7064b23fd8d2297f4d0bc288d..b894f7e9e9417d16372dbfd554b2ea89393fed01 100644 (file)
@@ -431,8 +431,11 @@ APIMANS += gnutls_x509_crt_get_subject_key_id.3
 APIMANS += gnutls_x509_crt_get_authority_key_id.3
 APIMANS += gnutls_x509_crt_get_pk_algorithm.3
 APIMANS += gnutls_x509_crt_get_subject_alt_name.3
+APIMANS += gnutls_x509_crt_get_issuer_alt_name.3
 APIMANS += gnutls_x509_crt_get_subject_alt_name2.3
+APIMANS += gnutls_x509_crt_get_issuer_alt_name2.3
 APIMANS += gnutls_x509_crt_get_subject_alt_othername_oid.3
+APIMANS += gnutls_x509_crt_get_issuer_alt_othername_oid.3
 APIMANS += gnutls_x509_crt_get_basic_constraints.3
 APIMANS += gnutls_x509_crt_get_ca_status.3
 APIMANS += gnutls_x509_crt_get_key_usage.3
index 2a87cba7a2d0b035e2e99abb224fac8b4f8a976d..e61ef2568454e2016d803c97615eef1adf1522ca 100644 (file)
@@ -198,6 +198,21 @@ extern "C"
                                                     void *ret,
                                                     size_t * ret_size);
 
+  int gnutls_x509_crt_get_issuer_alt_name (gnutls_x509_crt_t cert,
+                                           unsigned int seq, void *ret,
+                                           size_t * ret_size,
+                                           unsigned int *critical);
+  int gnutls_x509_crt_get_issuer_alt_name2 (gnutls_x509_crt_t cert,
+                                            unsigned int seq, void *ret,
+                                            size_t * ret_size,
+                                            unsigned int *ret_type,
+                                            unsigned int *critical);
+
+  int gnutls_x509_crt_get_issuer_alt_othername_oid (gnutls_x509_crt_t cert,
+                                                    unsigned int seq,
+                                                    void *ret,
+                                                    size_t * ret_size);
+
   int gnutls_x509_crt_get_ca_status (gnutls_x509_crt_t cert,
                                     unsigned int *critical);
   int gnutls_x509_crt_get_basic_constraints (gnutls_x509_crt_t cert,
index 100ac679f0d9c70dfaa7db6907840cd3af18d891..22524648bb018a1becbbf39c977871e88136168a 100644 (file)
@@ -562,6 +562,9 @@ GNUTLS_2_8
     gnutls_x509_crq_set_key_purpose_oid;
     gnutls_x509_crq_set_key_usage;
     gnutls_x509_crq_set_subject_alt_name;
+    gnutls_x509_crt_get_issuer_alt_name2;
+    gnutls_x509_crt_get_issuer_alt_name;
+    gnutls_x509_crt_get_issuer_alt_othername_oid;
     gnutls_x509_crt_get_verify_algorithm;
     gnutls_x509_crt_set_crq_extensions;
     gnutls_x509_crt_verify_hash;
index 854affb0ba5407f5da7498e911af00370b23fae1..10e5fc21fc9eeeb6519373a968bd82daca537afb 100644 (file)
@@ -212,6 +212,10 @@ print_ski (gnutls_string * str, gnutls_x509_crt_t cert)
 #define TYPE_CRT 2
 #define TYPE_CRQ 3
 
+#define TYPE_CRT_SAN TYPE_CRT
+#define TYPE_CRQ_SAN TYPE_CRQ
+#define TYPE_CRT_IAN 4
+
 typedef union
 {
   gnutls_x509_crt_t crt;
@@ -510,27 +514,31 @@ print_basic (gnutls_string * str, const char *prefix, int type,
 
 
 static void
-print_san (gnutls_string * str, const char *prefix, int type,
-          cert_type_t cert)
+print_altname (gnutls_string * str, const char *prefix, int altname_type,
+              cert_type_t cert)
 {
-  unsigned int san_idx;
+  unsigned int altname_idx;
   char str_ip[64];
   char *p;
 
-  for (san_idx = 0;; san_idx++)
+  for (altname_idx = 0;; altname_idx++)
     {
       char *buffer = NULL;
       size_t size = 0;
       int err;
 
-      if (type == TYPE_CRT)
+      if (altname_type == TYPE_CRT_SAN)
        err =
-         gnutls_x509_crt_get_subject_alt_name (cert.crt, san_idx, buffer,
+         gnutls_x509_crt_get_subject_alt_name (cert.crt, altname_idx, buffer,
                                                &size, NULL);
-      else if (type == TYPE_CRQ)
+      else if (altname_type == TYPE_CRQ_SAN)
        err =
-         gnutls_x509_crq_get_subject_alt_name (cert.crq, san_idx, buffer,
+         gnutls_x509_crq_get_subject_alt_name (cert.crq, altname_idx, buffer,
                                                &size, NULL, NULL);
+      else if (altname_type == TYPE_CRT_IAN)
+       err =
+         gnutls_x509_crt_get_issuer_alt_name (cert.crt, altname_idx, buffer,
+                                              &size, NULL);
       else
        return;
 
@@ -538,7 +546,7 @@ print_san (gnutls_string * str, const char *prefix, int type,
        break;
       if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
        {
-         addf (str, "error: get_subject_alt_name: %s\n",
+         addf (str, "error: get_subject/issuer_alt_name: %s\n",
                gnutls_strerror (err));
          return;
        }
@@ -551,19 +559,23 @@ print_san (gnutls_string * str, const char *prefix, int type,
          return;
        }
 
-      if (type == TYPE_CRT)
+      if (altname_type == TYPE_CRT_SAN)
        err =
-         gnutls_x509_crt_get_subject_alt_name (cert.crt, san_idx, buffer,
+         gnutls_x509_crt_get_subject_alt_name (cert.crt, altname_idx, buffer,
                                                &size, NULL);
-      else if (type == TYPE_CRQ)
+      else if (altname_type == TYPE_CRQ_SAN)
        err =
-         gnutls_x509_crq_get_subject_alt_name (cert.crq, san_idx, buffer,
+         gnutls_x509_crq_get_subject_alt_name (cert.crq, altname_idx, buffer,
                                                &size, NULL, NULL);
+      else if (altname_type == TYPE_CRT_IAN)
+       err = 
+         gnutls_x509_crt_get_issuer_alt_name (cert.crt, altname_idx, buffer,
+                                               &size, NULL);
 
       if (err < 0)
        {
          gnutls_free (buffer);
-         addf (str, "error: get_subject_alt_name2: %s\n",
+         addf (str, "error: get_subject/issuer_alt_name2: %s\n",
                gnutls_strerror (err));
          return;
        }
@@ -573,7 +585,7 @@ print_san (gnutls_string * str, const char *prefix, int type,
           || err == GNUTLS_SAN_URI) &&
          strlen (buffer) != size)
        {
-         adds (str, _("warning: SAN contains an embedded NUL, "
+         adds (str, _("warning: altname contains an embedded NUL, "
                       "replacing with '!'\n"));
          while (strlen (buffer) < size)
            buffer[strlen (buffer)] = '!';
@@ -611,17 +623,20 @@ print_san (gnutls_string * str, const char *prefix, int type,
            size_t oidsize;
 
            oidsize = 0;
-           if (type == TYPE_CRT)
+           if (altname_type == TYPE_CRT_SAN)
              err = gnutls_x509_crt_get_subject_alt_othername_oid
-               (cert.crt, san_idx, oid, &oidsize);
-           else if (type == TYPE_CRQ)
+               (cert.crt, altname_idx, oid, &oidsize);
+           else if (altname_type == TYPE_CRQ_SAN)
              err = gnutls_x509_crq_get_subject_alt_othername_oid
-               (cert.crq, san_idx, oid, &oidsize);
+               (cert.crq, altname_idx, oid, &oidsize);
+           else if (altname_type == TYPE_CRT_IAN)
+             err = gnutls_x509_crt_get_issuer_alt_othername_oid
+               (cert.crt, altname_idx, oid, &oidsize);
 
            if (err != GNUTLS_E_SHORT_MEMORY_BUFFER)
              {
                gnutls_free (buffer);
-               addf (str, "error: get_subject_alt_othername_oid: %s\n",
+               addf (str, "error: get_subject/issuer_alt_othername_oid: %s\n",
                      gnutls_strerror (err));
                return;
              }
@@ -635,12 +650,16 @@ print_san (gnutls_string * str, const char *prefix, int type,
                return;
              }
 
-           if (type == TYPE_CRT)
+           if (altname_type == TYPE_CRT_SAN)
              err = gnutls_x509_crt_get_subject_alt_othername_oid
-               (cert.crt, san_idx, oid, &oidsize);
-           else if (type == TYPE_CRQ)
+               (cert.crt, altname_idx, oid, &oidsize);
+           else if (altname_type == TYPE_CRQ_SAN)
              err = gnutls_x509_crq_get_subject_alt_othername_oid
-               (cert.crq, san_idx, oid, &oidsize);
+               (cert.crq, altname_idx, oid, &oidsize);
+           else if (altname_type == TYPE_CRT_IAN)
+             err = gnutls_x509_crt_get_issuer_alt_othername_oid
+               (cert.crt, altname_idx, oid, &oidsize);
+
            if (err < 0)
              {
                gnutls_free (buffer);
@@ -654,7 +673,7 @@ print_san (gnutls_string * str, const char *prefix, int type,
              {
                if (strlen (buffer) != size)
                  {
-                   adds (str, _("warning: SAN contains an embedded NUL, "
+                   adds (str, _("warning: altname contains an embedded NUL, "
                                 "replacing with '!'\n"));
                    while (strlen (buffer) < size)
                      buffer[strlen (buffer)] = '!';
@@ -678,7 +697,7 @@ print_san (gnutls_string * str, const char *prefix, int type,
          break;
 
        default:
-         addf (str, "error: unknown SAN\n");
+         addf (str, "error: unknown altname\n");
          break;
        }
 
@@ -686,12 +705,14 @@ print_san (gnutls_string * str, const char *prefix, int type,
     }
 }
 
+
 static void
 print_extensions (gnutls_string * str, const char *prefix, int type,
                  cert_type_t cert)
 {
   int i, err;
   int san_idx = 0;
+  int ian_idx = 0;
   int proxy_idx = 0;
   int basic_idx = 0;
   int keyusage_idx = 0;
@@ -824,10 +845,25 @@ print_extensions (gnutls_string * str, const char *prefix, int type,
          addf (str, _("%s\t\tSubject Alternative Name (%s):\n"), prefix,
                critical ? _("critical") : _("not critical"));
 
-         print_san (str, prefix, type, cert);
+         print_altname (str, prefix, type, cert);
 
          san_idx++;
        }
+      else if (strcmp (oid, "2.5.29.18") == 0)
+       {
+         if (ian_idx)
+           {
+             addf (str, "error: more than one Issuer AltName extension\n");
+             continue;
+           }
+
+         addf (str, _("%s\t\tIssuer Alternative Name (%s):\n"), prefix,
+               critical ? _("critical") : _("not critical"));
+
+         print_altname (str, prefix, TYPE_CRT_IAN, cert);
+
+         ian_idx++;
+       }
       else if (strcmp (oid, "2.5.29.31") == 0)
        {
          if (crldist_idx)
index a65626b9b358b586900af10b3ebafccaaae867cb..afb0fc96713cc3de8cf7271e917dea289b5df6e3 100644 (file)
@@ -1110,10 +1110,10 @@ _gnutls_parse_general_name (ASN1_TYPE src, const char *src_name,
 }
 
 static int
-get_subject_alt_name (gnutls_x509_crt_t cert,
-                     unsigned int seq, void *ret,
-                     size_t * ret_size, unsigned int *ret_type,
-                     unsigned int *critical, int othername_oid)
+get_alt_name(gnutls_x509_crt_t cert, const char *extension_id,
+             unsigned int seq, void *ret,
+            size_t * ret_size, unsigned int *ret_type,
+            unsigned int *critical, int othername_oid)
 {
   int result;
   gnutls_datum_t dnsname;
@@ -1132,7 +1132,7 @@ get_subject_alt_name (gnutls_x509_crt_t cert,
     *ret_size = 0;
 
   if ((result =
-       _gnutls_x509_crt_get_extension (cert, "2.5.29.17", 0, &dnsname,
+       _gnutls_x509_crt_get_extension (cert, extension_id, 0, &dnsname,
                                       critical)) < 0)
     {
       return result;
@@ -1144,8 +1144,20 @@ get_subject_alt_name (gnutls_x509_crt_t cert,
       return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
     }
 
-  result = asn1_create_element
-    (_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2);
+  if (strcmp("2.5.29.17", extension_id) == 0) 
+    {
+      result = asn1_create_element(_gnutls_get_pkix (), "PKIX1.SubjectAltName", &c2);
+    }
+  else if (strcmp("2.5.29.18", extension_id) == 0)
+    {
+      result = asn1_create_element(_gnutls_get_pkix (), "PKIX1.IssuerAltName", &c2);
+    }
+  else
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INTERNAL_ERROR;
+    }
+
   if (result != ASN1_SUCCESS)
     {
       gnutls_assert ();
@@ -1219,7 +1231,49 @@ gnutls_x509_crt_get_subject_alt_name (gnutls_x509_crt_t cert,
                                      size_t * ret_size,
                                      unsigned int *critical)
 {
-  return get_subject_alt_name (cert, seq, ret, ret_size, NULL, critical, 0);
+  return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, NULL, critical, 0);
+}
+
+/**
+  * gnutls_x509_crt_get_issuer_alt_name - Get certificate's issuer alternative name, if any
+  * @cert: should contain a #gnutls_x509_crt_t structure
+  * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+  * @ret: is the place where the alternative name will be copied to
+  * @ret_size: holds the size of ret.
+  * @critical: will be non zero if the extension is marked as critical (may be null)
+  *
+  * This function will return the issuer alternative names, contained in the
+  * given certificate.
+  *
+  * This is specified in X509v3 Certificate Extensions.  GNUTLS will
+  * return the Isssuer Alternative name (2.5.29.18), or a negative error code.
+  *
+  * When the SAN type is otherName, it will extract the data in the
+  * otherName's value field, and %GNUTLS_SAN_OTHERNAME is returned.
+  * You may use gnutls_x509_crt_get_subject_alt_othername_oid() to get
+  * the corresponding OID and the "virtual" SAN types (e.g.,
+  * %GNUTLS_SAN_OTHERNAME_XMPP).
+  *
+  * If an otherName OID is known, the data will be decoded.  Otherwise
+  * the returned data will be DER encoded, and you will have to decode
+  * it yourself.  Currently, only the RFC 3920 id-on-xmppAddr Issuer AltName
+  * is recognized.
+  *
+  * Returns: the alternative issuer name type on success, one of the
+  *   enumerated #gnutls_x509_subject_alt_name_t.  It will return
+  *   %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough
+  *   to hold the value.  In that case @ret_size will be updated with
+  *   the required size.  If the certificate does not have an
+  *   Alternative name with the specified sequence number then
+  *   %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+  **/
+int
+gnutls_x509_crt_get_issuer_alt_name (gnutls_x509_crt_t cert,
+                                     unsigned int seq, void *ret,
+                                     size_t * ret_size,
+                                     unsigned int *critical)
+{
+  return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, NULL, critical, 0);
 }
 
 /**
@@ -1253,8 +1307,41 @@ gnutls_x509_crt_get_subject_alt_name2 (gnutls_x509_crt_t cert,
                                       unsigned int *ret_type,
                                       unsigned int *critical)
 {
-  return get_subject_alt_name (cert, seq, ret, ret_size, ret_type, critical,
-                              0);
+  return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, ret_type, critical, 0);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_name2 - Get certificate issuer's alternative name, if any
+ * @cert: should contain a #gnutls_x509_crt_t structure
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ret: is the place where the alternative name will be copied to
+ * @ret_size: holds the size of ret.
+ * @ret_type: holds the type of the alternative name (one of gnutls_x509_subject_alt_name_t).
+ * @critical: will be non zero if the extension is marked as critical (may be null)
+ *
+ * This function will return the alternative names, contained in the
+ * given certificate. It is the same as
+ * gnutls_x509_crt_get_issuer_alt_name() except for the fact that it
+ * will return the type of the alternative name in @ret_type even if
+ * the function fails for some reason (i.e.  the buffer provided is
+ * not enough).
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ *   enumerated #gnutls_x509_subject_alt_name_t.  It will return
+ *   %GNUTLS_E_SHORT_MEMORY_BUFFER if @ret_size is not large enough
+ *   to hold the value.  In that case @ret_size will be updated with
+ *   the required size.  If the certificate does not have an
+ *   Alternative name with the specified sequence number then
+ *   %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_name2 (gnutls_x509_crt_t cert,
+                                      unsigned int seq, void *ret,
+                                      size_t * ret_size,
+                                      unsigned int *ret_type,
+                                      unsigned int *critical)
+{
+  return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, ret_type, critical, 0);
 }
 
 /**
@@ -1288,7 +1375,41 @@ gnutls_x509_crt_get_subject_alt_othername_oid (gnutls_x509_crt_t cert,
                                               unsigned int seq,
                                               void *ret, size_t * ret_size)
 {
-  return get_subject_alt_name (cert, seq, ret, ret_size, NULL, NULL, 1);
+  return get_alt_name (cert, "2.5.29.17", seq, ret, ret_size, NULL, NULL, 1);
+}
+
+/**
+ * gnutls_x509_crt_get_issuer_alt_othername_oid - Get Issuer AltName otherName OID
+ * @cert: should contain a #gnutls_x509_crt_t structure
+ * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.)
+ * @ret: is the place where the otherName OID will be copied to
+ * @ret_size: holds the size of ret.
+ *
+ * This function will extract the type OID of an otherName Subject
+ * Alternative Name, contained in the given certificate, and return
+ * the type as an enumerated element.
+ *
+ * This function is only useful if
+ * gnutls_x509_crt_get_issuer_alt_name() returned
+ * %GNUTLS_SAN_OTHERNAME.
+ *
+ * Returns: the alternative issuer name type on success, one of the
+ * enumerated gnutls_x509_subject_alt_name_t.  For supported OIDs, it
+ * will return one of the virtual (GNUTLS_SAN_OTHERNAME_*) types,
+ * e.g. %GNUTLS_SAN_OTHERNAME_XMPP, and %GNUTLS_SAN_OTHERNAME for
+ * unknown OIDs.  It will return %GNUTLS_E_SHORT_MEMORY_BUFFER if
+ * @ret_size is not large enough to hold the value.  In that case
+ * @ret_size will be updated with the required size.  If the
+ * certificate does not have an Alternative name with the specified
+ * sequence number and with the otherName type then
+ * %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE is returned.
+ **/
+int
+gnutls_x509_crt_get_issuer_alt_othername_oid (gnutls_x509_crt_t cert,
+                                              unsigned int seq,
+                                              void *ret, size_t * ret_size)
+{
+  return get_alt_name (cert, "2.5.29.18", seq, ret, ret_size, NULL, NULL, 1);
 }
 
 /**
index 5538fb72dc6d2d1012550681c5452c31f3e31e7a..4779c646a2caf0073294d2f2abc529be65522950 100644 (file)
@@ -58,7 +58,7 @@ ctests = simple gc set_pkcs12_cred certder mpi                                \
        finished hostname-check cve-2008-4989 pkcs12_s2k chainverify    \
        crq_key_id x509sign-verify cve-2009-1415 cve-2009-1416          \
        crq_apis init_roundtrip pkcs12_s2k_pem dn2 mini-eagain          \
-       nul-in-x509-names
+       nul-in-x509-names x509_altname
 
 if ENABLE_OPENSSL
 ctests +=  openssl
diff --git a/tests/x509_altname.c b/tests/x509_altname.c
new file mode 100644 (file)
index 0000000..ef99bae
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Free Software Foundation
+ * Author: Simon Josefsson, Howard Chu
+ *
+ * This file is part of GNUTLS.
+ *
+ * GNUTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUTLS 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNUTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include "utils.h"
+
+static char pem[] =
+"-----BEGIN CERTIFICATE-----\n"
+"MIIE6zCCA9OgAwIBAgIBdjANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJTRTEf\n"
+"MB0GA1UEChMWU3RvY2tob2xtcyB1bml2ZXJzaXRldDEgMB4GA1UEAxMXU3RvY2to\n"
+"b2xtIFVuaXZlcnNpdHkgQ0EwHhcNMDYwMzIyMDkxNTI4WhcNMDcwMzIyMDkxNTI4\n"
+"WjBDMQswCQYDVQQGEwJTRTEfMB0GA1UEChMWU3RvY2tob2xtcyB1bml2ZXJzaXRl\n"
+"dDETMBEGA1UEAxMKc2lwMS5zdS5zZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC\n"
+"gYEArUzXTD36ZK7CwZJH/faUNTcdaqM7JyiZsfrO703d7cT/bJ3wKxT8trOOh/Ou\n"
+"WwgGFX2+r7ykun3aIUXUuD13Yle/yHqH/4g9vWX7UeFCBlSI0tAxnlqt0QqlPgSd\n"
+"GLHcoO4PPyjon9jj0A/zpJGZHiRUCooo63YqE9MYfr5HBfkCAwEAAaOCAl8wggJb\n"
+"MAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYD\n"
+"VR0OBBYEFDpcXNHMLJ7fc/c72BtZseq4MDXFMH8GA1UdIwR4MHaAFJ4uMLo32VFE\n"
+"yZ2/GCHxvX7utYZIoVukWTBXMQswCQYDVQQGEwJTRTEYMBYGA1UEChMPVW1lYSBV\n"
+"bml2ZXJzaXR5MRMwEQYDVQQLEwpTd1VQS0ktUENBMRkwFwYDVQQDExBTd1VQS0kg\n"
+"UG9saWN5IENBggEQMDIGA1UdHwQrMCkwJ6AloCOGIWh0dHA6Ly9jYS5zdS5zZS8y\n"
+"MDA1LTEvY3JsLXYyLmNybDB5BgNVHSAEcjBwMG4GCCqFcCsCAQEBMGIwHwYIKwYB\n"
+"BQUHAgEWE2h0dHA6Ly9jYS5zdS5zZS9DUFMwPwYIKwYBBQUHAgIwMxoxTGltaXRl\n"
+"ZCBMaWFiaWxpdHksIHNlZSBodHRwOi8vd3d3LnN3dXBraS5zdS5zZS9DUDAkBgNV\n"
+"HRIEHTAbgQhjYUBzdS5zZYYPaHR0cDovL2NhLnN1LnNlMIG3BgNVHREEga8wgayC\n"
+"F2luY29taW5ncHJveHkuc2lwLnN1LnNlghhpbmNvbWluZ3Byb3h5MS5zaXAuc3Uu\n"
+"c2WCF291dGdvaW5ncHJveHkuc2lwLnN1LnNlghhvdXRnb2luZ3Byb3h5MS5zaXAu\n"
+"c3Uuc2WCDW91dC5zaXAuc3Uuc2WCE2FwcHNlcnZlci5zaXAuc3Uuc2WCFGFwcHNl\n"
+"cnZlcjEuc2lwLnN1LnNlggpzaXAxLnN1LnNlMA0GCSqGSIb3DQEBBQUAA4IBAQAR\n"
+"FYg7ytcph0E7WmvM44AN/8qru7tRX6aSFWrjLyVr/1Wk4prCK4y5JpfNw5dh9Z8f\n"
+"/gyFsr1iFsb6fS3nJTTd3fVlWRfcNCGIx5g8KuSb3u6f7VznkGOeiRMRESQc1G8B\n"
+"eh0zbdZS7BYO2g9EKlbGST5PwQnc4g9K7pqPyKSNVkzb60Nujg/+qYje7MCcN+ZR\n"
+"nUBo6U2NZ06/QEUFm+uUIhZ8IGM1gLehC7Q3G4+d4c38CDJxQnSPOgWiXuSvhhQm\n"
+"KDsbrKzRaeBRh5eEJbTkA8Dp0Emb0UrkRVhixeg97stxUcATAjdGljJ9MLnuHXnI\n"
+"7ihGdUfg5q/105vpsQpO\n"
+"-----END CERTIFICATE-----\n";
+
+#define MAX_DATA_SIZE 1024
+
+void
+doit (void)
+{
+  int ret;
+  gnutls_datum_t derCert = { pem, sizeof (pem) };
+  gnutls_x509_crt_t cert;
+  size_t data_len = MAX_DATA_SIZE;
+  char data[ MAX_DATA_SIZE ];
+  unsigned int critical = 0;
+  int alt_name_count = 0;
+
+  ret = gnutls_global_init ();
+  if (ret < 0)
+    fail ("init %d\n", ret);
+
+  ret = gnutls_x509_crt_init (&cert);
+  if (ret < 0)
+    fail ("crt_init %d\n", ret);
+
+  ret = gnutls_x509_crt_import (cert, &derCert, GNUTLS_X509_FMT_PEM);
+  if (ret < 0)
+    fail ("crt_import %d\n", ret);
+
+  for (alt_name_count = 0; ; ++alt_name_count) {
+    ret = gnutls_x509_crt_get_issuer_alt_name (cert, alt_name_count, data, &data_len, &critical);
+    if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+        break;
+
+    if (ret < 0)
+      fail ("get_issuer_alt_name: %d\n", ret);
+
+    // TODO: print out / check results
+    if (GNUTLS_SAN_URI == ret) {
+      if (strcmp( data, "http://ca.su.se" ) != 0) {
+       fail("unexpected issuer GNUTLS_SAN_URI: %s\n", data);
+      }
+    } else if (GNUTLS_SAN_RFC822NAME == ret) {
+      if (strcmp( data, "ca@su.se" ) != 0) {
+       fail("unexpected issuer GNUTLS_SAN_RFC822NAME: %s\n", data);
+      }
+    } else {
+      fail("unexpected alt name type: %d\n", ret);
+    }
+    data_len = MAX_DATA_SIZE;
+  }
+
+  if (alt_name_count !=2) {
+    fail("unexpected number of alt names: %i\n", alt_name_count);
+  }
+
+  success ("done\n");
+
+  gnutls_x509_crt_deinit (cert);
+  gnutls_global_deinit ();
+}