Skip to content

Commit

Permalink
SSL: NTLS support.
Browse files Browse the repository at this point in the history
NTLS (short for 'National Security'), is a term used to refer to various
Chinese security-related standards:
    - ShangMi (SM) Cipher Suites for TLS 1.3 (RFC 8998) (aka SM2,3,4) [1]
    - TLCP protocol (2020) [2]
    - GM/T 0024 "SSL VPN Technical Specifications" (2012)

Related algorithms are implemented in the TongSuo library [3],
fork of the OpenSSL.  API is documented in [4].

The notable feature of NTLS is that the digital certificate used in the TLS
protocol is divided into two types of certificates for encryption and
signature, thus feature is often referred as "dual certificates".

This patch allows Angie to use dual certificates exploiting SM2,3,4 features
and enable NTLS for SSL connections.

The patch adds server-side support to the ngx_http_ssl_module and client-side
support to the ngx_http_proxy_module.

The ssl_certificate, ssl_certificate_key and their proxy_ counterparts
directives now accept two arguments instead of one: sign and encryption
parts of certificate/key. The functionality must be enabled with
"ssl_ntls" and/or "proxy_ssl_ntls" directives.

Example configuration:

    listen ... ssl;

    ssl_ntls  on;

    # dual NTLS certificate
    ssl_certificate      sign.crt enc.crt;
    ssl_certificate_key  sign.key enc.key;

    # can be combined with regular RSA certificate:
    ssl_certificate  rsa.crt;
    ssl_certificate  rsa.key;

    location /proxy {
        proxy_ssl_ntls  on;

        proxy_ssl_certificate      sign.crt enc.crt;
        proxy_ssl_certificate_key  sign.key enc.key;

        proxy_ssl_ciphers "ECC-SM2-WITH-SM4-SM3:ECDHE-SM2-WITH-SM4-SM3:RSA";

        proxy_pass https://backend:443;
    }

Build Angie using the '--with-ntls' configure option and link with
NTLS-enabled SSL library:

./configure --with-openssl=../Tongsuo-8.3.0 \
            --with-openssl-opt=enable-ntls  \
            --with-ntls

[1] https://datatracker.ietf.org/doc/html/rfc8998
[2] https://www.chinesestandard.net/PDF/English.aspx/GBT38636-2020
[3] https://github.com/Tongsuo-Project/Tongsuo
[4] https://babassl.readthedocs.io/zh/latest/Tutorial/SM/ntls/
  • Loading branch information
vlhomutov committed May 26, 2023
1 parent 67a8634 commit 05a23cc
Show file tree
Hide file tree
Showing 12 changed files with 957 additions and 1 deletion.
6 changes: 6 additions & 0 deletions auto/modules
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,12 @@ if [ $USE_API = YES ]; then
fi


if [ $USE_NTLS = YES ]; then
have=NGX_HTTP_PROXY_MULTICERT . auto/have
have=NGX_HAVE_NTLS . auto/have
fi


modules="$CORE_MODULES $EVENT_MODULES"


Expand Down
6 changes: 6 additions & 0 deletions auto/options
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ DYNAMIC_ADDONS=

USE_API=NO

USE_NTLS=NO

NGX_COMPAT=NO

USE_PCRE=NO
Expand Down Expand Up @@ -360,6 +362,8 @@ use the \"--with-mail_ssl_module\" option instead"

--with-compat) NGX_COMPAT=YES ;;

--with-ntls) USE_NTLS=YES ;;

--with-cc=*) CC="$value" ;;
--with-cpp=*) CPP="$value" ;;
--with-cc-opt=*) NGX_CC_OPT="$value" ;;
Expand Down Expand Up @@ -582,6 +586,8 @@ cat << END

--with-compat dynamic modules compatibility

--with-ntls enable NTLS support

--with-cc=PATH set C compiler pathname
--with-cpp=PATH set C preprocessor pathname
--with-cc-opt=OPTIONS set additional C compiler options
Expand Down
26 changes: 26 additions & 0 deletions docs/xml/angie/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,32 @@ the session are routed to the same server.
</para>
</change>

