Add support for needed functionality to mbedTLS
authorKevin Kane <kkane@microsoft.com>
Tue, 20 Dec 2016 20:51:27 +0000 (12:51 -0800)
committerGreg Zaverucha <gregz@microsoft.com>
Tue, 14 Feb 2017 01:39:29 +0000 (01:39 +0000)
Support for directoryName-based subject alternative name
Creating certificates containing this extension
Specifying a particular EKU to check for in TLS negotiation instead
of standard client/server authentication EKUs

This work will be offered upstream to the mbedTLS maintainers; if
they accept it, this will later be reverted when IoTivity ingests
a newer version of mbedTLS.

Change-Id: I4f81d75b767683327564e27758d1aa1bc02613de
Signed-off-by: Kevin Kane <kkane@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/15733
Reviewed-by: Alex Kelley <alexke@microsoft.com>
Reviewed-by: Pawel Winogrodzki <pawelwi@microsoft.com>
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Oleksii Beketov <ol.beketov@samsung.com>
Reviewed-by: dongik Lee <dongik.lee@samsung.com>
Reviewed-by: Greg Zaverucha <gregz@microsoft.com>
extlibs/mbedtls/.gitattributes [new file with mode: 0644]
extlibs/mbedtls/SConscript
extlibs/mbedtls/ocf.patch

diff --git a/extlibs/mbedtls/.gitattributes b/extlibs/mbedtls/.gitattributes
new file mode 100644 (file)
index 0000000..edcf997
--- /dev/null
@@ -0,0 +1 @@
+ocf.patch text eol=lf
index 2170618..9400c95 100644 (file)
@@ -66,7 +66,7 @@ if target_os != 'tizen':
 
 # Apply ocf patch on git revision
 if os.path.exists('.git/HEAD'):
-    cmd = 'git checkout development && git reset --hard ' + mbedtls_revision + ' && git apply --whitespace=fix ../ocf.patch'
+    cmd = 'git checkout development && git reset --hard ' + mbedtls_revision + ' && git clean -f && git apply --whitespace=fix ../ocf.patch'
     os.system(cmd)
 else:
     print 'mbedtls: Assume ocf.patch (TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256) was applied in %s' % mbedtls_dir
index 972577d..04bf27d 100644 (file)
@@ -1,3 +1,18 @@
+diff --git a/include/mbedtls/certs.h b/include/mbedtls/certs.h
+index ca49086..e41de29 100644
+--- a/include/mbedtls/certs.h
++++ b/include/mbedtls/certs.h
+@@ -73,6 +73,10 @@ extern const char   mbedtls_test_cli_crt_ec[];
+ extern const size_t mbedtls_test_cli_crt_ec_len;
+ extern const char   mbedtls_test_cli_key_ec[];
+ extern const size_t mbedtls_test_cli_key_ec_len;
++#if defined(MBEDTLS_SHA256_C) && defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT) && defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
++extern const char   mbedtls_test_srv_directoryname_ec_crt[];
++extern const size_t mbedtls_test_srv_directoryname_ec_crt_len;
++#endif
+ #endif
+ #if defined(MBEDTLS_RSA_C)
 diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
 index fe86c1e..e4583d6 100644
 --- a/include/mbedtls/check_config.h
@@ -44,8 +59,73 @@ index 27abbd9..fa4db26 100644
  #define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA MBEDTLS_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
  #define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
  #define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 MBEDTLS_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
+diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
+index 6fc9c77..e6d300e 100644
+--- a/include/mbedtls/config.h
++++ b/include/mbedtls/config.h
+@@ -648,6 +648,21 @@
+ #define MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED
+ /**
++ * \def MBEDTLS_KEY_EXCHANGE_ECDH_ANON_ENABLED
++ *
++ * Enable the ECDHE-ANON based ciphersuite modes in SSL / TLS.
++ *
++ * Requires: MBEDTLS_ECDH_C
++ *
++ *
++ * This enables the following ciphersuites (if other requisites are
++ * enabled as well):
++ *      MBEDTLS_TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256
++ */
++#define MBEDTLS_KEY_EXCHANGE_ECDH_ANON_ENABLED
++
++
++/**
+  * \def MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
+  *
+  * Enable the ECDHE-ECDSA based ciphersuite modes in SSL / TLS.
+@@ -1233,7 +1248,7 @@
+  *
+  * Comment this macro to disable support for SSL session tickets
+  */
+-#define MBEDTLS_SSL_SESSION_TICKETS
++//#define MBEDTLS_SSL_SESSION_TICKETS
+ /**
+  * \def MBEDTLS_SSL_EXPORT_KEYS
+@@ -1360,6 +1375,21 @@
+ #define MBEDTLS_X509_RSASSA_PSS_SUPPORT
+ /**
++ * \def MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++ *
++ * Enable parsing of all supported subtypes of the Subject Alternative Name
++ * extension. When enabled, the subject_alt_names field of mbedtls_x509_crt
++ * is defined as an mbedtls_x509_subject_alt_name_sequence, each element of
++ * which can describe a different subtype of the GeneralName choice as defined
++ * by the standard.
++ *
++ * Comment this macro to only support dNSName subtypes, and to define the
++ * subject_alt_names field as an mbedtls_x509_sequence. Any other subtypes will
++ * be ignored. This was the behavior in earlier versions.
++ */
++#define MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++
++/**
+  * \def MBEDTLS_ZLIB_SUPPORT
+  *
+  * If set, the SSL/TLS module uses ZLIB to support compression and
+@@ -1473,6 +1503,7 @@
+  *      MBEDTLS_TLS_PSK_WITH_AES_128_GCM_SHA256
+  *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA256
+  *      MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA
++ *      MBEDTLS_TLS_ECDH_ANON_WITH_AES_128_CBC_SHA256
+  *
+  * PEM_PARSE uses AES for decrypting encrypted keys.
+  */
 diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
-index ba499d2..8046e6e 100644
+index ba499d2..807bff6 100644
 --- a/include/mbedtls/ssl.h
 +++ b/include/mbedtls/ssl.h
 @@ -358,7 +358,8 @@ union mbedtls_ssl_premaster_secret
@@ -58,6 +138,67 @@ index ba499d2..8046e6e 100644
      unsigned char _pms_ecdh[MBEDTLS_ECP_MAX_BYTES];    /* RFC 4492 5.10 */
  #endif
  #if defined(MBEDTLS_KEY_EXCHANGE_PSK_ENABLED)
+@@ -652,6 +653,12 @@ struct mbedtls_ssl_config
+     mbedtls_ssl_key_cert *key_cert; /*!< own certificate/key pair(s)        */
+     mbedtls_x509_crt *ca_chain;     /*!< trusted CAs                        */
+     mbedtls_x509_crl *ca_crl;       /*!< trusted CAs CRLs                   */
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++    const char *client_oid;         /*!< OID to check on client certs       */
++    size_t client_oid_len;          /*!< length of client OID               */
++    const char *server_oid;         /*!< OID to check on server certs       */
++    size_t server_oid_len;          /*!< length of server OID               */
++#endif /* MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE */
+ #endif /* MBEDTLS_X509_CRT_PARSE_C */
+ #if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED)
+@@ -1615,6 +1622,47 @@ void mbedtls_ssl_conf_ca_chain( mbedtls_ssl_config *conf,
+ int mbedtls_ssl_conf_own_cert( mbedtls_ssl_config *conf,
+                               mbedtls_x509_crt *own_cert,
+                               mbedtls_pk_context *pk_key );
++
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++/**
++ * \brief                  Set custom EKU OIDs to be checked on certificates during TLS negotiation,
++ *                         and for selecting suitable certificates for TLS negotation.
++ *
++ * \note                   By default, if this function is not called, clients will
++ *                         check for the server authentication EKU (1.3.6.1.5.5.7.3.1) in
++ *                         a server's certificate, and servers will check for the
++ *                         client authentication EKU (1.3.6.1.5.5.7.3.2) if a client
++ *                         presents a certificate. 
++ *
++ * \param conf             SSL configuration
++ * \param client_oid       OID to check for when verifying client certificates as a server.
++ *                         This must be an MBEDTLS_OID_* constant from oid.h, or a custom OID
++ *                         supplied by the caller. If a custom OID is used, it must be provided in
++ *                         its ASN.1 encoding; human-readable dotted numeric strings are not supported.
++ *                         Additionally, callers using custom OID buffers must ensure those buffers remain
++ *                         live while this SSL configuration is live. Passing NULL will
++ *                         disable EKU checking of client certificates.
++ * \param client_oid_len   The length of client_oid, not counting a terminating NULL if present; for constants
++ *                         from oid.h, this can be obtained with MBEDTLS_OID_SIZE(x) where x is the OID constant.
++ *                         If client_oid is NULL, this must be zero.
++ * \param server_oid       OID to check for when verifying server certificates as a client.
++ *                         This must be an MBEDTLS_OID_* constant from oid.h, or a custom OID
++ *                         supplied by the caller. If a custom OID is used, it must be provided in
++ *                         its ASN.1 encoding; human-readable dotted numeric strings are not supported.
++ *                         Additionally, callers using custom OID buffers must ensure those buffers remain
++ *                         live while this SSL configuration is live. Passing NULL will
++ *                         disable EKU checking of server certificates.
++ * \param server_oid_len   The length of server_oid not counting a terminating NULL if present; for constants
++ *                         from oid.h, this can be obtained with MBEDTLS_OID_SIZE(x) where x is the OID constant.
++ *                         If client_oid is NULL, this must be zero.
++ *
++ * \return                 0 on success or MBEDTLS_ERR_SSL_BAD_INPUT_DATA for invalid arguments.
++ *                         On failure, existing behavior is unchanged.
++ */
++int mbedtls_ssl_conf_ekus( mbedtls_ssl_config *conf,
++                           const char *client_oid, size_t client_oid_len,
++                           const char *server_oid, size_t server_oid_len );
++#endif /* MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE */
+ #endif /* MBEDTLS_X509_CRT_PARSE_C */
+ #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
 diff --git a/include/mbedtls/ssl_ciphersuites.h b/include/mbedtls/ssl_ciphersuites.h
 index deaaa37..4f10540 100644
 --- a/include/mbedtls/ssl_ciphersuites.h
@@ -89,6 +230,130 @@ index deaaa37..4f10540 100644
  #define MBEDTLS_KEY_EXCHANGE__SOME__ECDHE_ENABLED
  #endif
  
+diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
+index 668c0f5..e1035f9 100644
+--- a/include/mbedtls/ssl_internal.h
++++ b/include/mbedtls/ssl_internal.h
+@@ -437,6 +437,8 @@ static inline mbedtls_x509_crt *mbedtls_ssl_own_cert( mbedtls_ssl_context *ssl )
+ int mbedtls_ssl_check_cert_usage( const mbedtls_x509_crt *cert,
+                           const mbedtls_ssl_ciphersuite_t *ciphersuite,
+                           int cert_endpoint,
++                          const char *client_oid, size_t client_oid_len,
++                          const char *server_oid, size_t server_oid_len,
+                           uint32_t *flags );
+ #endif /* MBEDTLS_X509_CRT_PARSE_C */
+diff --git a/include/mbedtls/x509.h b/include/mbedtls/x509.h
+index 54dac16..21f11a4 100644
+--- a/include/mbedtls/x509.h
++++ b/include/mbedtls/x509.h
+@@ -310,7 +310,7 @@ int mbedtls_x509_set_extension( mbedtls_asn1_named_data **head, const char *oid,
+ int mbedtls_x509_write_extensions( unsigned char **p, unsigned char *start,
+                            mbedtls_asn1_named_data *first );
+ int mbedtls_x509_write_names( unsigned char **p, unsigned char *start,
+-                      mbedtls_asn1_named_data *first );
++                      const mbedtls_asn1_named_data *first );
+ int mbedtls_x509_write_sig( unsigned char **p, unsigned char *start,
+                     const char *oid, size_t oid_len,
+                     unsigned char *sig, size_t size );
+diff --git a/include/mbedtls/x509_crt.h b/include/mbedtls/x509_crt.h
+index 383e484..d4cdae1 100644
+--- a/include/mbedtls/x509_crt.h
++++ b/include/mbedtls/x509_crt.h
+@@ -46,6 +46,31 @@ extern "C" {
+  * \{
+  */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++typedef enum
++{
++    /* Don't use the value zero in this enum, because we use zero to denote an unset struct. */
++    MBEDTLS_X509_GENERALNAME_DNSNAME = 1,
++    MBEDTLS_X509_GENERALNAME_DIRECTORYNAME
++} mbedtls_x509_general_name_choice;
++
++typedef struct mbedtls_x509_general_name
++{
++    mbedtls_x509_general_name_choice name_type;
++    union
++    {
++        mbedtls_x509_buf dns_name;
++        mbedtls_x509_name *directory_name;
++    };
++} mbedtls_x509_general_name;
++
++typedef struct mbedtls_x509_general_names
++{
++    mbedtls_x509_general_name general_name;
++    struct mbedtls_x509_general_names *next;
++} mbedtls_x509_general_names;
++#endif
++
+ /**
+  * Container for an X.509 certificate. The certificate may be chained.
+  */
+@@ -72,7 +97,11 @@ typedef struct mbedtls_x509_crt
+     mbedtls_x509_buf issuer_id;         /**< Optional X.509 v2/v3 issuer unique identifier. */
+     mbedtls_x509_buf subject_id;        /**< Optional X.509 v2/v3 subject unique identifier. */
+     mbedtls_x509_buf v3_ext;            /**< Optional X.509 v3 extensions.  */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    mbedtls_x509_general_names subject_alt_names; /**< Optional list of Subject Alternative Names (Only dNSName and directoryName supported). */
++#else
+     mbedtls_x509_sequence subject_alt_names;    /**< Optional list of Subject Alternative Names (Only dNSName supported). */
++#endif
+     int ext_types;              /**< Bit string containing detected and parsed extensions */
+     int ca_istrue;              /**< Optional Basic Constraint extension value: 1 if this certificate belongs to a CA, 0 otherwise. */
+@@ -593,6 +622,21 @@ int mbedtls_x509write_crt_set_key_usage( mbedtls_x509write_cert *ctx,
+ int mbedtls_x509write_crt_set_ns_cert_type( mbedtls_x509write_cert *ctx,
+                                     unsigned char ns_cert_type );
++
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++/**
++ * \brief           Set the subject alternative name extension
++ *
++ * \param ctx       CRT context to use
++ * \param names     subject alternative names. For each dNSName element, the tag field of the dns_name
++ *                  member does not need to be set and will be ignored.
++ *
++ * \return          0 if successful, or a specific error code
++ */
++int mbedtls_x509write_crt_set_subject_alt_names( mbedtls_x509write_cert *ctx,
++                                                 const mbedtls_x509_general_names *names );
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++
+ /**
+  * \brief           Free the contents of a CRT write context
+  *
+diff --git a/library/certs.c b/library/certs.c
+index ffe6bc9..812969d 100644
+--- a/library/certs.c
++++ b/library/certs.c
+@@ -114,6 +114,23 @@ const size_t mbedtls_test_srv_crt_ec_len = sizeof( mbedtls_test_srv_crt_ec );
+ const size_t mbedtls_test_srv_key_ec_len = sizeof( mbedtls_test_srv_key_ec );
+ const size_t mbedtls_test_cli_crt_ec_len = sizeof( mbedtls_test_cli_crt_ec );
+ const size_t mbedtls_test_cli_key_ec_len = sizeof( mbedtls_test_cli_key_ec );
++
++#if defined(MBEDTLS_SHA256_C) && defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT) && defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
++const char mbedtls_test_srv_directoryname_ec_crt[] =
++"-----BEGIN CERTIFICATE-----\r\n"
++"MIIBVzCB/qADAgECAgkAkWvgYjFeWV0wCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI\r\n"
++"VGVzdENlcnQwHhcNMTYxMjEzMjMwNDM3WhcNMzAwODIyMjMwNDM3WjATMREwDwYD\r\n"
++"VQQDDAhUZXN0Q2VydDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOkrKfbStoGN\r\n"
++"oOUTet2ryyRul++FE++6vpEup83bHywiOO1a4JtDgzNJcjj8/uAKsECYQrI1T4Y/\r\n"
++"rpnYu8ff9VGjOzA5MDcGA1UdEQQwMC6kLDAqMRYwFAYDVQQLDA1OYW1lIEFzc2ln\r\n"
++"bmVyMRAwDgYDVQQDDAdNeSBSb2xlMAoGCCqGSM49BAMCA0gAMEUCIGdd/GGuoOXJ\r\n"
++"8Ipl3hy69gb35MmkwEQKuYrud+Qs5XfFAiEAsmEOP4KFZadL23XJfMfGuPn2MDLr\r\n"
++"G9lDDpiediVxGO0=\r\n"
++"-----END CERTIFICATE-----\r\n";
++
++const size_t mbedtls_test_srv_directoryname_ec_crt_len = sizeof( mbedtls_test_srv_directoryname_ec_crt );
++#endif
++
+ #else
+ #define TEST_CA_CRT_EC
+ #endif /* MBEDTLS_ECDSA_C */
 diff --git a/library/entropy_poll.c b/library/entropy_poll.c
 index a116e60..c022caf 100644
 --- a/library/entropy_poll.c
@@ -406,10 +671,27 @@ index 223823b..945c973 100644
          MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate verify" ) );
          ssl->state++;
 diff --git a/library/ssl_srv.c b/library/ssl_srv.c
-index fc0d2d7..6965f1f 100644
+index fc0d2d7..18f89cd 100644
 --- a/library/ssl_srv.c
 +++ b/library/ssl_srv.c
-@@ -2498,6 +2498,7 @@ static int ssl_write_certificate_request( mbedtls_ssl_context *ssl )
+@@ -648,7 +648,15 @@ static int ssl_pick_cert( mbedtls_ssl_context *ssl,
+          * and decrypting with the same RSA key.
+          */
+         if( mbedtls_ssl_check_cert_usage( cur->cert, ciphersuite_info,
+-                                  MBEDTLS_SSL_IS_SERVER, &flags ) != 0 )
++                                  MBEDTLS_SSL_IS_SERVER, 
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++                                  ssl->conf->client_oid, ssl->conf->client_oid_len,
++                                  ssl->conf->server_oid, ssl->conf->server_oid_len,
++#else
++                                  NULL, 0,
++                                  NULL, 0,
++#endif
++                                  &flags ) != 0 )
+         {
+             MBEDTLS_SSL_DEBUG_MSG( 3, ( "certificate mismatch: "
+                                 "(extended) key usage extension" ) );
+@@ -2498,6 +2506,7 @@ static int ssl_write_certificate_request( mbedtls_ssl_context *ssl )
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ||
@@ -417,7 +699,7 @@ index fc0d2d7..6965f1f 100644
          authmode == MBEDTLS_SSL_VERIFY_NONE )
      {
          MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip write certificate request" ) );
-@@ -2675,7 +2676,8 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
+@@ -2675,7 +2684,8 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
      defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) ||                     \
      defined(MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED) ||                     \
      defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) ||                   \
@@ -427,7 +709,7 @@ index fc0d2d7..6965f1f 100644
      unsigned char *p = ssl->out_msg + 4;
      unsigned char *dig_signed = p;
      size_t dig_signed_len = 0, len;
-@@ -2736,12 +2738,11 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
+@@ -2736,12 +2746,11 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
      if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK )
      {
@@ -445,7 +727,7 @@ index fc0d2d7..6965f1f 100644
      }
  #endif /* MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED ||
            MBEDTLS_KEY_EXCHANGE_ECDHE_PSK_ENABLED */
-@@ -2798,7 +2799,8 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
+@@ -2798,7 +2807,8 @@ static int ssl_write_server_key_exchange( mbedtls_ssl_context *ssl )
  #if defined(MBEDTLS_KEY_EXCHANGE__SOME__ECDHE_ENABLED)
      if( ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_RSA ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA ||
@@ -455,7 +737,7 @@ index fc0d2d7..6965f1f 100644
      {
          /*
           * Ephemeral ECDH parameters:
-@@ -3336,11 +3338,13 @@ static int ssl_parse_client_key_exchange( mbedtls_ssl_context *ssl )
+@@ -3336,11 +3346,13 @@ static int ssl_parse_client_key_exchange( mbedtls_ssl_context *ssl )
  #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED) ||                     \
      defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED) ||                   \
      defined(MBEDTLS_KEY_EXCHANGE_ECDH_RSA_ENABLED) ||                      \
@@ -471,7 +753,7 @@ index fc0d2d7..6965f1f 100644
      {
          if( ( ret = mbedtls_ecdh_read_public( &ssl->handshake->ecdh_ctx,
                                        p, end - p) ) != 0 )
-@@ -3539,7 +3543,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
+@@ -3539,7 +3551,8 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_RSA_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
@@ -481,7 +763,7 @@ index fc0d2d7..6965f1f 100644
      {
          MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
          ssl->state++;
-@@ -3570,6 +3575,7 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
+@@ -3570,6 +3583,7 @@ static int ssl_parse_certificate_verify( mbedtls_ssl_context *ssl )
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECDHE_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_DHE_PSK ||
          ciphersuite_info->key_exchange == MBEDTLS_KEY_EXCHANGE_ECJPAKE ||
@@ -490,7 +772,7 @@ index fc0d2d7..6965f1f 100644
      {
          MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate verify" ) );
 diff --git a/library/ssl_tls.c b/library/ssl_tls.c
-index 84a04ae..938b840 100644
+index 84a04ae..2f23a3e 100644
 --- a/library/ssl_tls.c
 +++ b/library/ssl_tls.c
 @@ -4066,7 +4066,8 @@ int mbedtls_ssl_write_certificate( mbedtls_ssl_context *ssl )
@@ -533,7 +815,84 @@ index 84a04ae..938b840 100644
      {
          MBEDTLS_SSL_DEBUG_MSG( 2, ( "<= skip parse certificate" ) );
          ssl->state++;
-@@ -7539,6 +7543,7 @@ int mbedtls_ssl_check_cert_usage( const mbedtls_x509_crt *cert,
+@@ -4476,6 +4480,13 @@ int mbedtls_ssl_parse_certificate( mbedtls_ssl_context *ssl )
+         if( mbedtls_ssl_check_cert_usage( ssl->session_negotiate->peer_cert,
+                                   ciphersuite_info,
+                                   ! ssl->conf->endpoint,
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++                                  ssl->conf->client_oid, ssl->conf->client_oid_len,
++                                  ssl->conf->server_oid, ssl->conf->server_oid_len,
++#else
++                                  NULL, 0,
++                                  NULL, 0,
++#endif
+                                  &ssl->session_negotiate->verify_result ) != 0 )
+         {
+             MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad certificate (usage extensions)" ) );
+@@ -5759,6 +5770,28 @@ int mbedtls_ssl_conf_own_cert( mbedtls_ssl_config *conf,
+     return( ssl_append_key_cert( &conf->key_cert, own_cert, pk_key ) );
+ }
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++int mbedtls_ssl_conf_ekus( mbedtls_ssl_config *conf,
++                           const char *client_oid, size_t client_oid_len,
++                           const char *server_oid, size_t server_oid_len )
++{
++    if( ( client_oid_len == 0 && client_oid )  ||
++        ( client_oid_len != 0 && !client_oid ) ||
++        ( server_oid_len == 0 && server_oid )  ||
++        ( server_oid_len != 0 && !server_oid ) )
++    {
++        return( MBEDTLS_ERR_SSL_BAD_INPUT_DATA );
++    }
++
++    conf->client_oid = client_oid;
++    conf->client_oid_len = client_oid_len;
++    conf->server_oid = server_oid;
++    conf->server_oid_len = server_oid_len;
++
++    return( 0 );
++}
++#endif /* MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE */
++
+ void mbedtls_ssl_conf_ca_chain( mbedtls_ssl_config *conf,
+                                mbedtls_x509_crt *ca_chain,
+                                mbedtls_x509_crl *ca_crl )
+@@ -7246,6 +7279,13 @@ int mbedtls_ssl_config_defaults( mbedtls_ssl_config *conf,
+             }
+ #endif
++#if defined(MBEDTLS_X509_CRT_PARSE_C) && defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++    conf->client_oid = MBEDTLS_OID_CLIENT_AUTH;
++    conf->client_oid_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CLIENT_AUTH );
++    conf->server_oid = MBEDTLS_OID_SERVER_AUTH;
++    conf->server_oid_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH );
++#endif
++
+     /*
+      * Preset-specific defaults
+      */
+@@ -7493,6 +7533,8 @@ int mbedtls_ssl_check_sig_hash( const mbedtls_ssl_context *ssl,
+ int mbedtls_ssl_check_cert_usage( const mbedtls_x509_crt *cert,
+                           const mbedtls_ssl_ciphersuite_t *ciphersuite,
+                           int cert_endpoint,
++                          const char *client_oid, size_t client_oid_len,
++                          const char *server_oid, size_t server_oid_len,
+                           uint32_t *flags )
+ {
+     int ret = 0;
+@@ -7509,6 +7551,10 @@ int mbedtls_ssl_check_cert_usage( const mbedtls_x509_crt *cert,
+     ((void) cert);
+     ((void) cert_endpoint);
+     ((void) flags);
++    ((void) client_oid);
++    ((void) client_oid_len);
++    ((void) server_oid);
++    ((void) server_oid_len);
+ #endif
+ #if defined(MBEDTLS_X509_CHECK_KEY_USAGE)
+@@ -7539,6 +7585,7 @@ int mbedtls_ssl_check_cert_usage( const mbedtls_x509_crt *cert,
              case MBEDTLS_KEY_EXCHANGE_DHE_PSK:
              case MBEDTLS_KEY_EXCHANGE_ECDHE_PSK:
              case MBEDTLS_KEY_EXCHANGE_ECJPAKE:
@@ -541,8 +900,26 @@ index 84a04ae..938b840 100644
                  usage = 0;
          }
      }
+@@ -7560,13 +7607,13 @@ int mbedtls_ssl_check_cert_usage( const mbedtls_x509_crt *cert,
+ #if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
+     if( cert_endpoint == MBEDTLS_SSL_IS_SERVER )
+     {
+-        ext_oid = MBEDTLS_OID_SERVER_AUTH;
+-        ext_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH );
++        ext_oid = server_oid;
++        ext_len = server_oid_len;
+     }
+     else
+     {
+-        ext_oid = MBEDTLS_OID_CLIENT_AUTH;
+-        ext_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CLIENT_AUTH );
++        ext_oid = client_oid;
++        ext_len = client_oid_len;
+     }
+     if( mbedtls_x509_crt_check_extended_key_usage( cert, ext_oid, ext_len ) != 0 )
 diff --git a/library/version_features.c b/library/version_features.c
-index e866e67..3184bc2 100644
+index e866e67..7798b27 100644
 --- a/library/version_features.c
 +++ b/library/version_features.c
 @@ -264,6 +264,9 @@ static const char *features[] = {
@@ -555,8 +932,103 @@ index e866e67..3184bc2 100644
  #if defined(MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED)
      "MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED",
  #endif /* MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED */
+@@ -423,6 +426,9 @@ static const char *features[] = {
+ #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT)
+     "MBEDTLS_X509_RSASSA_PSS_SUPPORT",
+ #endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    "MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT",
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
+ #if defined(MBEDTLS_ZLIB_SUPPORT)
+     "MBEDTLS_ZLIB_SUPPORT",
+ #endif /* MBEDTLS_ZLIB_SUPPORT */
+diff --git a/library/x509.c b/library/x509.c
+index fad390d..0bc5367 100644
+--- a/library/x509.c
++++ b/library/x509.c
+@@ -1005,6 +1005,10 @@ int mbedtls_x509_self_test( int verbose )
+     uint32_t flags;
+     mbedtls_x509_crt cacert;
+     mbedtls_x509_crt clicert;
++#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_SHA256_C) && defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT) && defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
++    mbedtls_x509_crt directorynamecert;
++    char buf[2048];
++#endif
+     if( verbose != 0 )
+         mbedtls_printf( "  X.509 certificate load: " );
+@@ -1045,11 +1049,45 @@ int mbedtls_x509_self_test( int verbose )
+         return( ret );
+     }
++#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_SHA256_C) && defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT) && defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
++    if( verbose != 0 )
++        mbedtls_printf( "passed\n  X.509 subject alt name verify: " );
++
++    mbedtls_x509_crt_init( &directorynamecert );
++
++    ret = mbedtls_x509_crt_parse( &directorynamecert, (const unsigned char *) mbedtls_test_srv_directoryname_ec_crt,
++                                  mbedtls_test_srv_directoryname_ec_crt_len );
++
++    if( ret != 0 )
++    {
++        if( verbose != 0 )
++            mbedtls_printf( "failed\n" );
++
++        return( ret );
++    }
++
++    if( verbose != 0 )
++        mbedtls_printf( "passed\n  X.509 directoryName parsing: " );
++
++    ret = mbedtls_x509_crt_info( buf, sizeof( buf ), "", &directorynamecert );
++    if ( ret < 0 )
++    {
++        if ( verbose != 0 )
++            mbedtls_printf( "failed\n" );
++
++        return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
++    }
++
++#endif
++
+     if( verbose != 0 )
+-        mbedtls_printf( "passed\n\n");
++        mbedtls_printf( "passed\n\n" );
+     mbedtls_x509_crt_free( &cacert  );
+     mbedtls_x509_crt_free( &clicert );
++#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_SHA256_C) && defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT) && defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
++    mbedtls_x509_crt_free( &directorynamecert );
++#endif
+     return( 0 );
+ #else
+diff --git a/library/x509_create.c b/library/x509_create.c
+index df20ec8..5faec78 100644
+--- a/library/x509_create.c
++++ b/library/x509_create.c
+@@ -231,15 +231,15 @@ static int x509_write_name( unsigned char **p, unsigned char *start,
+ }
+ int mbedtls_x509_write_names( unsigned char **p, unsigned char *start,
+-                      mbedtls_asn1_named_data *first )
++                      const mbedtls_asn1_named_data *first )
+ {
+     int ret;
+     size_t len = 0;
+-    mbedtls_asn1_named_data *cur = first;
++    const mbedtls_asn1_named_data *cur = first;
+     while( cur != NULL )
+     {
+-        MBEDTLS_ASN1_CHK_ADD( len, x509_write_name( p, start, (char *) cur->oid.p,
++        MBEDTLS_ASN1_CHK_ADD( len, x509_write_name( p, start, (const char *) cur->oid.p,
+                                             cur->oid.len,
+                                             cur->val.p, cur->val.len ) );
+         cur = cur->next;
 diff --git a/library/x509_crt.c b/library/x509_crt.c
-index 60e14f9..67cedde 100644
+index 60e14f9..037efae 100644
 --- a/library/x509_crt.c
 +++ b/library/x509_crt.c
 @@ -62,6 +62,7 @@
@@ -567,7 +1039,99 @@ index 60e14f9..67cedde 100644
  #else
  #include <time.h>
  #endif
-@@ -1108,6 +1109,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+@@ -438,17 +439,31 @@ static int x509_get_ext_key_usage( unsigned char **p,
+  *      nameAssigner            [0]     DirectoryString OPTIONAL,
+  *      partyName               [1]     DirectoryString }
+  *
+- * NOTE: we only parse and use dNSName at this point.
++ * NOTE: If MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT is not defined, we only parse and use dNSName.
++ * If it is defined, we parse and use all supported types, which are currently dNSName and directoryName.
+  */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static int x509_get_subject_alt_name( unsigned char **p,
++                                      const unsigned char *end,
++                                      mbedtls_x509_general_names *subject_alt_name )
++
++#else
+ static int x509_get_subject_alt_name( unsigned char **p,
+                                       const unsigned char *end,
+                                       mbedtls_x509_sequence *subject_alt_name )
++#endif
+ {
+     int ret;
+     size_t len, tag_len;
+-    mbedtls_asn1_buf *buf;
+     unsigned char tag;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    mbedtls_x509_general_names *cur = subject_alt_name;
++    mbedtls_x509_general_name general_name;
++    size_t name_len;
++#else
++    mbedtls_asn1_buf *buf;
+     mbedtls_asn1_sequence *cur = subject_alt_name;
++#endif
+     /* Get main sequence tag */
+     if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
+@@ -474,6 +489,49 @@ static int x509_get_subject_alt_name( unsigned char **p,
+             return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+                     MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++        memset( &general_name, 0, sizeof( general_name ) );
++        switch ( tag )
++        {
++        case ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ): /* dNSName */
++            general_name.name_type = MBEDTLS_X509_GENERALNAME_DNSNAME;
++            general_name.dns_name.tag = tag;
++            general_name.dns_name.p = *p;
++            general_name.dns_name.len = tag_len;
++            *p += tag_len;
++            break;
++        case ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 4 ): /* directoryName */
++            general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
++            if( ( ret = mbedtls_asn1_get_tag( p, end, &name_len,
++                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
++                return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
++            general_name.directory_name = mbedtls_calloc( 1, sizeof( mbedtls_x509_name ) );
++            if ( general_name.directory_name == NULL )
++                return( MBEDTLS_ERR_X509_ALLOC_FAILED );
++            if( ( ret = mbedtls_x509_get_name( p, *p + name_len, general_name.directory_name ) ) != 0 )
++                return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
++            break;
++        default:
++            *p += tag_len;
++            continue;
++        }
++
++        if( cur->general_name.name_type != 0 )
++        {
++            if( cur->next != NULL )
++                return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS );
++
++            cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_general_names ) );
++
++            if( cur->next == NULL )
++                return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
++                        MBEDTLS_ERR_ASN1_ALLOC_FAILED );
++
++            cur = cur->next;
++        }
++
++        memcpy( &cur->general_name, &general_name, sizeof( general_name ) );
++#else
+         /* Skip everything but DNS name */
+         if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) )
+         {
+@@ -501,6 +559,7 @@ static int x509_get_subject_alt_name( unsigned char **p,
+         buf->p = *p;
+         buf->len = tag_len;
+         *p += buf->len;
++#endif
+     }
+     /* Set final sequence entry's next pointer to NULL */
+@@ -1108,6 +1167,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
      char filename[MAX_PATH];
      char *p;
      size_t len = strlen( path );
@@ -575,7 +1139,7 @@ index 60e14f9..67cedde 100644
  
      WIN32_FIND_DATAW file_data;
      HANDLE hFind;
-@@ -1122,7 +1124,10 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+@@ -1122,7 +1182,10 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
      p = filename + len;
      filename[len++] = '*';
  
@@ -587,7 +1151,7 @@ index 60e14f9..67cedde 100644
                                   MAX_PATH - 3 );
      if( w_ret == 0 )
          return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
-@@ -1139,8 +1144,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+@@ -1139,8 +1202,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
          if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
              continue;
  
@@ -600,3 +1164,1110 @@ index 60e14f9..67cedde 100644
                                       p, (int) len - 1,
                                       NULL, NULL );
          if( w_ret == 0 )
+@@ -1219,6 +1285,98 @@ cleanup:
+ }
+ #endif /* MBEDTLS_FS_IO */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static const char x509_directory_name_label[] = "directoryName=(";
++static const char x509_directory_name_epilogue[] = ")";
++
++/* Length of label constant excluding terminating null. */
++#define LABEL_LEN( label ) ( sizeof ( label ) - 1 )
++
++static int x509_info_subject_alt_name( char **buf, size_t *size,
++                                       const mbedtls_x509_general_names *subject_alt_name )
++{
++    int ret;
++    size_t i;
++    size_t n = *size;
++    char *p = *buf;
++    const mbedtls_x509_general_names *cur = subject_alt_name;
++    const char *sep = "";
++    size_t sep_len = 0;
++
++    while( cur != NULL )
++    {
++        switch ( cur->general_name.name_type )
++        {
++        case MBEDTLS_X509_GENERALNAME_DNSNAME:
++            i = cur->general_name.dns_name.len + sep_len;
++
++            if( i >= n )
++            {
++                *p = '\0';
++                return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++            }
++
++            n -= i;
++            for( i = 0; i < sep_len; i++ )
++                *p++ = sep[i];
++            for( i = 0; i < cur->general_name.dns_name.len; i++ )
++                *p++ = cur->general_name.dns_name.p[i];
++
++            break;
++
++        case MBEDTLS_X509_GENERALNAME_DIRECTORYNAME:
++            i = sep_len + LABEL_LEN( x509_directory_name_label );
++            if( i >= n )
++            {
++                *p = '\0';
++                return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++            }
++
++            n -= i;
++            for( i = 0; i < sep_len; i++ )
++                *p++ = sep[i];
++            for( i = 0; i < LABEL_LEN( x509_directory_name_label ); i++ )
++                *p++ = x509_directory_name_label[i];
++
++            ret = mbedtls_x509_dn_gets( p, n, cur->general_name.directory_name );
++            if( ret < 0 || ( (size_t) ret ) >= n )
++            {
++                *p = '\0';
++                return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++            }
++
++            n -= ret;
++            p += ret;
++
++            i = LABEL_LEN( x509_directory_name_epilogue );
++
++            if( i >= n )
++            {
++                *p = '\0';
++                return( MBEDTLS_ERR_X509_BUFFER_TOO_SMALL );
++            }
++
++            n -= i;
++            for( i = 0; i < LABEL_LEN( x509_directory_name_epilogue ); i++ )
++                *p++ = x509_directory_name_epilogue[i];
++
++            break;
++        }
++
++        sep = ", ";
++        sep_len = 2;
++
++        cur = cur->next;
++    }
++
++    *p = '\0';
++
++    *size = n;
++    *buf = p;
++
++    return( 0 );
++}
++#else
+ static int x509_info_subject_alt_name( char **buf, size_t *size,
+                                        const mbedtls_x509_sequence *subject_alt_name )
+ {
+@@ -1256,6 +1414,7 @@ static int x509_info_subject_alt_name( char **buf, size_t *size,
+     return( 0 );
+ }
++#endif
+ #define PRINT_ITEM(i)                           \
+     {                                           \
+@@ -2188,7 +2347,11 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
+     int pathlen = 0, selfsigned = 0;
+     mbedtls_x509_crt *parent;
+     mbedtls_x509_name *name;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    mbedtls_x509_general_names *cur = NULL;
++#else
+     mbedtls_x509_sequence *cur = NULL;
++#endif
+     mbedtls_pk_type_t pk_type;
+     if( profile == NULL )
+@@ -2207,6 +2370,22 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
+             while( cur != NULL )
+             {
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++                /* Only consider dNSName subject alternative names for this check; ignore other types. */
++                if ( cur->general_name.name_type == MBEDTLS_X509_GENERALNAME_DNSNAME )
++                {
++                    if ( cur->general_name.dns_name.len == cn_len &&
++                        x509_memcasecmp( cn, cur->general_name.dns_name.p, cn_len ) == 0 )
++                        break;
++
++                    if ( cur->general_name.dns_name.len > 2 &&
++                        memcmp( cur->general_name.dns_name.p, "*.", 2 ) == 0 &&
++                        x509_check_wildcard( cn, &cur->general_name.dns_name ) == 0 )
++                    {
++                        break;
++                    }
++                }
++#else
+                 if( cur->buf.len == cn_len &&
+                     x509_memcasecmp( cn, cur->buf.p, cn_len ) == 0 )
+                     break;
+@@ -2217,6 +2396,7 @@ int mbedtls_x509_crt_verify_with_profile( mbedtls_x509_crt *crt,
+                 {
+                     break;
+                 }
++#endif
+                 cur = cur->next;
+             }
+@@ -2318,6 +2498,10 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
+     mbedtls_x509_crt *cert_prv;
+     mbedtls_x509_name *name_cur;
+     mbedtls_x509_name *name_prv;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    mbedtls_x509_general_names *san_cur;
++    mbedtls_x509_general_names *san_prv;
++#endif
+     mbedtls_x509_sequence *seq_cur;
+     mbedtls_x509_sequence *seq_prv;
+@@ -2359,6 +2543,38 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
+             mbedtls_free( seq_prv );
+         }
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++        if ( cert_cur->subject_alt_names.general_name.name_type == MBEDTLS_X509_GENERALNAME_DIRECTORYNAME )
++        {
++            name_cur = cert_cur->subject_alt_names.general_name.directory_name;
++            while ( name_cur != NULL )
++            {
++                name_prv = name_cur;
++                name_cur = name_cur->next;
++                mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
++                mbedtls_free( name_prv );
++            }
++        }
++        san_cur = cert_cur->subject_alt_names.next;
++        while ( san_cur != NULL )
++        {
++            san_prv = san_cur;
++            san_cur = san_cur->next;
++            if ( san_prv->general_name.name_type == MBEDTLS_X509_GENERALNAME_DIRECTORYNAME )
++            {
++                name_cur = san_prv->general_name.directory_name;
++                while ( name_cur != NULL )
++                {
++                    name_prv = name_cur;
++                    name_cur = name_cur->next;
++                    mbedtls_zeroize( name_prv, sizeof( mbedtls_x509_name ) );
++                    mbedtls_free( name_prv );
++                }
++            }
++            mbedtls_zeroize( san_prv, sizeof( mbedtls_x509_general_names ));
++            mbedtls_free( san_prv );
++        }
++#else
+         seq_cur = cert_cur->subject_alt_names.next;
+         while( seq_cur != NULL )
+         {
+@@ -2367,6 +2583,7 @@ void mbedtls_x509_crt_free( mbedtls_x509_crt *crt )
+             mbedtls_zeroize( seq_prv, sizeof( mbedtls_x509_sequence ) );
+             mbedtls_free( seq_prv );
+         }
++#endif
+         if( cert_cur->raw.p != NULL )
+         {
+diff --git a/library/x509write_crt.c b/library/x509write_crt.c
+index d1d9a22..55f194d 100644
+--- a/library/x509write_crt.c
++++ b/library/x509write_crt.c
+@@ -263,6 +263,63 @@ int mbedtls_x509write_crt_set_ns_cert_type( mbedtls_x509write_cert *ctx,
+     return( 0 );
+ }
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static int x509write_crt_set_subject_alt_name( unsigned char **c, unsigned char *buf,
++                                               const mbedtls_x509_general_name *name )
++{
++    int ret;
++    size_t len = 0;
++
++    switch ( name->name_type )
++    {
++    case MBEDTLS_X509_GENERALNAME_DNSNAME:
++        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_raw_buffer( c, buf, name->dns_name.p, name->dns_name.len ) );
++        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( c, buf, name->dns_name.len ) );
++        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( c, buf, MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) );
++        break;
++
++    case MBEDTLS_X509_GENERALNAME_DIRECTORYNAME:
++        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_x509_write_names( c, buf, name->directory_name ) );
++        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( c, buf, len ) );
++        MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( c, buf,
++                                   MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 4 ) );
++        break;
++
++    default:
++        return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
++    }
++
++    return( (int)len );
++}
++
++int mbedtls_x509write_crt_set_subject_alt_names( mbedtls_x509write_cert *ctx,
++                                                 const mbedtls_x509_general_names *names )
++{
++    int ret;
++    unsigned char buf[2048];
++    unsigned char *c = buf + sizeof( buf );
++    size_t len = 0;
++    const mbedtls_x509_general_names *cur;
++
++    for ( cur = names; cur != NULL; cur = cur->next )
++    {
++        MBEDTLS_ASN1_CHK_ADD( len, x509write_crt_set_subject_alt_name( &c, buf, &cur->general_name ) );
++    }
++
++    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &c, buf, len ) );
++    MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &c, buf, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
++
++    ret = mbedtls_x509write_crt_set_extension( ctx, MBEDTLS_OID_SUBJECT_ALT_NAME,
++                                               MBEDTLS_OID_SIZE( MBEDTLS_OID_SUBJECT_ALT_NAME ),
++                                               0, c, len );
++
++    if( ret != 0 )
++        return( ret );
++
++    return( 0 );
++}
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++
+ static int x509_write_time( unsigned char **p, unsigned char *start,
+                             const char *time, size_t size )
+ {
+diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
+index a1d71e1..b334656 100644
+--- a/programs/ssl/ssl_client2.c
++++ b/programs/ssl/ssl_client2.c
+@@ -58,6 +58,7 @@ int main( void )
+ #include "mbedtls/error.h"
+ #include "mbedtls/debug.h"
+ #include "mbedtls/timing.h"
++#include "mbedtls/oid.h"
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -103,6 +104,8 @@ int main( void )
+ #define DFL_FALLBACK            -1
+ #define DFL_EXTENDED_MS         -1
+ #define DFL_ETM                 -1
++#define DFL_EKU_CLIENT          ""
++#define DFL_EKU_SERVER          ""
+ #define GET_REQUEST "GET %s HTTP/1.0\r\nExtra-header: "
+ #define GET_REQUEST_END "\r\n\r\n"
+@@ -222,6 +225,14 @@ int main( void )
+ #define USAGE_ECJPAKE ""
+ #endif
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++#define USAGE_EKU \
++    "    eku=%%s-%%s           default: client-server\n"          \
++    "                        options for each: server, client, codesign\n"
++#else
++#define USAGE_EKU ""
++#endif
++
+ #define USAGE \
+     "\n usage: ssl_client2 param=<>...\n"                   \
+     "\n acceptable parameters:\n"                           \
+@@ -261,6 +272,7 @@ int main( void )
+     USAGE_ETM                                               \
+     USAGE_RECSPLIT                                          \
+     USAGE_DHMLEN                                            \
++    USAGE_EKU                                               \
+     "\n"                                                    \
+     "    arc4=%%d             default: (library default: 0)\n" \
+     "    min_version=%%s      default: (library default: tls1)\n"       \
+@@ -317,6 +329,10 @@ struct options
+     int fallback;               /* is this a fallback connection?           */
+     int extended_ms;            /* negotiate extended master secret?        */
+     int etm;                    /* negotiate encrypt then mac?              */
++    const char *eku_cli;        /* EKU to check for in client cert          */
++    size_t eku_cli_len;         /* length of eku_cli                        */
++    const char *eku_srv;        /* EKU to check for in server cert          */
++    size_t eku_srv_len;         /* length of eku_srv                        */
+ } opt;
+ static void my_debug( void *ctx, int level,
+@@ -507,6 +523,10 @@ int main( int argc, char *argv[] )
+     opt.fallback            = DFL_FALLBACK;
+     opt.extended_ms         = DFL_EXTENDED_MS;
+     opt.etm                 = DFL_ETM;
++    opt.eku_cli             = DFL_EKU_CLIENT;
++    opt.eku_cli_len         = MBEDTLS_OID_SIZE( DFL_EKU_CLIENT );
++    opt.eku_srv             = DFL_EKU_SERVER;
++    opt.eku_srv_len         = MBEDTLS_OID_SIZE( DFL_EKU_SERVER );
+     for( i = 1; i < argc; i++ )
+     {
+@@ -797,6 +817,47 @@ int main( int argc, char *argv[] )
+             if( opt.dhmlen < 0 )
+                 goto usage;
+         }
++        else if ( strcmp(p, "eku") == 0 )
++        {
++            if ( ( p = strchr( q, '-' ) ) == NULL )
++                goto usage;
++            *p++ = '\0';
++            if ( strcmp( q, "server" ) == 0 )
++            {
++                opt.eku_cli = MBEDTLS_OID_SERVER_AUTH;
++                opt.eku_cli_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH);
++            }
++            else if ( strcmp( q, "client" ) == 0 )
++            {
++                opt.eku_cli = MBEDTLS_OID_CLIENT_AUTH;
++                opt.eku_cli_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH);
++            }
++            else if ( strcmp( q, "codesign" ) == 0 )
++            {
++                opt.eku_cli = MBEDTLS_OID_CODE_SIGNING;
++                opt.eku_cli_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CODE_SIGNING);
++            }
++            else
++                goto usage;
++
++            if ( strcmp( p, "server" ) == 0 )
++            {
++                opt.eku_srv = MBEDTLS_OID_SERVER_AUTH;
++                opt.eku_srv_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH);
++            }
++            else if ( strcmp( p, "client" ) == 0 )
++            {
++                opt.eku_srv = MBEDTLS_OID_CLIENT_AUTH;
++                opt.eku_srv_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH);
++            }
++            else if ( strcmp( p, "codesign" ) == 0 )
++            {
++                opt.eku_srv = MBEDTLS_OID_CODE_SIGNING;
++                opt.eku_srv_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CODE_SIGNING);
++            }
++            else
++                goto usage;
++        }
+         else
+             goto usage;
+     }
+@@ -1088,6 +1149,19 @@ int main( int argc, char *argv[] )
+         goto exit;
+     }
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++    if ( opt.eku_cli_len > 0 && opt.eku_srv_len > 0 )
++    {
++        if( ( ret = mbedtls_ssl_conf_ekus( &conf,
++                        opt.eku_cli, opt.eku_cli_len,
++                        opt.eku_srv, opt.eku_srv_len ) ) != 0 )
++        {
++            mbedtls_printf( " failed\n  ! mbedtls_ssl_config_ekus returned -0x%x\n\n", -ret );
++            goto exit;
++        }
++    }
++#endif
++
+ #if defined(MBEDTLS_X509_CRT_PARSE_C)
+     if( opt.debug_level > 0 )
+         mbedtls_ssl_conf_verify( &conf, my_verify, NULL );
+diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
+index 18bda59..e65576b 100644
+--- a/programs/ssl/ssl_server2.c
++++ b/programs/ssl/ssl_server2.c
+@@ -59,6 +59,7 @@ int main( void )
+ #include "mbedtls/error.h"
+ #include "mbedtls/debug.h"
+ #include "mbedtls/timing.h"
++#include "mbedtls/oid.h"
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -136,6 +137,8 @@ int main( void )
+ #define DFL_BADMAC_LIMIT        -1
+ #define DFL_EXTENDED_MS         -1
+ #define DFL_ETM                 -1
++#define DFL_EKU_CLIENT          ""
++#define DFL_EKU_SERVER          ""
+ #define LONG_RESPONSE "<p>01-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n" \
+     "02-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah-blah\r\n"  \
+@@ -304,6 +307,14 @@ int main( void )
+ #define USAGE_ECJPAKE ""
+ #endif
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++#define USAGE_EKU \
++    "    eku=%%s-%%s           default: client-server\n"          \
++    "                        options for each: server, client, codesign\n"
++#else
++#define USAGE_EKU ""
++#endif
++
+ #define USAGE \
+     "\n usage: ssl_server2 param=<>...\n"                   \
+     "\n acceptable parameters:\n"                           \
+@@ -338,6 +349,7 @@ int main( void )
+     USAGE_ALPN                                              \
+     USAGE_EMS                                               \
+     USAGE_ETM                                               \
++    USAGE_EKU                                               \
+     "\n"                                                    \
+     "    arc4=%%d             default: (library default: 0)\n" \
+     "    min_version=%%s      default: (library default: tls1)\n"       \
+@@ -400,6 +412,10 @@ struct options
+     uint32_t hs_to_min;         /* Initial value of DTLS handshake timer    */
+     uint32_t hs_to_max;         /* Max value of DTLS handshake timer        */
+     int badmac_limit;           /* Limit of records with bad MAC            */
++    const char *eku_cli;        /* EKU to check for in client cert          */
++    size_t eku_cli_len;         /* length of eku_cli                        */
++    const char *eku_srv;        /* EKU to check for in server cert          */
++    size_t eku_srv_len;         /* length of eku_srv                        */
+ } opt;
+ static void my_debug( void *ctx, int level,
+@@ -943,6 +959,10 @@ int main( int argc, char *argv[] )
+     opt.badmac_limit        = DFL_BADMAC_LIMIT;
+     opt.extended_ms         = DFL_EXTENDED_MS;
+     opt.etm                 = DFL_ETM;
++    opt.eku_cli             = DFL_EKU_CLIENT;
++    opt.eku_cli_len         = MBEDTLS_OID_SIZE( DFL_EKU_CLIENT );
++    opt.eku_srv             = DFL_EKU_SERVER;
++    opt.eku_srv_len         = MBEDTLS_OID_SIZE( DFL_EKU_SERVER );
+     for( i = 1; i < argc; i++ )
+     {
+@@ -1232,6 +1252,47 @@ int main( int argc, char *argv[] )
+         {
+             opt.sni = q;
+         }
++        else if( strcmp( p, "eku" ) == 0 )
++        {
++            if( ( p = strchr( q, '-' ) ) == NULL )
++                goto usage;
++            *p++ = '\0';
++            if( strcmp( q, "server" ) == 0 )
++            {
++                opt.eku_cli = MBEDTLS_OID_SERVER_AUTH;
++                opt.eku_cli_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH );
++            }
++            else if( strcmp( q, "client" ) == 0 )
++            {
++                opt.eku_cli = MBEDTLS_OID_CLIENT_AUTH;
++                opt.eku_cli_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CLIENT_AUTH );
++            }
++            else if( strcmp( q, "codesign" ) == 0 )
++            {
++                opt.eku_cli = MBEDTLS_OID_CODE_SIGNING;
++                opt.eku_cli_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CODE_SIGNING );
++            }
++            else
++                goto usage;
++
++            if( strcmp( p, "server" ) == 0 )
++            {
++                opt.eku_srv = MBEDTLS_OID_SERVER_AUTH;
++                opt.eku_srv_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_SERVER_AUTH );
++            }
++            else if( strcmp( p, "client" ) == 0 )
++            {
++                opt.eku_srv = MBEDTLS_OID_CLIENT_AUTH;
++                opt.eku_srv_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CLIENT_AUTH );
++            }
++            else if( strcmp( p, "codesign" ) == 0 )
++            {
++                opt.eku_srv = MBEDTLS_OID_CODE_SIGNING;
++                opt.eku_srv_len = MBEDTLS_OID_SIZE( MBEDTLS_OID_CODE_SIGNING );
++            }
++            else
++                goto usage;
++        }
+         else
+             goto usage;
+     }
+@@ -1608,6 +1669,20 @@ int main( int argc, char *argv[] )
+         goto exit;
+     }
++#if defined(MBEDTLS_X509_CHECK_EXTENDED_KEY_USAGE)
++    if ( opt.eku_cli_len > 0 && opt.eku_srv_len > 0 )
++    {
++        if( ( ret = mbedtls_ssl_conf_ekus( &conf,
++                        opt.eku_cli, opt.eku_cli_len,
++                        opt.eku_srv, opt.eku_srv_len ) ) != 0 )
++        {
++            mbedtls_printf( " failed\n  ! mbedtls_ssl_config_ekus returned -0x%x\n\n", -ret );
++            goto exit;
++        }
++    }
++#endif
++
++
+     if( opt.auth_mode != DFL_AUTH_MODE )
+         mbedtls_ssl_conf_authmode( &conf, opt.auth_mode );
+diff --git a/programs/x509/cert_write.c b/programs/x509/cert_write.c
+index 66e5f1d..1405e71 100644
+--- a/programs/x509/cert_write.c
++++ b/programs/x509/cert_write.c
+@@ -66,6 +66,16 @@ int main( void )
+ #define USAGE_CSR ""
+ #endif /* MBEDTLS_X509_CSR_PARSE_C */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++#define USAGE_SUBJ_ALT_NAME \
++    "    subj_alt_name=%%s    default: (empty)\n"       \
++    "                        Comma-separated-list of values:\n"      \
++    "                          dns_name=%%s\n"          \
++    "                          directory_name=(OU=%%s;CN=%%s;...)\n"
++#else
++#define USAGE_SUBJ_ALT_NAME ""
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++
+ #define DFL_ISSUER_CRT          ""
+ #define DFL_REQUEST_FILE        ""
+ #define DFL_SUBJECT_KEY         "subject.key"
+@@ -127,6 +137,7 @@ int main( void )
+     "                          ssl_ca\n"                \
+     "                          email_ca\n"              \
+     "                          object_signing_ca\n"     \
++    USAGE_SUBJ_ALT_NAME                                 \
+     "\n"
+ /*
+@@ -151,6 +162,9 @@ struct options
+     int max_pathlen;            /* maximum CA path length               */
+     unsigned char key_usage;    /* key usage flags                      */
+     unsigned char ns_cert_type; /* NS cert type                         */
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    mbedtls_x509_general_names subj_alt_names; /* Subject alternative names  */
++#endif
+ } opt;
+ int write_certificate( mbedtls_x509write_cert *crt, const char *output_file,
+@@ -182,6 +196,58 @@ int write_certificate( mbedtls_x509write_cert *crt, const char *output_file,
+     return( 0 );
+ }
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++static int add_subj_alt_name( mbedtls_x509_general_names **cur, const mbedtls_x509_general_name *add )
++{
++    mbedtls_x509_general_names *new_cur = *cur;
++
++    if ( new_cur->general_name.name_type != 0 )
++    {
++        if (new_cur->next != NULL)
++            return( -1 );
++
++        new_cur->next = mbedtls_calloc( 1, sizeof( mbedtls_x509_general_names ) );
++
++        if (new_cur->next == NULL)
++            return( -1 );
++
++        new_cur = new_cur->next;
++    }
++
++    memcpy( &new_cur->general_name, add, sizeof( mbedtls_x509_general_name ) );
++
++    *cur = new_cur;
++
++    return( 0 );
++}
++
++static void subj_alt_names_free( mbedtls_x509_general_names *names )
++{
++    mbedtls_x509_general_names *cur = names;
++    mbedtls_x509_general_names *prv;
++
++    while ( cur != NULL )
++    {
++        prv = cur;
++        cur = cur->next;
++
++        if ( prv->general_name.name_type == MBEDTLS_X509_GENERALNAME_DIRECTORYNAME )
++        {
++            mbedtls_asn1_free_named_data_list( &prv->general_name.directory_name );
++        }
++
++        /*
++         * The first node is part of the opt struct and not heap allocated, so don't free it.
++         * Every other loop, free the node.
++         */
++        if ( prv != names )
++        {
++            mbedtls_free( prv );
++        }
++    }
++}
++#endif
++
+ int main( int argc, char *argv[] )
+ {
+     int ret = 0;
+@@ -202,6 +268,10 @@ int main( int argc, char *argv[] )
+     mbedtls_entropy_context entropy;
+     mbedtls_ctr_drbg_context ctr_drbg;
+     const char *pers = "crt example app";
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    mbedtls_x509_general_names *name_cur = &opt.subj_alt_names;
++    mbedtls_x509_general_name name_tmp;
++#endif
+     /*
+      * Set to sane values
+@@ -243,6 +313,9 @@ int main( int argc, char *argv[] )
+     opt.max_pathlen         = DFL_MAX_PATHLEN;
+     opt.key_usage           = DFL_KEY_USAGE;
+     opt.ns_cert_type        = DFL_NS_CERT_TYPE;
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    memset( &opt.subj_alt_names, 0, sizeof( opt.subj_alt_names ) );
++#endif
+     for( i = 1; i < argc; i++ )
+     {
+@@ -358,6 +431,86 @@ int main( int argc, char *argv[] )
+                 q = r;
+             }
+         }
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++        else if( strcmp( p, "subj_alt_name" ) == 0 )
++        {
++            while( q != NULL )
++            {
++                char *s;
++
++                if( ( r = strchr( q, ',' ) ) != NULL )
++                    *r++ = '\0';
++
++                if( ( s = strchr( q, '=' ) ) == NULL )
++                    goto usage;
++
++                *s++ = '\0';
++
++                if( strcmp( q, "dns_name" ) == 0 )
++                {
++                    name_tmp.name_type = MBEDTLS_X509_GENERALNAME_DNSNAME;
++                    name_tmp.dns_name.len = strlen( s );
++                    name_tmp.dns_name.p = (unsigned char *)s;
++                    /* tag field doesn't need to be set for writing. */
++                }
++                else if( strcmp( q, "directory_name" ) == 0 )
++                {
++                    char *rp, *tmp;
++
++                    if ( *s != '(' )
++                        goto usage;
++
++                    if ( ( rp = strchr( s + 1, ')' ) ) == NULL )
++                        goto usage;
++
++                    /*
++                     * Replace semicolons in the parenthesized list with commas and temporarily
++                     * terminate with null so we can use mbedtls_x509_string_to_names, call it,
++                     * and then change them back so the commas don't interfere with later parsing.
++                     */
++
++                    for ( tmp = s + 1; tmp < rp; tmp++ )
++                    {
++                        if ( *tmp == ';' )
++                        {
++                            *tmp = ',';
++                        }
++                    }
++
++                    *rp = '\0';
++
++                    name_tmp.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
++                    name_tmp.directory_name = NULL;
++                    ret = mbedtls_x509_string_to_names( &name_tmp.directory_name, s + 1 );
++
++                    if ( ret < 0 )
++                    {
++                        mbedtls_strerror( ret, buf, 1024 );
++                        mbedtls_printf( " failed\n  ! mbedtls_x509_string_to_names returned %d - %s\n", ret, buf );
++                        goto exit;
++                    }
++
++                    for ( tmp = s + 1; tmp < rp; tmp++ )
++                    {
++                        if ( *tmp == ',' )
++                        {
++                            *tmp = ';';
++                        }
++                    }
++
++                    *rp = ')';
++
++                }
++                else
++                    goto usage;
++
++                if ( add_subj_alt_name( &name_cur, &name_tmp ) != 0 )
++                    goto exit;
++
++                q = r;
++            }
++        }
++#endif /* MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
+         else
+             goto usage;
+     }
+@@ -632,6 +785,24 @@ int main( int argc, char *argv[] )
+         mbedtls_printf( " ok\n" );
+     }
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    if ( opt.subj_alt_names.general_name.name_type )
++    {
++        mbedtls_printf( "  . Adding the Subject Alternative Name extension ..." );
++        fflush( stdout );
++
++        ret = mbedtls_x509write_crt_set_subject_alt_names( &crt, &opt.subj_alt_names );
++        if ( ret != 0 )
++        {
++            mbedtls_strerror( ret, buf, 1024 );
++            mbedtls_printf( " failed\n  !  mbedtls_x509write_crt_set_subject_alt_names returned -0x%02x - %s\n\n", -ret, buf );
++            goto exit;
++        }
++
++        mbedtls_printf( " ok\n" );
++    }
++#endif
++
+     /*
+      * 1.2. Writing the request
+      */
+@@ -649,6 +820,9 @@ int main( int argc, char *argv[] )
+     mbedtls_printf( " ok\n" );
+ exit:
++#if defined(MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT)
++    subj_alt_names_free( &opt.subj_alt_names );
++#endif
+     mbedtls_x509write_crt_free( &crt );
+     mbedtls_pk_free( &loaded_subject_key );
+     mbedtls_pk_free( &loaded_issuer_key );
+diff --git a/tests/data_files/server1-bothnames.crt b/tests/data_files/server1-bothnames.crt
+new file mode 100644
+index 0000000..b7e1f40
+--- /dev/null
++++ b/tests/data_files/server1-bothnames.crt
+@@ -0,0 +1,22 @@
++-----BEGIN CERTIFICATE-----
++MIIDoTCCAomgAwIBAgIBATANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER
++MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
++MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G
++A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN
++BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/
++uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD
++d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf
++CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr
++lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w
++bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB
++o4GuMIGrMAkGA1UdEwQCMAAwHQYDVR0OBBYEFB901j8pwXR0RTsFEiw9qL1DWQKm
++MB8GA1UdIwQYMBaAFLRa5KWz3tJS9rnVppUP6z68x/3/MF4GA1UdEQRXMFWkPzA9
++MQswCQYDVQQGEwJOTDERMA8GA1UEChMIUG9sYXJTU0wxGzAZBgNVBAMTElBvbGFy
++U1NMIFNlcnZlciAxQYISb3RoZXIucG9sYXJzc2wub3JnMA0GCSqGSIb3DQEBCwUA
++A4IBAQCUqTN1WjWPMNpflyDTOEq+DEbNgj83ghhB6cCxHCGNT12CpBvQDh4bj5QC
++c77x8kN5fwy+HO1QIwHhBtjLC0IUfKvJUaTbwvIDH1NotrxSg/ft3HG/ijbTrX9/
++kxDXDcHkOlNX4Y5cJnU7UBm81n7WInGvGqperXTIDgW5CEkQp2ErtxvSZkNtYe82
++0DwVIQwjbF5hxFxQPGEzM1AANMyzOYKNtg6S7ElbtU4NQqvcBNr3vaG5ipMrkf2u
++kfJZAs+bPo+UyuFBCRGmOMgOAUrK4WvvxtqKFZDdty+pyTiR25x55Upfum7jxeBt
++Nnu2TUYcY472fXbZA5Y7/gGnmmRe
++-----END CERTIFICATE-----
+diff --git a/tests/data_files/server1-directoryname.crt b/tests/data_files/server1-directoryname.crt
+new file mode 100644
+index 0000000..b220230
+--- /dev/null
++++ b/tests/data_files/server1-directoryname.crt
+@@ -0,0 +1,22 @@
++-----BEGIN CERTIFICATE-----
++MIIDjTCCAnWgAwIBAgIBATANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER
++MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
++MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G
++A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN
++BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/
++uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD
++d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf
++CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr
++lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w
++bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB
++o4GaMIGXMAkGA1UdEwQCMAAwHQYDVR0OBBYEFB901j8pwXR0RTsFEiw9qL1DWQKm
++MB8GA1UdIwQYMBaAFLRa5KWz3tJS9rnVppUP6z68x/3/MEoGA1UdEQRDMEGkPzA9
++MQswCQYDVQQGEwJOTDERMA8GA1UEChMIUG9sYXJTU0wxGzAZBgNVBAMTElBvbGFy
++U1NMIFNlcnZlciAxQTANBgkqhkiG9w0BAQsFAAOCAQEAYb6K1UU+6eUdfMD/vayr
++36VEOFd2SA2AxBgj2GKAB7IyKgT6bZTo6SyfJWtvWlp7Ofjc9nQ2iObNRqN1499t
++DmAb3IM9yXuG00GVVg5brC3RtX1w4KJci2z+R2FX+HPAJW5E2rlU7OIm0FfF2aV6
++0qhHeJSCwi8DkS6U6dQW3vmFXf4sEVtyRkY5Zh3pEcSb3mNpvxNdq1Ro8TRmOGHe
++HrHAWxgBTeUJ0Dv/TNn39FNyvzwvWDFFxhwgs4+43N7FuSM6XUA86mkwLAsrjfWx
++fKNjq7fpJX2dS08ISV/RismU6RGt/TW4PXrtvEIfy1+kaMDSt+ffz1C1cuSen7xz
++6w==
++-----END CERTIFICATE-----
+diff --git a/tests/data_files/server1-dnsname.crt b/tests/data_files/server1-dnsname.crt
+new file mode 100644
+index 0000000..f3bad52
+--- /dev/null
++++ b/tests/data_files/server1-dnsname.crt
+@@ -0,0 +1,21 @@
++-----BEGIN CERTIFICATE-----
++MIIDXjCCAkagAwIBAgIBATANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQGEwJOTDER
++MA8GA1UEChMIUG9sYXJTU0wxGTAXBgNVBAMTEFBvbGFyU1NMIFRlc3QgQ0EwHhcN
++MTEwMjEyMTQ0NDA2WhcNMjEwMjEyMTQ0NDA2WjA8MQswCQYDVQQGEwJOTDERMA8G
++A1UEChMIUG9sYXJTU0wxGjAYBgNVBAMTEVBvbGFyU1NMIFNlcnZlciAxMIIBIjAN
++BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQIfPUBq1VVTi/027oJlLhVhXom/
++uOhFkNvuiBZS0/FDUEeWEllkh2v9K+BG+XO+3c+S4ZFb7Wagb4kpeUWA0INq1UFD
++d185fAkER4KwVzlw7aPsFRkeqDMIR8EFQqn9TMO0390GH00QUUBncxMPQPhtgSVf
++CrFTxjB+FTms+Vruf5KepgVb5xOXhbUjktnUJAbVCSWJdQfdphqPPwkZvq1lLGTr
++lZvc/kFeF6babFtpzAK6FCwWJJxK3M3Q91Jnc/EtoCP9fvQxyi1wyokLBNsupk9w
++bp7OvViJ4lNZnm5akmXiiD8MlBmj3eXonZUT7Snbq3AS3FrKaxerUoJUsQIDAQAB
++o2wwajAJBgNVHRMEAjAAMB0GA1UdDgQWBBQfdNY/KcF0dEU7BRIsPai9Q1kCpjAf
++BgNVHSMEGDAWgBS0WuSls97SUva51aaVD+s+vMf9/zAdBgNVHREEFjAUghJvdGhl
++ci5wb2xhcnNzbC5vcmcwDQYJKoZIhvcNAQELBQADggEBAFTQDT3BP8qKss7yVjZM
++8RkFF9GuqgCYPWyyuixzxYU5OJzzo/g5ZQ37wfcOMIUA7Ygs0Pv/L0n5PzncJqwO
++mJpQRghbI4yp2qVsKpdx+SVTS9aDm/madjREbSzk/tXVskINbuaWQ/azKbB6MReu
++XXgcw/iROpKJl+WCVr0cy6EAWPd3QBNLYyoAKpMQT2HXfQT/AhEZJINSYrkJeHCt
++BgTGq8tvXE2OtcjTcmp+KIpkXA6A0laRx5up1xET9wDrdll5AEhXSXDzIEOwIBhJ
++3cExQNdWbO6JdjBi5Qaoq9o7ItQOFSTM6PvRC3MYRwN+WhFdb+4Kmvd5oIArRaHo
++MEM=
++-----END CERTIFICATE-----
+diff --git a/tests/data_files/server11-directoryname.crt b/tests/data_files/server11-directoryname.crt
+new file mode 100644
+index 0000000..d9e0a16
+--- /dev/null
++++ b/tests/data_files/server11-directoryname.crt
+@@ -0,0 +1,10 @@
++-----BEGIN CERTIFICATE-----
++MIIBVzCB/qADAgECAgkAkWvgYjFeWV0wCgYIKoZIzj0EAwIwEzERMA8GA1UEAwwI
++VGVzdENlcnQwHhcNMTYxMjEzMjMwNDM3WhcNMzAwODIyMjMwNDM3WjATMREwDwYD
++VQQDDAhUZXN0Q2VydDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOkrKfbStoGN
++oOUTet2ryyRul++FE++6vpEup83bHywiOO1a4JtDgzNJcjj8/uAKsECYQrI1T4Y/
++rpnYu8ff9VGjOzA5MDcGA1UdEQQwMC6kLDAqMRYwFAYDVQQLDA1OYW1lIEFzc2ln
++bmVyMRAwDgYDVQQDDAdNeSBSb2xlMAoGCCqGSM49BAMCA0gAMEUCIGdd/GGuoOXJ
++8Ipl3hy69gb35MmkwEQKuYrud+Qs5XfFAiEAsmEOP4KFZadL23XJfMfGuPn2MDLr
++G9lDDpiediVxGO0=
++-----END CERTIFICATE-----
+diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
+index 57155b8..7e754bb 100755
+--- a/tests/ssl-opt.sh
++++ b/tests/ssl-opt.sh
+@@ -2526,6 +2526,16 @@ run_test    "extKeyUsage cli: codeSign -> fail" \
+             -c "Processing of the Certificate handshake message failed" \
+             -C "Ciphersuite is TLS-"
++run_test    "extKeyUsage cli: codeSign(requested) -> OK " \
++            "$O_SRV -key data_files/server5.key \
++             -cert data_files/server5.eku-cs.crt" \
++            "$P_CLI debug_level=1 eku=client-codesign" \
++            0 \
++            -C "bad certificate (usage extensions)" \
++            -C "Processing of the Certificate handshake message failed" \
++            -c "Ciphersuite is TLS-"
++
++
+ # Tests for extendedKeyUsage, part 3: server-side checking of client cert
+ run_test    "extKeyUsage cli-auth: clientAuth -> OK" \
+@@ -2568,6 +2578,14 @@ run_test    "extKeyUsage cli-auth: codeSign -> fail (hard)" \
+             -s "bad certificate (usage extensions)" \
+             -s "Processing of the Certificate handshake message failed"
++run_test    "extKeyUsage cli-auth: codeSign(requested) -> OK" \
++            "$P_SRV debug_level=1 auth_mode=required eku=codesign-server" \
++            "$O_CLI -key data_files/server5.key \
++             -cert data_files/server5.eku-cs.crt" \
++            0 \
++            -S "bad certificate (usage extensions)" \
++            -S "Processing of the Certificate handshake message failed"
++
+ # Tests for DHM parameters loading
+ run_test    "DHM parameters: reference" \
+diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data
+index c829823..30fff78 100644
+--- a/tests/suites/test_suite_x509parse.data
++++ b/tests/suites/test_suite_x509parse.data
+@@ -122,6 +122,22 @@ X509 certificate v1 with extension
+ depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_X509_ALLOW_EXTENSIONS_NON_V3:MBEDTLS_SHA1_C
+ x509_cert_info:"data_files/cert_v1_with_ext.crt":"cert. version     \: 1\nserial number     \: BD\:ED\:44\:C7\:D2\:3E\:C2\:A4\nissuer name       \: C=XX, ST=XX, L=XX, O=XX, OU=XX, emailAddress=admin@identity-check.org, CN=identity-check.org\nsubject name      \: C=XX, ST=XX, L=XX, O=XX, OU=XX, emailAddress=admin@identity-check.org, CN=identity-check.org\nissued  on        \: 2013-07-04 16\:17\:02\nexpires on        \: 2014-07-04 16\:17\:02\nsigned using      \: RSA with SHA1\nRSA key size      \: 2048 bits\nsubject alt name  \: identity-check.org, www.identity-check.org\n"
++X509 Certificate information EC directoryName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECDSA_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server11-directoryname.crt":"cert. version     \: 3\nserial number     \: 91\:6B\:E0\:62\:31\:5E\:59\:5D\nissuer name       \: CN=TestCert\nsubject name      \: CN=TestCert\nissued  on        \: 2016-12-13 23\:04\:37\nexpires on        \: 2030-08-22 23\:04\:37\nsigned using      \: ECDSA with SHA256\nEC key size       \: 256 bits\nsubject alt name  \: directoryName=(OU=Name Assigner, CN=My Role)\n"
++
++X509 Certificate information dNSName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server1-dnsname.crt":"cert. version     \: 3\nserial number     \: 01\nissuer name       \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name      \: C=NL, O=PolarSSL, CN=PolarSSL Server 1\nissued  on        \: 2011-02-12 14\:44\:06\nexpires on        \: 2021-02-12 14\:44\:06\nsigned using      \: RSA with SHA-256\nRSA key size      \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name  \: other.polarssl.org\n"
++
++X509 Certificate information directoryName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server1-directoryname.crt":"cert. version     \: 3\nserial number     \: 01\nissuer name       \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name      \: C=NL, O=PolarSSL, CN=PolarSSL Server 1\nissued  on        \: 2011-02-12 14\:44\:06\nexpires on        \: 2021-02-12 14\:44\:06\nsigned using      \: RSA with SHA-256\nRSA key size      \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name  \: directoryName=(C=NL, O=PolarSSL, CN=PolarSSL Server 1A)\n"
++
++X509 Certificate information dNSName+directoryName subjectAltName
++depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_cert_info:"data_files/server1-bothnames.crt":"cert. version     \: 3\nserial number     \: 01\nissuer name       \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nsubject name      \: C=NL, O=PolarSSL, CN=PolarSSL Server 1\nissued  on        \: 2011-02-12 14\:44\:06\nexpires on        \: 2021-02-12 14\:44\:06\nsigned using      \: RSA with SHA-256\nRSA key size      \: 2048 bits\nbasic constraints \: CA=false\nsubject alt name  \: directoryName=(C=NL, O=PolarSSL, CN=PolarSSL Server 1A), other.polarssl.org\n"
++
+ X509 CRL information #1
+ depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_SHA1_C
+ mbedtls_x509_crl_info:"data_files/crl_expired.pem":"CRL version   \: 1\nissuer name   \: C=NL, O=PolarSSL, CN=PolarSSL Test CA\nthis update   \: 2011-02-20 10\:24\:19\nnext update   \: 2011-02-20 11\:24\:19\nRevoked certificates\:\nserial number\: 01 revocation date\: 2011-02-12 14\:44\:07\nserial number\: 03 revocation date\: 2011-02-12 14\:44\:07\nsigned using  \: RSA with SHA1\n"
+diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data
+index d4d2a98..57c2ad2 100644
+--- a/tests/suites/test_suite_x509write.data
++++ b/tests/suites/test_suite_x509write.data
+@@ -58,6 +58,18 @@ Certificate write check Server1 SHA1, version 1
+ depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C
+ x509_crt_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA1:0:0:MBEDTLS_X509_CRT_VERSION_1:"data_files/server1.v1.crt"
++Certificate write check Server1 SHA256 with dNSName
++depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_crt_subj_alt_name_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA256:0:0:-1:1:"data_files/server1-dnsname.crt"
++
++Certificate write check Server1 SHA256 with directoryName
++depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_crt_subj_alt_name_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA256:0:0:-1:2:"data_files/server1-directoryname.crt"
++
++Certificate write check Server1 SHA256 with dNSName and directoryName
++depends_on:MBEDTLS_SHA1_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC:MBEDTLS_MD5_C:MBEDTLS_SHA256_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT
++x509_crt_subj_alt_name_check:"data_files/server1.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=PolarSSL Test CA":"1":"20110212144406":"20210212144406":MBEDTLS_MD_SHA256:0:0:-1:3:"data_files/server1-bothnames.crt"
++
+ X509 String to Names #1
+ mbedtls_x509_string_to_names:"C=NL,O=Offspark\, Inc., OU=PolarSSL":"C=NL, O=Offspark, Inc., OU=PolarSSL":0
+diff --git a/tests/suites/test_suite_x509write.function b/tests/suites/test_suite_x509write.function
+index 89be31f..184ac2c 100644
+--- a/tests/suites/test_suite_x509write.function
++++ b/tests/suites/test_suite_x509write.function
+@@ -157,6 +157,131 @@ exit:
+ }
+ /* END_CASE */
++/* BEGIN_CASE depends_on:MBEDTLS_PEM_WRITE_C:MBEDTLS_X509_CRT_WRITE_C:MBEDTLS_SHA1_C:MBEDTLS_X509_EXPANDED_SUBJECT_ALT_NAME_SUPPORT */
++void x509_crt_subj_alt_name_check( char *subject_key_file, char *subject_pwd,
++                                   char *subject_name, char *issuer_key_file,
++                                   char *issuer_pwd, char *issuer_name,
++                                   char *serial_str, char *not_before, char *not_after,
++                                   int md_type, int key_usage, int cert_type, int ver, int subj_alt_names_type,
++                                   char *cert_check_file )
++{
++    mbedtls_pk_context subject_key, issuer_key;
++    mbedtls_x509write_cert crt;
++    unsigned char buf[4096];
++    unsigned char check_buf[5000];
++    mbedtls_mpi serial;
++    int ret;
++    size_t olen = 0, pem_len = 0;
++    int der_len = -1;
++    FILE *f;
++    rnd_pseudo_info rnd_info;
++
++    memset( &rnd_info, 0x2a, sizeof( rnd_pseudo_info ) );
++    mbedtls_mpi_init( &serial );
++    mbedtls_pk_init( &subject_key );
++    mbedtls_pk_init( &issuer_key );
++
++    TEST_ASSERT( mbedtls_pk_parse_keyfile( &subject_key, subject_key_file,
++                                         subject_pwd ) == 0 );
++    TEST_ASSERT( mbedtls_pk_parse_keyfile( &issuer_key, issuer_key_file,
++                                         issuer_pwd ) == 0 );
++    TEST_ASSERT( mbedtls_mpi_read_string( &serial, 10, serial_str ) == 0 );
++
++    mbedtls_x509write_crt_init( &crt );
++    if( ver != -1 )
++        mbedtls_x509write_crt_set_version( &crt, ver );
++    TEST_ASSERT( mbedtls_x509write_crt_set_serial( &crt, &serial ) == 0 );
++    TEST_ASSERT( mbedtls_x509write_crt_set_validity( &crt, not_before,
++                                                   not_after ) == 0 );
++    mbedtls_x509write_crt_set_md_alg( &crt, md_type );
++    TEST_ASSERT( mbedtls_x509write_crt_set_issuer_name( &crt, issuer_name ) == 0 );
++    TEST_ASSERT( mbedtls_x509write_crt_set_subject_name( &crt, subject_name ) == 0 );
++    mbedtls_x509write_crt_set_subject_key( &crt, &subject_key );
++    mbedtls_x509write_crt_set_issuer_key( &crt, &issuer_key );
++
++    if( crt.version >= MBEDTLS_X509_CRT_VERSION_3 )
++    {
++        TEST_ASSERT( mbedtls_x509write_crt_set_basic_constraints( &crt, 0, 0 ) == 0 );
++        TEST_ASSERT( mbedtls_x509write_crt_set_subject_key_identifier( &crt ) == 0 );
++        TEST_ASSERT( mbedtls_x509write_crt_set_authority_key_identifier( &crt ) == 0 );
++        if( key_usage != 0 )
++            TEST_ASSERT( mbedtls_x509write_crt_set_key_usage( &crt, key_usage ) == 0 );
++        if( cert_type != 0 )
++            TEST_ASSERT( mbedtls_x509write_crt_set_ns_cert_type( &crt, cert_type ) == 0 );
++        if( subj_alt_names_type != 0 )
++        {
++            mbedtls_x509_general_names dns_name, directory_name;
++            mbedtls_x509_general_names *first = NULL;
++
++            memset( &dns_name, 0, sizeof( dns_name ) );
++            memset( &directory_name, 0, sizeof( directory_name ) );
++
++            if ( ( subj_alt_names_type & 1 ) == 1 )
++            {
++                dns_name.general_name.name_type = MBEDTLS_X509_GENERALNAME_DNSNAME;
++                /* Nothing will try to modify the memory pointed to by p, so this cast is safe. */
++                dns_name.general_name.dns_name.p = (unsigned char *) "other.polarssl.org";
++                dns_name.general_name.dns_name.len = strlen( (const char *) dns_name.general_name.dns_name.p );
++                first = &dns_name;
++            }
++
++            if ( ( subj_alt_names_type & 2 ) == 2 )
++            {
++                directory_name.general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
++                TEST_ASSERT( mbedtls_x509_string_to_names( &directory_name.general_name.directory_name, "C=NL,O=PolarSSL,CN=PolarSSL Server 1A" ) == 0 );
++                if ( first == NULL )
++                {
++                    first = &directory_name;
++                }
++                else
++                {
++                    first->next = &directory_name;
++                }
++            }
++
++            TEST_ASSERT( mbedtls_x509write_crt_set_subject_alt_names( &crt, first ) == 0 );
++
++            if ( ( subj_alt_names_type & 2 ) == 2 )
++            {
++                mbedtls_asn1_free_named_data_list( &directory_name.general_name.directory_name );
++            }
++        }
++    }
++
++    ret = mbedtls_x509write_crt_pem( &crt, buf, sizeof(buf),
++                             rnd_pseudo_rand, &rnd_info );
++    TEST_ASSERT( ret == 0 );
++
++    pem_len = strlen( (char *) buf );
++
++    f = fopen( cert_check_file, "r" );
++    TEST_ASSERT( f != NULL );
++    olen = fread( check_buf, 1, sizeof(check_buf), f );
++    fclose( f );
++    TEST_ASSERT( olen < sizeof(check_buf) );
++
++    TEST_ASSERT( olen >= pem_len - 1 );
++    TEST_ASSERT( memcmp( buf, check_buf, pem_len - 1 ) == 0 );
++
++    der_len = mbedtls_x509write_crt_der( &crt, buf, sizeof( buf ),
++                            rnd_pseudo_rand, &rnd_info );
++    TEST_ASSERT( der_len >= 0 );
++
++    if( der_len == 0 )
++        goto exit;
++
++    ret = mbedtls_x509write_crt_der( &crt, buf, (size_t)( der_len - 1 ),
++                            rnd_pseudo_rand, &rnd_info );
++    TEST_ASSERT( ret == MBEDTLS_ERR_ASN1_BUF_TOO_SMALL );
++
++exit:
++    mbedtls_x509write_crt_free( &crt );
++    mbedtls_pk_free( &issuer_key );
++    mbedtls_pk_free( &subject_key );
++    mbedtls_mpi_free( &serial );
++}
++/* END_CASE */
++
+ /* BEGIN_CASE depends_on:MBEDTLS_X509_CREATE_C:MBEDTLS_X509_USE_C */
+ void mbedtls_x509_string_to_names( char *name, char *parsed_name, int result )
+ {