<change type="feature">
<para lang="ru">
поддержка NTLS в HTTP SSL и HTTP proxy модулях при использовании библиотеки
TongSuo, которую можно включить опцией сборки "--with-ntls" и сконфигурировать
с помощью соответствующих директив "ssl_ntls" и "proxy_ssl_ntls".
</para>
<para lang="en">
support for NTLS in the HTTP SSL and HTTP proxy modules using TongSuo library,
that can be enabled via the "--with-ntls" build time option and configured
with the "ssl_ntls" and "proxy_ssl_ntls" corresponding directives.
</para>
</change>

<change type="feature">
<para lang="ru">
в HTTP proxy модуле теперь можно настраивать несколько сертификатов разного
типа (RSA и ECDSA) и соответствующих им ключей, используя директивы
"proxy_ssl_certificate" и "proxy_ssl_certificate_key".
</para>
<para lang="en">
in HTTP proxy module ability to specify multiple certificates with different
types (RSA and ECDSA) and corresponding keys, using the "proxy_ssl_certificate"
and "proxy_ssl_certificate_key" directives.
</para>
</change>

<change type="feature">
<para lang="ru">
возможность сжатия модулем gzip ответов со статусом "207 Multi-Status".
Expand Down
178 changes: 178 additions & 0 deletions src/event/ngx_event_openssl.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

/*
* Copyright (C) 2023 Web Server LLC
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
Expand All @@ -12,12 +13,23 @@

#define NGX_SSL_PASSWORD_BUFFER_SIZE 4096

#if (NGX_HAVE_NTLS)

#define NGX_SSL_NTLS_CERT_REGULAR 0
#define NGX_SSL_NTLS_CERT_SIGN 1
#define NGX_SSL_NTLS_CERT_ENC 2

#endif


typedef struct {
ngx_uint_t engine; /* unsigned engine:1; */
} ngx_openssl_conf_t;


#if (NGX_HAVE_NTLS)
static ngx_uint_t ngx_ssl_ntls_type(ngx_str_t *s);
#endif
static X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,
ngx_str_t *cert, STACK_OF(X509) **chain);
static EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
Expand Down Expand Up @@ -424,6 +436,9 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
X509 *x509;
EVP_PKEY *pkey;
STACK_OF(X509) *chain;
#if (NGX_HAVE_NTLS)
ngx_uint_t type;
#endif

x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);
if (x509 == NULL) {
Expand All @@ -436,6 +451,35 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
return NGX_ERROR;
}

#if (NGX_HAVE_NTLS)
type = ngx_ssl_ntls_type(cert);

if (type == NGX_SSL_NTLS_CERT_SIGN) {

if (SSL_CTX_use_sign_certificate(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_sign_certificate(\"%s\") failed",
cert->data);
X509_free(x509);
sk_X509_pop_free(chain, X509_free);
return NGX_ERROR;
}

} else if (type == NGX_SSL_NTLS_CERT_ENC) {

if (SSL_CTX_use_enc_certificate(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_enc_certificate(\"%s\") failed",
cert->data);
X509_free(x509);
sk_X509_pop_free(chain, X509_free);
return NGX_ERROR;
}

} else

#endif

if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_certificate(\"%s\") failed", cert->data);
Expand Down Expand Up @@ -524,6 +568,33 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
return NGX_ERROR;
}

#if (NGX_HAVE_NTLS)
type = ngx_ssl_ntls_type(key);

if (type == NGX_SSL_NTLS_CERT_SIGN) {

if (SSL_CTX_use_sign_PrivateKey(ssl->ctx, pkey) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_sign_PrivateKey(\"%s\") failed",
key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}

} else if (type == NGX_SSL_NTLS_CERT_ENC) {

if (SSL_CTX_use_enc_PrivateKey(ssl->ctx, pkey) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_enc_PrivateKey(\"%s\") failed",
key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}

} else

#endif

if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {
ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
"SSL_CTX_use_PrivateKey(\"%s\") failed", key->data);
Expand All @@ -537,6 +608,40 @@ ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
}


#if (NGX_HAVE_NTLS)

static ngx_uint_t
ngx_ssl_ntls_type(ngx_str_t *s)
{
if (ngx_strncmp(s->data, "sign:", sizeof("sign:") - 1) == 0) {

return NGX_SSL_NTLS_CERT_SIGN;

} else if (ngx_strncmp(s->data, "enc:", sizeof("enc:") - 1) == 0) {

return NGX_SSL_NTLS_CERT_ENC;
}

return NGX_SSL_NTLS_CERT_REGULAR;
}


void
ngx_ssl_ntls_prefix_strip(ngx_str_t *s)
{
if (ngx_strncmp(s->data, "sign:", sizeof("sign:") - 1) == 0) {
s->data += sizeof("sign:") - 1;
s->len -= sizeof("sign:") - 1;

} else if (ngx_strncmp(s->data, "enc:", sizeof("enc:") - 1) == 0) {
s->data += sizeof("enc:") - 1;
s->len -= sizeof("enc:") - 1;
}
}

#endif


ngx_int_t
ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords)
Expand All @@ -545,6 +650,9 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
X509 *x509;
EVP_PKEY *pkey;
STACK_OF(X509) *chain;
#if (NGX_HAVE_NTLS)
ngx_uint_t type;
#endif

x509 = ngx_ssl_load_certificate(pool, &err, cert, &chain);
if (x509 == NULL) {
Expand All @@ -557,6 +665,35 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
return NGX_ERROR;
}

#if (NGX_HAVE_NTLS)
type = ngx_ssl_ntls_type(cert);

if (type == NGX_SSL_NTLS_CERT_SIGN) {

if (SSL_use_sign_certificate(c->ssl->connection, x509) == 0) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_sign_certificate(\"%s\") failed",
cert->data);
X509_free(x509);
sk_X509_pop_free(chain, X509_free);
return NGX_ERROR;
}

} else if (type == NGX_SSL_NTLS_CERT_ENC) {

if (SSL_use_enc_certificate(c->ssl->connection, x509) == 0) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_enc_certificate(\"%s\") failed",
cert->data);
X509_free(x509);
sk_X509_pop_free(chain, X509_free);
return NGX_ERROR;
}

} else

#endif

if (SSL_use_certificate(c->ssl->connection, x509) == 0) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_certificate(\"%s\") failed", cert->data);
Expand Down Expand Up @@ -595,6 +732,31 @@ ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
return NGX_ERROR;
}

#if (NGX_HAVE_NTLS)
type = ngx_ssl_ntls_type(key);

if (type == NGX_SSL_NTLS_CERT_SIGN) {

if (SSL_use_sign_PrivateKey(c->ssl->connection, pkey) == 0) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_sign_PrivateKey(\"%s\") failed", key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}

} else if (type == NGX_SSL_NTLS_CERT_ENC) {

if (SSL_use_enc_PrivateKey(c->ssl->connection, pkey) == 0) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_enc_PrivateKey(\"%s\") failed", key->data);
EVP_PKEY_free(pkey);
return NGX_ERROR;
}

} else

#endif

if (SSL_use_PrivateKey(c->ssl->connection, pkey) == 0) {
ngx_ssl_error(NGX_LOG_ERR, c->log, 0,
"SSL_use_PrivateKey(\"%s\") failed", key->data);
Expand All @@ -616,6 +778,14 @@ ngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,
X509 *x509, *temp;
u_long n;

#if (NGX_HAVE_NTLS)
ngx_str_t tcert;

tcert = *cert;
ngx_ssl_ntls_prefix_strip(&tcert);
cert = &tcert;
#endif

if (ngx_strncmp(cert->data, "data:", sizeof("data:") - 1) == 0) {

bio = BIO_new_mem_buf(cert->data + sizeof("data:") - 1,
Expand Down Expand Up @@ -708,6 +878,14 @@ ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,
ngx_uint_t tries;
pem_password_cb *cb;

#if (NGX_HAVE_NTLS)
ngx_str_t tkey;

tkey = *key;
ngx_ssl_ntls_prefix_strip(&tkey);
key = &tkey;
#endif

if (ngx_strncmp(key->data, "engine:", sizeof("engine:") - 1) == 0) {

#ifndef OPENSSL_NO_ENGINE
Expand Down
4 changes: 4 additions & 0 deletions src/event/ngx_event_openssl.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

/*
* Copyright (C) 2023 Web Server LLC
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
Expand Down Expand Up @@ -197,6 +198,9 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
ngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,
ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);
#if (NGX_HAVE_NTLS)
void ngx_ssl_ntls_prefix_strip(ngx_str_t *s);
#endif

ngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,
ngx_uint_t prefer_server_ciphers);
Expand Down
Loading

0 comments on commit 05a23cc

Please sign in to comment.