Make cryptoapicert work with TLS 1.2
cover Make cryptoapicert work with TLS 1.2
From: Selva Nair <[email protected]...>
Hi,
I am not sure how receptive the crypto maintaineres are to the idea of adding more code into cryptoapi.c, but here goes:
I've been wanting to add TLS 1.2 support for certs in the Windows cert store using management external key. But that's a lot more work than extending cryptoapicert support. And, rather surprsingly, it turns out that the CNG API for signing is easy to use (well after some groping in the dark..) and doesn't take much to implement.
So these patches..
The first patch is not really related and to make the existing code "openssl-1.1 ready" (missed by past patches as no one probably builds Windows binary with 1.1..).
The second patch is not dependent on this, but close-by code paths are touched by both.
Selva
Selva Nair (2):
Bring cryptoapi.c upto speed with openssl 1.1
TLS v1.2 support for cryptoapicert -- RSA only
configure.ac | 1 + src/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 155 ++++++++++++++++++++++++++++++++++--------- src/openvpn/openssl_compat.h | 14 ++++ src/openvpn/options.c | 18 ----- 5 files changed, 140 insertions(+), 50 deletions(-)
Signature · 1 line
2.1.4
PATCH 1/2 Bring cryptoapi.c upto speed with openssl 1.1
From: Selva Nair <[email protected]...>
- Replace direct access to internals of openssl structs by corresponding methods.
---
Tested on Windows 10 with openssl 1.0.1r and 1.1.0g
configure.ac | 1 + src/openvpn/cryptoapi.c | 69 +++++++++++++++++++++++++++----------------- src/openvpn/openssl_compat.h | 14 +++++++++ 3 files changed, 57 insertions(+), 27 deletions(-)
configure.ac +1 -0
@@ -944,6 +944,7 @@ if test "${with_crypto_library}" = "openssl"; then RSA_meth_set_init \ RSA_meth_set_finish \ RSA_meth_set0_app_data \ + RSA_meth_get0_app_data \ EC_GROUP_order_bits ] )
src/openvpn/cryptoapi.c +42 -27
152 lines not on this page · raw message
src/openvpn/openssl_compat.h +14 -0
@@ -624,6 +624,20 @@ RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) }#endif +#if !defined(HAVE_RSA_METH_GET0_APP_DATA ) +/** +* Get the application data of an RSA_METHOD object +* +* @param meth The RSA_METHOD object +* @return pointer to application data, may be NULL +*/ +static inline void * +RSA_meth_get0_app_data (const RSA_METHOD *meth) +{ +return meth ? meth->app_data :NULL ; +} +#endif +#if !defined(HAVE_EC_GROUP_ORDER_BITS ) && !defined(OPENSSL_NO_EC )/** * Gets the number of bits of the order of an EC_GROUP
Signature · 1 line
2.1.4
Re: [Openvpn-devel] [PATCH 1/2] Bring cryptoapi.c upto speed with openssl 1.1
Hi,
Quoted 169 lines · On 08-01-18 03:21, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- Replace direct access to internals of openssl structs by corresponding methods.
Signed-off-by Selva Nair <[email protected]...>---
Tested on Windows 10 with openssl 1.0.1r and 1.1.0gconfigure.ac | 1 + src/openvpn/cryptoapi.c | 69 +++++++++++++++++++++++++++----------------- src/openvpn/openssl_compat.h | 14 +++++++++ 3 files changed, 57 insertions(+), 27 deletions(-)configure.ac +1
-0@@ -944,6 +944,7 @@ if test "${with_crypto_library}" = "openssl"; then RSA_meth_set_init \ RSA_meth_set_finish \ RSA_meth_set0_app_data \ + RSA_meth_get0_app_data \ EC_GROUP_order_bits ] )src/openvpn/cryptoapi.c +26
-23@@ -47,6 +47,7 @@#include <assert.h> #include "buffer.h" +#include "openssl_compat.h" /* MinGW w32api 3.17 is still incomplete when it comes to CryptoAPI while * MinGW32-w64 defines all macros used. This is a hack around that problem. @@ -213,20 +214,20 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, instatic int rsa_priv_enc (int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding) { -CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; +CAPI_DATA *cd = (CAPI_DATA *)RSA_meth_get0_app_data (RSA_get_method (rsa));HCRYPTHASH hash;DWORD hash_size, len, i;unsigned char *buf;if (cd ==NULL ) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,ERR_R_PASSED_NULL_PARAMETER ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,ERR_R_PASSED_NULL_PARAMETER );return 0 ; }if (padding !=RSA_PKCS1_PADDING ) {/* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */ -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE );return 0 ; }/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would @@ -236,7 +237,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i/* For now, we only support NID_md5_sha1 */ if (flen !=SSL_SIG_LENGTH ) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH );return 0 ; }if (!CryptCreateHash (cd->crypt_prov,CALG_SSL3_SHAMD5 ,0 ,0 , &hash))@@ -253,7 +254,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i }if ((int ) hash_size != flen) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH );CryptDestroyHash (hash);return 0 ; }@@ -268,7 +269,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i buf =malloc (len);if (buf ==NULL ) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,ERR_R_MALLOC_FAILURE ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,ERR_R_MALLOC_FAILURE );CryptDestroyHash (hash);return 0 ; }@@ -312,7 +313,8 @@ init(RSA *rsa)static int finish (RSA *rsa) { -CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; +const RSA_METHOD *rsa_meth =RSA_get_method (rsa); +CAPI_DATA *cd = (CAPI_DATA *)RSA_meth_get0_app_data (rsa_meth);if (cd ==NULL ) {@@ -326,9 +328,8 @@ finish(RSA *rsa) {CertFreeCertificateContext (cd->cert_context); } -free (rsa->meth->app_data); -free ((char *) rsa->meth); - rsa->meth =NULL ; +free (cd); +RSA_meth_free ((RSA_METHOD *) rsa_meth);return 1 ; }@@ -412,9 +413,9 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)X509 *cert =NULL ;RSA *rsa =NULL , *pub_rsa;CAPI_DATA *cd =calloc (1 ,sizeof (*cd)); -RSA_METHOD *my_rsa_method =calloc (1 ,sizeof (*my_rsa_method)); +RSA_METHOD *my_rsa_method =NULL ; -if (cd ==NULL || my_rsa_method ==NULL ) +if (cd ==NULL ) {SSLerr (SSL_F_SSL_CTX_USE_CERTIFICATE_FILE ,ERR_R_MALLOC_FAILURE ); goto err;@@ -469,15 +470,16 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)/* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ - my_rsa_method->name ="Microsoft CryptoAPI RSA Method" ; - my_rsa_method->rsa_pub_enc = rsa_pub_enc; - my_rsa_method->rsa_pub_dec = rsa_pub_dec; - my_rsa_method->rsa_priv_enc = rsa_priv_enc; - my_rsa_method->rsa_priv_dec = rsa_priv_dec; -/* my_rsa_method->init = init; */ - my_rsa_method->finish = finish; - my_rsa_method->flags =RSA_METHOD_FLAG_NO_CHECK ; - my_rsa_method->app_data = (char *) cd; + my_rsa_method =RSA_meth_new ("Microsoft Cryptography API RSA Method" , +RSA_METHOD_FLAG_NO_CHECK ); +check_malloc_return (my_rsa_method); +RSA_meth_set_pub_enc (my_rsa_method, rsa_pub_enc); +RSA_meth_set_pub_dec (my_rsa_method, rsa_pub_dec); +RSA_meth_set_priv_enc (my_rsa_method, rsa_priv_enc); +RSA_meth_set_priv_dec (my_rsa_method, rsa_priv_dec); +RSA_meth_set_init (my_rsa_method,NULL ); +RSA_meth_set_finish (my_rsa_method, finish); +RSA_meth_set0_app_data (my_rsa_method, cd); rsa =RSA_new ();if (rsa ==NULL )@@ -486,23 +488,36 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) goto err; } -/* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(), +/* Public key in cert is NULL until we call SSL_CTX_use_certificate(), * so we do it here then... */ if (!SSL_CTX_use_certificate (ssl_ctx, cert)) { goto err; }/* the public key */ - pub_rsa = cert->cert_info->key->pkey->pkey.rsa; +EVP_PKEY *pkey =X509_get0_pubkey (cert); +/* SSL_CTX_use_certificate() increased the reference count in 'cert', so * we decrease it here with X509_free(), or it will never be cleaned up. */ X509_free (cert); cert =NULL ;
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 510
Quoted 10 lines
src/openvpn/cryptoapi.c +6
-4-/* I'm not sure about what we have to fill in in the RSA, trying out stuff... */ -/* rsa->n indicates the key size */ - rsa->n =BN_dup (pub_rsa->n); - rsa->flags |=RSA_FLAG_EXT_PKEY ; +if (EVP_PKEY_id (pkey) !=EVP_PKEY_RSA ) + { +msg (M_WARN ,"cryptoapicert requires an RSA certificate" ); + goto err; + } + pub_rsa =EVP_PKEY_get0_RSA (pkey);
This conflicts with the patch set from Emmanuel, where he removes EVP_PKEY_id(). Canbe easily resolved by changing the if to
if (!(pub_rsa = EVP_PKEY_get0_RSA))
... but only once the NULL-check patch for openssl_compat.h is applied.
Quoted 29 lines
src/openvpn/cryptoapi.c +10
-0+ +/* Our private key is external, so we fill in only n and e from the public key */ +const BIGNUM *n =NULL ; +const BIGNUM *e =NULL ; +RSA_get0_key (pub_rsa, &n, &e,NULL ); +if (!RSA_set0_key (rsa,BN_dup (n),BN_dup (e),NULL )) + { + goto err; + } +RSA_set_flags (rsa,RSA_flags (rsa) |RSA_FLAG_EXT_PKEY );if (!RSA_set_method (rsa, my_rsa_method)) { goto err;src/openvpn/openssl_compat.h +7
-0@@ -624,6 +624,20 @@ RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) }#endif +#if !defined(HAVE_RSA_METH_GET0_APP_DATA ) +/** +* Get the application data of an RSA_METHOD object +* +* @param meth The RSA_METHOD object +* @return pointer to application data, may be NULL +*/
src/openvpn/openssl_compat.h RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) · line 643
Quoted 10 lines
src/openvpn/openssl_compat.h +7
-0+static inline void * +RSA_meth_get0_app_data (const RSA_METHOD *meth) +{ +return meth ? meth->app_data :NULL ; +} +#endif +#if !defined(HAVE_EC_GROUP_ORDER_BITS ) && !defined(OPENSSL_NO_EC )/** * Gets the number of bits of the order of an EC_GROUP
Otherwise this looks good (does exactly the same we've done before for management-external-key), and compiles fine for both OpenSSL 1.0.1 and 1.1.0.
I haven't tested the resulting binary on Windows myself, because I ran out of time for today. So testing volunteers are welcome :)
-Steffan
Re: [Openvpn-devel] [PATCH 1/2] Bring cryptoapi.c upto speed with openssl 1.1
Hi,
Quoted 18 lines · Steffan Karger
Hi,
Quoted 15 lines · On 08-01-18 03:21, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- Replace direct access to internals of openssl structs by corresponding methods.
Signed-off-by Selva Nair <[email protected]...>---
Tested on Windows 10 with openssl 1.0.1r and 1.1.0gconfigure.ac | 1 + src/openvpn/cryptoapi.c | 69 +++++++++++++++++++++++++++----------------- src/openvpn/openssl_compat.h | 14 +++++++++ 3 files changed, 57 insertions(+), 27 deletions(-)
..
Quoted 9 lines
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 510
Quoted 8 lines
src/openvpn/cryptoapi.c +6
-2- rsa->n =BN_dup (pub_rsa->n); - rsa->flags |=RSA_FLAG_EXT_PKEY ; +if (EVP_PKEY_id (pkey) !=EVP_PKEY_RSA ) + { +msg (M_WARN ,"cryptoapicert requires an RSA certificate" ); + goto err; + } + pub_rsa =EVP_PKEY_get0_RSA (pkey);
Quoted 6 lines
This conflicts with the patch set from Emmanuel, where he removes
EVP_PKEY_id(). Canbe easily resolved by changing the if toif (!(pub_rsa = EVP_PKEY_get0_RSA))... but only once the NULL-check patch for
openssl_compat.his applied.
I'll send a v2 assuming that patch will get merged eventually.
I would have preferred to have EVP_PKEY_id() retained in the compat layer as its much more convenient to use it when multiple key types are to be differentiated.. (read as: I want to support EC certs here).
Thanks for the review.
Selva
Re: [Openvpn-devel] [PATCH 1/2] Bring cryptoapi.c upto speed with openssl 1.1
Hi,
Quoted 33 lines · Selva Nair
Quoted 16 lines · Steffan Karger
Quoted 15 lines · On 08-01-18 03:21, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- Replace direct access to internals of openssl structs by corresponding methods.
Signed-off-by Selva Nair <[email protected]...>---
Tested on Windows 10 with openssl 1.0.1r and 1.1.0gconfigure.ac | 1 + src/openvpn/cryptoapi.c | 69 +++++++++++++++++++++++++++----------------- src/openvpn/openssl_compat.h | 14 +++++++++ 3 files changed, 57 insertions(+), 27 deletions(-)..
Quoted 14 lines
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 510
Quoted 8 lines
src/openvpn/cryptoapi.c +6
-2- rsa->n =BN_dup (pub_rsa->n); - rsa->flags |=RSA_FLAG_EXT_PKEY ; +if (EVP_PKEY_id (pkey) !=EVP_PKEY_RSA ) + { +msg (M_WARN ,"cryptoapicert requires an RSA certificate" ); + goto err; + } + pub_rsa =EVP_PKEY_get0_RSA (pkey);This conflicts with the patch set from Emmanuel, where he removes
EVP_PKEY_id(). Canbe easily resolved by changing the if toif (!(pub_rsa = EVP_PKEY_get0_RSA))
Quoted 7 lines
Quoted 1 line
... but only once the NULL-check patch for
openssl_compat.his applied.I'll send a v2 assuming that patch will get merged eventually.
I would have preferred to have
EVP_PKEY_id()retained in the compat layer as its much more convenient to use it when multiple key types are to be differentiated.. (read as: I want to support EC certs here).
That should still be possible just fine, see e.g. how Emmanuel does that in "OpenSSL: remove some EVP_PKEY type checks".
-Steffan
Re: [Openvpn-devel] [PATCH 1/2] Bring cryptoapi.c upto speed with openssl 1.1
In reply to Steffan KargerMoin,
Quoted 6 lines · Steffan Karger
This conflicts with the patch set from Emmanuel, where he removes
EVP_PKEY_id(). Canbe easily resolved by changing the if toif (!(pub_rsa = EVP_PKEY_get0_RSA))... but only once the NULL-check patch for
openssl_compat.his applied.
"go for it" :-)
gert
Signature · 3 lines
now what should I write here... Gert Doering - Munich, Germany [email protected]...
PATCH v2 1/2 Bring cryptoapi.c upto speed with openssl 1.1
In reply to Steffan KargerFrom: Selva Nair <[email protected]...>
- Replace direct access to internals of openssl structs by corresponding methods.
v2: Remove the call to EVP_PKEY_id() as its slated for removal
from the compat layer (see also review by Stefan)
---
Freeing rsa_method in this code path requires more fixup than just use of RSA_meth_free (not freed at in n some errors etc.), so will be addressed in a separate patch.
configure.ac | 1 + src/openvpn/cryptoapi.c | 68 ++++++++++++++++++++++++++------------------ src/openvpn/openssl_compat.h | 14 +++++++++ 3 files changed, 56 insertions(+), 27 deletions(-)
configure.ac +1 -0
@@ -944,6 +944,7 @@ if test "${with_crypto_library}" = "openssl"; then RSA_meth_set_init \ RSA_meth_set_finish \ RSA_meth_set0_app_data \ + RSA_meth_get0_app_data \ EC_GROUP_order_bits ] )
src/openvpn/cryptoapi.c +41 -27
151 lines not on this page · raw message
src/openvpn/openssl_compat.h +14 -0
@@ -624,6 +624,20 @@ RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) }#endif +#if !defined(HAVE_RSA_METH_GET0_APP_DATA ) +/** +* Get the application data of an RSA_METHOD object +* +* @param meth The RSA_METHOD object +* @return pointer to application data, may be NULL +*/ +static inline void * +RSA_meth_get0_app_data (const RSA_METHOD *meth) +{ +return meth ? meth->app_data :NULL ; +} +#endif +#if !defined(HAVE_EC_GROUP_ORDER_BITS ) && !defined(OPENSSL_NO_EC )/** * Gets the number of bits of the order of an EC_GROUP
Signature · 1 line
2.1.4
Re: [Openvpn-devel] [PATCH v2 1/2] Bring cryptoapi.c upto speed with openssl 1.1
Hi,
Quoted 212 lines · On 14-01-18 20:04, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- Replace direct access to internals of openssl structs by corresponding methods.
v2: Remove the call to
EVP_PKEY_id()as its slated for removal
from the compat layer (see also review by Stefan)Signed-off-by Selva Nair <[email protected]...>---
Freeingrsa_methodin this code path requires more fixup than just use ofRSA_meth_free(not freed at in n some errors etc.), so will be addressed in a separate patch.configure.ac | 1 + src/openvpn/cryptoapi.c | 68 ++++++++++++++++++++++++++------------------ src/openvpn/openssl_compat.h | 14 +++++++++ 3 files changed, 56 insertions(+), 27 deletions(-)configure.ac +1
-0@@ -944,6 +944,7 @@ if test "${with_crypto_library}" = "openssl"; then RSA_meth_set_init \ RSA_meth_set_finish \ RSA_meth_set0_app_data \ + RSA_meth_get0_app_data \ EC_GROUP_order_bits ] )src/openvpn/cryptoapi.c +41
-27@@ -47,6 +47,7 @@#include <assert.h> #include "buffer.h" +#include "openssl_compat.h" /* MinGW w32api 3.17 is still incomplete when it comes to CryptoAPI while * MinGW32-w64 defines all macros used. This is a hack around that problem. @@ -213,20 +214,20 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, instatic int rsa_priv_enc (int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding) { -CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; +CAPI_DATA *cd = (CAPI_DATA *)RSA_meth_get0_app_data (RSA_get_method (rsa));HCRYPTHASH hash;DWORD hash_size, len, i;unsigned char *buf;if (cd ==NULL ) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,ERR_R_PASSED_NULL_PARAMETER ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,ERR_R_PASSED_NULL_PARAMETER );return 0 ; }if (padding !=RSA_PKCS1_PADDING ) {/* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */ -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE );return 0 ; }/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would @@ -236,7 +237,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i/* For now, we only support NID_md5_sha1 */ if (flen !=SSL_SIG_LENGTH ) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH );return 0 ; }if (!CryptCreateHash (cd->crypt_prov,CALG_SSL3_SHAMD5 ,0 ,0 , &hash))@@ -253,7 +254,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i }if ((int ) hash_size != flen) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_INVALID_MESSAGE_LENGTH );CryptDestroyHash (hash);return 0 ; }@@ -268,7 +269,7 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, i buf =malloc (len);if (buf ==NULL ) { -RSAerr (RSA_F_RSA_EAY_PRIVATE_ENCRYPT ,ERR_R_MALLOC_FAILURE ); +RSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,ERR_R_MALLOC_FAILURE );CryptDestroyHash (hash);return 0 ; }@@ -312,7 +313,8 @@ init(RSA *rsa)static int finish (RSA *rsa) { -CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; +const RSA_METHOD *rsa_meth =RSA_get_method (rsa); +CAPI_DATA *cd = (CAPI_DATA *)RSA_meth_get0_app_data (rsa_meth);if (cd ==NULL ) {@@ -326,9 +328,8 @@ finish(RSA *rsa) {CertFreeCertificateContext (cd->cert_context); } -free (rsa->meth->app_data); -free ((char *) rsa->meth); - rsa->meth =NULL ; +free (cd); +RSA_meth_free ((RSA_METHOD *) rsa_meth);return 1 ; }@@ -412,9 +413,9 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)X509 *cert =NULL ;RSA *rsa =NULL , *pub_rsa;CAPI_DATA *cd =calloc (1 ,sizeof (*cd)); -RSA_METHOD *my_rsa_method =calloc (1 ,sizeof (*my_rsa_method)); +RSA_METHOD *my_rsa_method =NULL ; -if (cd ==NULL || my_rsa_method ==NULL ) +if (cd ==NULL ) {SSLerr (SSL_F_SSL_CTX_USE_CERTIFICATE_FILE ,ERR_R_MALLOC_FAILURE ); goto err;@@ -469,15 +470,16 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)/* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ - my_rsa_method->name ="Microsoft CryptoAPI RSA Method" ; - my_rsa_method->rsa_pub_enc = rsa_pub_enc; - my_rsa_method->rsa_pub_dec = rsa_pub_dec; - my_rsa_method->rsa_priv_enc = rsa_priv_enc; - my_rsa_method->rsa_priv_dec = rsa_priv_dec; -/* my_rsa_method->init = init; */ - my_rsa_method->finish = finish; - my_rsa_method->flags =RSA_METHOD_FLAG_NO_CHECK ; - my_rsa_method->app_data = (char *) cd; + my_rsa_method =RSA_meth_new ("Microsoft Cryptography API RSA Method" , +RSA_METHOD_FLAG_NO_CHECK ); +check_malloc_return (my_rsa_method); +RSA_meth_set_pub_enc (my_rsa_method, rsa_pub_enc); +RSA_meth_set_pub_dec (my_rsa_method, rsa_pub_dec); +RSA_meth_set_priv_enc (my_rsa_method, rsa_priv_enc); +RSA_meth_set_priv_dec (my_rsa_method, rsa_priv_dec); +RSA_meth_set_init (my_rsa_method,NULL ); +RSA_meth_set_finish (my_rsa_method, finish); +RSA_meth_set0_app_data (my_rsa_method, cd); rsa =RSA_new ();if (rsa ==NULL )@@ -486,23 +488,35 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) goto err; } -/* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(), +/* Public key in cert is NULL until we call SSL_CTX_use_certificate(), * so we do it here then... */ if (!SSL_CTX_use_certificate (ssl_ctx, cert)) { goto err; }/* the public key */ - pub_rsa = cert->cert_info->key->pkey->pkey.rsa; +EVP_PKEY *pkey =X509_get0_pubkey (cert); +/* SSL_CTX_use_certificate() increased the reference count in 'cert', so * we decrease it here with X509_free(), or it will never be cleaned up. */ X509_free (cert); cert =NULL ; -/* I'm not sure about what we have to fill in in the RSA, trying out stuff... */ -/* rsa->n indicates the key size */ - rsa->n =BN_dup (pub_rsa->n); - rsa->flags |=RSA_FLAG_EXT_PKEY ; +if (!(pub_rsa =EVP_PKEY_get0_RSA (pkey))) + { +msg (M_WARN ,"cryptoapicert requires an RSA certificate" ); + goto err; + } + +/* Our private key is external, so we fill in only n and e from the public key */ +const BIGNUM *n =NULL ; +const BIGNUM *e =NULL ; +RSA_get0_key (pub_rsa, &n, &e,NULL ); +if (!RSA_set0_key (rsa,BN_dup (n),BN_dup (e),NULL )) + { + goto err; + } +RSA_set_flags (rsa,RSA_flags (rsa) |RSA_FLAG_EXT_PKEY );if (!RSA_set_method (rsa, my_rsa_method)) { goto err;src/openvpn/openssl_compat.h +7
-0@@ -624,6 +624,20 @@ RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) }#endif +#if !defined(HAVE_RSA_METH_GET0_APP_DATA ) +/** +* Get the application data of an RSA_METHOD object +* +* @param meth The RSA_METHOD object +* @return pointer to application data, may be NULL +*/
src/openvpn/openssl_compat.h RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) · line 643
Quoted 10 lines
src/openvpn/openssl_compat.h +7
-0+static inline void * +RSA_meth_get0_app_data (const RSA_METHOD *meth) +{ +return meth ? meth->app_data :NULL ; +} +#endif +#if !defined(HAVE_EC_GROUP_ORDER_BITS ) && !defined(OPENSSL_NO_EC )/** * Gets the number of bits of the order of an EC_GROUP
Codewise no more comments. Only "compile-tested" myself, but given the strong resemblance to the management-external-key code I'm still confident that this change is good. So:
-Steffan
[Openvpn-devel] [PATCH applied] Re: Bring cryptoapi.c upto speed with openssl 1.1
In reply to [email protected]...Your patch has been applied to the master and release/2.4 branch.
(Technically it might not look like a bugfix, but since we decided "2.4 gets support for OpenSSL 1.1" and cryptoapi was overlooked, this *makes* it a bugfix. I have not even compile-tested it, trusting Selva and Steffan here - and buildbot will tell me if they overlooked something :-) )
commit 862cbe538b6d53435f60065b0235639095c9ad0d (master) commit 94de253e720154efd2202281042560af18f2bdcb (release/2.4)
Author: Selva Nair Date: Sun Jan 14 14:04:22 2018 -0500
Bring cryptoapi.c upto speed with openssl 1.1
Signed-off-by: Selva Nair <[email protected]...>
Acked-by: Steffan Karger <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/search?l=mid&[email protected]...
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering
PATCH 2/2 TLS v1.2 support for cryptoapicert -- RSA only
In reply to [email protected]...From: Selva Nair <[email protected]...>
- If an NCRYPT handle for the private key can be obtained, use NCryptSignHash from the Cryptography NG API to sign the hash.
This should work for all keys in the Windows certifiate stores but may fail for keys in a legacy token, for example. In such cases, we disable TLS v1.2 and fall back to the current behaviour. A warning is logged unless TLS version is already restricted to <= 1.1
---
Tested on Windows 10 with openssl 1.0.1r and 1.1.0g with certificate/key in Windows user store
src/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++--- src/openvpn/options.c | 18 ----------- 3 files changed, 83 insertions(+), 23 deletions(-)
src/openvpn/Makefile.am +1 -1
@@ -132,5 +132,5 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) if WIN32 openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt endif
src/openvpn/cryptoapi.c +74 -4
143 lines not on this page · raw message
src/openvpn/options.c +0 -18
@@ -3018,24 +3018,6 @@ options_postprocess_mutate(struct options *o) }#endif -#ifdef ENABLE_CRYPTOAPI -if (o->cryptoapi_cert) - { -const int tls_version_max = - (o->ssl_flags >>SSLF_TLS_VERSION_MAX_SHIFT ) - &SSLF_TLS_VERSION_MAX_MASK ; - -if (tls_version_max ==TLS_VER_UNSPEC || tls_version_max >TLS_VER_1_1 ) - { -msg (M_WARN ,"Warning: cryptapicert used, setting maximum TLS " -"version to 1.1." ); - o->ssl_flags &= ~(SSLF_TLS_VERSION_MAX_MASK - <<SSLF_TLS_VERSION_MAX_SHIFT ); - o->ssl_flags |= (TLS_VER_1_1 <<SSLF_TLS_VERSION_MAX_SHIFT ); - } - } -#endif /* ENABLE_CRYPTOAPI */ -#if P2MP /* * Save certain parms before modifying options via --pull
Signature · 1 line
2.1.4
Re: [Openvpn-devel] [PATCH 2/2] TLS v1.2 support for cryptoapicert -- RSA only
Hi,
Some preliminary comments:
Quoted 83 lines · On 08-01-18 03:21, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- If an NCRYPT handle for the private key can be obtained, use NCryptSignHash from the Cryptography NG API to sign the hash.
This should work for all keys in the Windows certifiate stores but may fail for keys in a legacy token, for example. In such cases, we disable TLS v1.2 and fall back to the current behaviour. A warning is logged unless TLS version is already restricted to <= 1.1
Signed-off-by Selva Nair <[email protected]...>---
Tested on Windows 10 with openssl 1.0.1r and 1.1.0g with certificate/key in Windows user storesrc/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++--- src/openvpn/options.c | 18 ----------- 3 files changed, 83 insertions(+), 23 deletions(-)src/openvpn/Makefile.am +1
-1@@ -132,5 +132,5 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) if WIN32 openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt endifsrc/openvpn/cryptoapi.c +9
-1@@ -42,6 +42,7 @@#include <openssl/err.h> #include <windows.h> #include <wincrypt.h> +#include <ncrypt.h> #include <stdio.h> #include <ctype.h> #include <assert.h> @@ -68,6 +69,14 @@#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000#endif /* Size of an SSL signature: MD5+SHA1 */ #define SSL_SIG_LENGTH 36@@ -83,6 +92,7 @@#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106#define CRYPTOAPI_F_LOAD_LIBRARY 107#define CRYPTOAPI_F_GET_PROC_ADDRESS 108 +#define CRYPTOAPI_F_NCRYPT_SIGN_HASH 109static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { {ERR_PACK (ERR_LIB_CRYPTOAPI ,0 ,0 ),"microsoft cryptoapi" },@@ -95,12 +105,13 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" }, { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" }, { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" }, + { ERR_PACK(0, CRYPTOAPI_F_NCRYPT_SIGN_HASH, 0), "NCryptSignHash" }, { 0, NULL } };typedef struct _CAPI_DATA {const CERT_CONTEXT *cert_context; -HCRYPTPROV crypt_prov; +HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;DWORD key_spec;BOOL free_crypt_prov; }CAPI_DATA ;@@ -210,6 +221,41 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, inreturn 0 ; } +/** +* Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT +* key handle in cd->crypt_prov. On return the signature is in 'to'. Returns +* the length of the signature or 0 on error. +* For now we support only RSA and the padding is assumed to be PKCS1 v1.5
src/openvpn/cryptoapi.c rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, in · line 238
Quoted 10 lines
src/openvpn/cryptoapi.c +10
-0+*/ +static int +priv_enc_CNG (const CAPI_DATA *cd,const unsigned char *from,int flen, +unsigned char *to,int tlen,int padding) +{ +NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; +DWORD len; +ASSERT (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ); + +msg (M_INFO ,"Signing hash using CNG: data size = %d" , flen);
M_INFO is probably a bit verbose here.
Quoted 51 lines
src/openvpn/cryptoapi.c +32
-1+/* The hash OID is already in 'from'. So set the hash algorithm +* in the padding info struct to NULL. +*/ +BCRYPT_PKCS1_PADDING_INFO padinfo = {NULL }; +DWORD status; + + status =NCryptSignHash (hkey, padding? &padinfo :NULL , (BYTE *) from, flen, + to, tlen, &len, padding?BCRYPT_PAD_PKCS1 :0 ); +if (status !=ERROR_SUCCESS ) + { +SetLastError (status); +CRYPTOAPIerr (CRYPTOAPI_F_NCRYPT_SIGN_HASH ); + len =0 ; + } + +/* Unlike CAPI, CNG signature is in big endian order. No reversing needed. */ +return len; +} +/* sign arbitrary data */ static int rsa_priv_enc (int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding)@@ -230,6 +276,11 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, iRSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE );return 0 ; } +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +return priv_enc_CNG (cd, from, flen, to,RSA_size (rsa), padding); + } +/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would * be way to straightforward for M$, I guess... So we have to do it this * tricky way instead, by creating a "Hash", and load the already-made hash @@ -322,7 +373,14 @@ finish(RSA *rsa) }if (cd->crypt_prov && cd->free_crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 522
Quoted 8 lines
src/openvpn/cryptoapi.c +3
-1@@ -458,7 +516,9 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) }/* set up stuff to use the private key */ -if (!CryptAcquireCertificatePrivateKey (cd->cert_context,CRYPT_ACQUIRE_COMPARE_KEY_FLAG , +/* We prefer to get an NCRYPT key handle so that TLS1.2 can be supported */ +DWORD flags =CRYPT_ACQUIRE_COMPARE_KEY_FLAG |CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG ; +if (!CryptAcquireCertificatePrivateKey (cd->cert_context, flags,NULL , &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))
Some rewrapping of the arguments to no longer exceed 80 chars would be nice if you touch this anyway.
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 537
Quoted 11 lines
src/openvpn/cryptoapi.c +5
-0{/* if we don't have a smart card reader here, and we try to access a @@ -470,6 +530,17 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)/* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ +/* If we do not have an NCRYPT key handle disable TLS v1.2 */ +if (cd->key_spec !=CERT_NCRYPT_KEY_SPEC ) + { +if (!(SSL_CTX_get_options (ssl_ctx) & SSL_OP_NO_TLSv1_2)) + {
This no longer works for OpenSSL 1.1, we'll need the patch from
https://patchwork.openvpn.net/patch/160/ (awaiting review), and then use the SSL_CTX_{get,set}_max_proto_version functions instead.
Quoted 45 lines
src/openvpn/cryptoapi.c +14
-1+msg (M_WARN ,"WARNING: cryptapicert: only legacy private key handle is available." +"Disabling TLS v1.2" ); +SSL_CTX_set_options (ssl_ctx, SSL_OP_NO_TLSv1_2); + } + } + my_rsa_method =RSA_meth_new ("Microsoft Cryptography API RSA Method" ,RSA_METHOD_FLAG_NO_CHECK );check_malloc_return (my_rsa_method);@@ -551,7 +622,14 @@ err: {if (cd->free_crypt_prov && cd->crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {src/openvpn/options.c +0
-11@@ -3018,24 +3018,6 @@ options_postprocess_mutate(struct options *o) }#endif -#ifdef ENABLE_CRYPTOAPI -if (o->cryptoapi_cert) - { -const int tls_version_max = - (o->ssl_flags >>SSLF_TLS_VERSION_MAX_SHIFT ) - &SSLF_TLS_VERSION_MAX_MASK ; - -if (tls_version_max ==TLS_VER_UNSPEC || tls_version_max >TLS_VER_1_1 ) - { -msg (M_WARN ,"Warning: cryptapicert used, setting maximum TLS " -"version to 1.1." );
src/openvpn/options.c options_postprocess_mutate(struct options *o) · line 3023
Quoted 10 lines
src/openvpn/options.c +0
-7- o->ssl_flags &= ~(SSLF_TLS_VERSION_MAX_MASK - <<SSLF_TLS_VERSION_MAX_SHIFT ); - o->ssl_flags |= (TLS_VER_1_1 << SSLF_TLS_VERSION_MAX_SHIFT); - } - } -#endif /* ENABLE_CRYPTOAPI */ -#if P2MP /* * Save certain parms before modifying options via --pull
Otherwise this looks good on first sight. I'll try to do some building and testing later.
-Steffan
Re: [Openvpn-devel] [PATCH 2/2] TLS v1.2 support for cryptoapicert -- RSA only
Hi,
Thanks for the comments
Quoted 17 lines · Steffan Karger
Hi,
Some preliminary comments:
Quoted 12 lines · On 08-01-18 03:21, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- If an NCRYPT handle for the private key can be obtained, use NCryptSignHash from the Cryptography NG API to sign the hash.
This should work for all keys in the Windows certifiate stores but may fail for keys in a legacy token, for example. In such cases, we disable TLS v1.2 and fall back to the current behaviour. A warning is logged unless TLS version is already restricted to <= 1.1
.. snipped..
Quoted 11 lines
src/openvpn/cryptoapi.c rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, in · line 238
Quoted 9 lines
src/openvpn/cryptoapi.c +9
-0+static int +priv_enc_CNG (const CAPI_DATA *cd,const unsigned char *from,int flen, +unsigned char *to,int tlen,int padding) +{ +NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; +DWORD len; +ASSERT (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ); + +msg (M_INFO ,"Signing hash using CNG: data size = %d" , flen);
M_INFOis probably a bit verbose here.
Yeah, that was more for testing -- will use one of the TLS debug flags -- we really need to make M_DEBUG print only at some high verbosity.
...
Quoted 22 lines
Quoted 22 lines
/* Unfortunately, there is no "
CryptSign()" function in CryptoAPI, that would
* be way to straightforward for M$, I guess... So we have to do it this
* tricky way instead, by creating a "Hash", and load the already-made hashsrc/openvpn/cryptoapi.c +8
-1@@ -322,7 +373,14 @@ finish(RSA *rsa) }if (cd->crypt_prov && cd->free_crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {@@ -458,7 +516,9 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) }
Quoted 9 lines
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 522
Quoted 6 lines
src/openvpn/cryptoapi.c +3
-1/* set up stuff to use the private key */ -if (!CryptAcquireCertificatePrivateKey (cd->cert_context,CRYPT_ACQUIRE_COMPARE_KEY_FLAG , +/* We prefer to get an NCRYPT key handle so that TLS1.2 can be supported */ +DWORD flags =CRYPT_ACQUIRE_COMPARE_KEY_FLAG |CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG ; +if (!CryptAcquireCertificatePrivateKey (cd->cert_context, flags,NULL , &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov))Some rewrapping of the arguments to no longer exceed 80 chars would be nice if you touch this anyway.
done :)
Quoted 7 lines
Quoted 7 lines
src/openvpn/cryptoapi.c +0
-0{/* if we don't have a smart card reader here, and we try to access a @@ -470,6 +530,17 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)/* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */
Quoted 9 lines
src/openvpn/cryptoapi.c SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) · line 537
Quoted 5 lines
src/openvpn/cryptoapi.c +5
-0+/* If we do not have an NCRYPT key handle disable TLS v1.2 */ +if (cd->key_spec !=CERT_NCRYPT_KEY_SPEC ) + { +if (!(SSL_CTX_get_options (ssl_ctx) & SSL_OP_NO_TLSv1_2)) + {This no longer works for OpenSSL 1.1, we'll need the patch from
https://patchwork.openvpn.net/patch/160/ (awaiting review), and then use the SSL_CTX_{get,set}_max_proto_version functions instead.
Good point.
Strangely enough it works in some cases -- I had tested with a non-NCRYPT key in a legacy token and got restricted to TLS1.1. But point noted. And, anyway, set_max_proto_version is better, as otherwise we'll have to touch this again when TLS 1.3 is out.
Will take care of it in v2.
Selva
PATCH v2 2/2 TLS v1.2 support for cryptoapicert -- RSA only
In reply to [email protected]...From: Selva Nair <[email protected]...>
- If an NCRYPT handle for the private key can be obtained, use NCryptSignHash from the Cryptography NG API to sign the hash.
This should work for all keys in the Windows certifiate stores but may fail for keys in a legacy token, for example. In such cases, we disable TLS v1.2 and fall back to the current behaviour. A warning is logged unless TLS version is already restricted to <= 1.1
---
Depends on patches:
patch 200: https://patchwork.openvpn.net/patch/200/
patch 201: https://patchwork.openvpn.net/patch/201/
v2: Based on Stefan's review:
- Replace SSL_CTX_get(set)_option with SSL_CTX_get(set)_max_proto_version
- Wrap some lines to 80 chars
- replace M_INFO by D_LOW in a low level debug message
Also removed some defines added in v1 that are actually present in mingw versions that we target
src/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++--- src/openvpn/options.c | 18 ----------- 3 files changed, 81 insertions(+), 24 deletions(-)
src/openvpn/Makefile.am +1 -1
@@ -132,5 +132,5 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) if WIN32 openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt endif
src/openvpn/cryptoapi.c +80 -5
144 lines not on this page · raw message
src/openvpn/options.c +0 -18
@@ -3018,24 +3018,6 @@ options_postprocess_mutate(struct options *o) }#endif -#ifdef ENABLE_CRYPTOAPI -if (o->cryptoapi_cert) - { -const int tls_version_max = - (o->ssl_flags >>SSLF_TLS_VERSION_MAX_SHIFT ) - &SSLF_TLS_VERSION_MAX_MASK ; - -if (tls_version_max ==TLS_VER_UNSPEC || tls_version_max >TLS_VER_1_1 ) - { -msg (M_WARN ,"Warning: cryptapicert used, setting maximum TLS " -"version to 1.1." ); - o->ssl_flags &= ~(SSLF_TLS_VERSION_MAX_MASK - <<SSLF_TLS_VERSION_MAX_SHIFT ); - o->ssl_flags |= (TLS_VER_1_1 <<SSLF_TLS_VERSION_MAX_SHIFT ); - } - } -#endif /* ENABLE_CRYPTOAPI */ -#if P2MP /* * Save certain parms before modifying options via --pull
Signature · 1 line
2.1.4
Re: [Openvpn-devel] [PATCH v2 2/2] TLS v1.2 support for cryptoapicert -- RSA only
Hi,
Quoted 219 lines · On 20-01-18 05:52, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- If an NCRYPT handle for the private key can be obtained, use NCryptSignHash from the Cryptography NG API to sign the hash.
This should work for all keys in the Windows certifiate stores but may fail for keys in a legacy token, for example. In such cases, we disable TLS v1.2 and fall back to the current behaviour. A warning is logged unless TLS version is already restricted to <= 1.1
Signed-off-by Selva Nair <[email protected]...>---
Depends on patches:
patch 200: https://patchwork.openvpn.net/patch/200/
patch 201: https://patchwork.openvpn.net/patch/201/v2: Based on Stefan's review:
- Replace SSL_CTX_get(set)_option with SSL_CTX_get(set)_max_proto_version
- Wrap some lines to 80 chars
- replaceM_INFObyD_LOWin a low level debug messageAlso removed some defines added in v1 that are actually present in mingw versions that we target
src/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++--- src/openvpn/options.c | 18 ----------- 3 files changed, 81 insertions(+), 24 deletions(-)src/openvpn/Makefile.am +1
-1@@ -132,5 +132,5 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) if WIN32 openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt endifsrc/openvpn/cryptoapi.c +80
-5@@ -42,6 +42,7 @@#include <openssl/err.h> #include <windows.h> #include <wincrypt.h> +#include <ncrypt.h> #include <stdio.h> #include <ctype.h> #include <assert.h> @@ -83,6 +84,7 @@#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106#define CRYPTOAPI_F_LOAD_LIBRARY 107#define CRYPTOAPI_F_GET_PROC_ADDRESS 108 +#define CRYPTOAPI_F_NCRYPT_SIGN_HASH 109static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { {ERR_PACK (ERR_LIB_CRYPTOAPI ,0 ,0 ),"microsoft cryptoapi" },@@ -95,12 +97,13 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" }, { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" }, { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" }, + { ERR_PACK(0, CRYPTOAPI_F_NCRYPT_SIGN_HASH, 0), "NCryptSignHash" }, { 0, NULL } };typedef struct _CAPI_DATA {const CERT_CONTEXT *cert_context; -HCRYPTPROV crypt_prov; +HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;DWORD key_spec;BOOL free_crypt_prov; }CAPI_DATA ;@@ -210,6 +213,41 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, inreturn 0 ; } +/** +* Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT +* key handle in cd->crypt_prov. On return the signature is in 'to'. Returns +* the length of the signature or 0 on error. +* For now we support only RSA and the padding is assumed to be PKCS1 v1.5 +*/ +static int +priv_enc_CNG (const CAPI_DATA *cd,const unsigned char *from,int flen, +unsigned char *to,int tlen,int padding) +{ +NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; +DWORD len; +ASSERT (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ); + +msg (D_LOW ,"Signing hash using CNG: data size = %d" , flen); + +/* The hash OID is already in 'from'. So set the hash algorithm +* in the padding info struct to NULL. +*/ +BCRYPT_PKCS1_PADDING_INFO padinfo = {NULL }; +DWORD status; + + status =NCryptSignHash (hkey, padding? &padinfo :NULL , (BYTE *) from, flen, + to, tlen, &len, padding?BCRYPT_PAD_PKCS1 :0 ); +if (status !=ERROR_SUCCESS ) + { +SetLastError (status); +CRYPTOAPIerr (CRYPTOAPI_F_NCRYPT_SIGN_HASH ); + len =0 ; + } + +/* Unlike CAPI, CNG signature is in big endian order. No reversing needed. */ +return len; +} +/* sign arbitrary data */ static int rsa_priv_enc (int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding)@@ -230,6 +268,11 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, iRSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE );return 0 ; } +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +return priv_enc_CNG (cd, from, flen, to,RSA_size (rsa), padding); + } +/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would * be way to straightforward for M$, I guess... So we have to do it this * tricky way instead, by creating a "Hash", and load the already-made hash @@ -322,7 +365,14 @@ finish(RSA *rsa) }if (cd->crypt_prov && cd->free_crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {@@ -458,8 +508,11 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) }/* set up stuff to use the private key */ -if (!CryptAcquireCertificatePrivateKey (cd->cert_context,CRYPT_ACQUIRE_COMPARE_KEY_FLAG , -NULL , &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) +/* We prefer to get an NCRYPT key handle so that TLS1.2 can be supported */ +DWORD flags =CRYPT_ACQUIRE_COMPARE_KEY_FLAG + |CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG ; +if (!CryptAcquireCertificatePrivateKey (cd->cert_context, flags,NULL , + &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) {/* if we don't have a smart card reader here, and we try to access a * smart card certificate, we get: @@ -470,6 +523,21 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)/* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ +/* if we do not have an NCRYPT key handle restrict TLS to v1.1 or lower */ +int max_version =SSL_CTX_get_max_proto_version (ssl_ctx); +if ((!max_version || max_version >TLS1_1_VERSION ) + && cd->key_spec !=CERT_NCRYPT_KEY_SPEC ) + { +msg (M_WARN ,"WARNING: cryptoapicert: private key is in a legacy store." +" Restricting TLS version to 1.1" ); +if (!SSL_CTX_set_max_proto_version (ssl_ctx,TLS1_1_VERSION )) + { +msg (M_NONFATAL ,"ERROR: cryptoapicert: unable to set max TLS version" +" to 1.1. Try config option --tls-version-min 1.1" ); + goto err; + } + } + my_rsa_method =RSA_meth_new ("Microsoft Cryptography API RSA Method" ,RSA_METHOD_FLAG_NO_CHECK );check_malloc_return (my_rsa_method);@@ -550,7 +618,14 @@ err: {if (cd->free_crypt_prov && cd->crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {src/openvpn/options.c +0
-11@@ -3018,24 +3018,6 @@ options_postprocess_mutate(struct options *o) }#endif -#ifdef ENABLE_CRYPTOAPI -if (o->cryptoapi_cert) - { -const int tls_version_max = - (o->ssl_flags >>SSLF_TLS_VERSION_MAX_SHIFT ) - &SSLF_TLS_VERSION_MAX_MASK ; - -if (tls_version_max ==TLS_VER_UNSPEC || tls_version_max >TLS_VER_1_1 ) - { -msg (M_WARN ,"Warning: cryptapicert used, setting maximum TLS " -"version to 1.1." );
src/openvpn/options.c options_postprocess_mutate(struct options *o) · line 3023
Quoted 10 lines
src/openvpn/options.c +0
-7- o->ssl_flags &= ~(SSLF_TLS_VERSION_MAX_MASK - <<SSLF_TLS_VERSION_MAX_SHIFT ); - o->ssl_flags |= (TLS_VER_1_1 << SSLF_TLS_VERSION_MAX_SHIFT); - } - } -#endif /* ENABLE_CRYPTOAPI */ -#if P2MP /* * Save certain parms before modifying options via --pull
Code looks good, and now nicely connects using TLS 1.2 with a certificate from the windows certificate store, nice!
I don't know how to trigger the 'legacy store' code path. Have you tested that yourself? If you can confirm that you've tested it:
Thanks for fixing this long-standing shortcoming!
-Steffan
Selva Nair
Tested-byRe: [Openvpn-devel] [PATCH v2 2/2] TLS v1.2 support for cryptoapicert -- RSA only
Hi Steffan,
Thank you for the review and the ack.
Quoted 236 lines · Steffan Karger
Hi,
Quoted 219 lines · On 20-01-18 05:52, [email protected]... wrote:
From: Selva Nair <[email protected]...>
- If an NCRYPT handle for the private key can be obtained, use NCryptSignHash from the Cryptography NG API to sign the hash.
This should work for all keys in the Windows certifiate stores but may fail for keys in a legacy token, for example. In such cases, we disable TLS v1.2 and fall back to the current behaviour. A warning is logged unless TLS version is already restricted to <= 1.1
Signed-off-by Selva Nair <[email protected]...>---
Depends on patches:
patch 200: https://patchwork.openvpn.net/patch/200/
patch 201: https://patchwork.openvpn.net/patch/201/v2: Based on Stefan's review:
- Replace SSL_CTX_get(set)_option with SSL_CTX_get(set)_max_proto_version
- Wrap some lines to 80 chars
- replaceM_INFObyD_LOWin a low level debug messageAlso removed some defines added in v1 that are actually present in mingw versions that we target
src/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++--- src/openvpn/options.c | 18 ----------- 3 files changed, 81 insertions(+), 24 deletions(-)src/openvpn/Makefile.am +1
-1@@ -132,5 +132,5 @@ openvpn_LDADD = \ $(OPTIONAL_DL_LIBS) if WIN32 openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h -openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 +openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt endifsrc/openvpn/cryptoapi.c +80
-5@@ -42,6 +42,7 @@#include <openssl/err.h> #include <windows.h> #include <wincrypt.h> +#include <ncrypt.h> #include <stdio.h> #include <ctype.h> #include <assert.h> @@ -83,6 +84,7 @@#define CRYPTOAPI_F_CRYPT_SIGN_HASH 106#define CRYPTOAPI_F_LOAD_LIBRARY 107#define CRYPTOAPI_F_GET_PROC_ADDRESS 108 +#define CRYPTOAPI_F_NCRYPT_SIGN_HASH 109static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { {ERR_PACK (ERR_LIB_CRYPTOAPI ,0 ,0 ),"microsoft cryptoapi" },@@ -95,12 +97,13 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { { ERR_PACK(0, CRYPTOAPI_F_CRYPT_SIGN_HASH, 0), "CryptSignHash" }, { ERR_PACK(0, CRYPTOAPI_F_LOAD_LIBRARY, 0), "LoadLibrary" }, { ERR_PACK(0, CRYPTOAPI_F_GET_PROC_ADDRESS, 0), "GetProcAddress" }, + { ERR_PACK(0, CRYPTOAPI_F_NCRYPT_SIGN_HASH, 0), "NCryptSignHash" }, { 0, NULL } };typedef struct _CAPI_DATA {const CERT_CONTEXT *cert_context; -HCRYPTPROV crypt_prov; +HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;DWORD key_spec;BOOL free_crypt_prov; }CAPI_DATA ;@@ -210,6 +213,41 @@ rsa_pub_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, inreturn 0 ; } +/** +* Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT +* key handle in cd->crypt_prov. On return the signature is in 'to'. Returns +* the length of the signature or 0 on error. +* For now we support only RSA and the padding is assumed to be PKCS1 v1.5 +*/ +static int +priv_enc_CNG (const CAPI_DATA *cd,const unsigned char *from,int flen, +unsigned char *to,int tlen,int padding) +{ +NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; +DWORD len; +ASSERT (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ); + +msg (D_LOW ,"Signing hash using CNG: data size = %d" , flen); + +/* The hash OID is already in 'from'. So set the hash algorithm +* in the padding info struct to NULL. +*/ +BCRYPT_PKCS1_PADDING_INFO padinfo = {NULL }; +DWORD status; + + status =NCryptSignHash (hkey, padding? &padinfo :NULL , (BYTE *) from, flen, + to, tlen, &len, padding?BCRYPT_PAD_PKCS1 :0 ); +if (status !=ERROR_SUCCESS ) + { +SetLastError (status); +CRYPTOAPIerr (CRYPTOAPI_F_NCRYPT_SIGN_HASH ); + len =0 ; + } + +/* Unlike CAPI, CNG signature is in big endian order. No reversing needed. */ +return len; +} +/* sign arbitrary data */ static int rsa_priv_enc (int flen,const unsigned char *from,unsigned char *to,RSA *rsa,int padding)@@ -230,6 +268,11 @@ rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, iRSAerr (RSA_F_RSA_OSSL_PRIVATE_ENCRYPT ,RSA_R_UNKNOWN_PADDING_TYPE );return 0 ; } +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +return priv_enc_CNG (cd, from, flen, to,RSA_size (rsa), padding); + } +/* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would * be way to straightforward for M$, I guess... So we have to do it this * tricky way instead, by creating a "Hash", and load the already-made hash @@ -322,7 +365,14 @@ finish(RSA *rsa) }if (cd->crypt_prov && cd->free_crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {@@ -458,8 +508,11 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) }/* set up stuff to use the private key */ -if (!CryptAcquireCertificatePrivateKey (cd->cert_context,CRYPT_ACQUIRE_COMPARE_KEY_FLAG , -NULL , &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) +/* We prefer to get an NCRYPT key handle so that TLS1.2 can be supported */ +DWORD flags =CRYPT_ACQUIRE_COMPARE_KEY_FLAG + |CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG ; +if (!CryptAcquireCertificatePrivateKey (cd->cert_context, flags,NULL , + &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) {/* if we don't have a smart card reader here, and we try to access a * smart card certificate, we get: @@ -470,6 +523,21 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)/* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ +/* if we do not have an NCRYPT key handle restrict TLS to v1.1 or lower */ +int max_version =SSL_CTX_get_max_proto_version (ssl_ctx); +if ((!max_version || max_version >TLS1_1_VERSION ) + && cd->key_spec !=CERT_NCRYPT_KEY_SPEC ) + { +msg (M_WARN ,"WARNING: cryptoapicert: private key is in a legacy store." +" Restricting TLS version to 1.1" ); +if (!SSL_CTX_set_max_proto_version (ssl_ctx,TLS1_1_VERSION )) + { +msg (M_NONFATAL ,"ERROR: cryptoapicert: unable to set max TLS version" +" to 1.1. Try config option --tls-version-min 1.1" ); + goto err; + } + } + my_rsa_method =RSA_meth_new ("Microsoft Cryptography API RSA Method" ,RSA_METHOD_FLAG_NO_CHECK );check_malloc_return (my_rsa_method);@@ -550,7 +618,14 @@ err: {if (cd->free_crypt_prov && cd->crypt_prov) { -CryptReleaseContext (cd->crypt_prov,0 ); +if (cd->key_spec ==CERT_NCRYPT_KEY_SPEC ) + { +NCryptFreeObject (cd->crypt_prov); + } +else + { +CryptReleaseContext (cd->crypt_prov,0 ); + } }if (cd->cert_context) {src/openvpn/options.c +0
-11@@ -3018,24 +3018,6 @@ options_postprocess_mutate(struct options *o) }#endif -#ifdef ENABLE_CRYPTOAPI -if (o->cryptoapi_cert) - { -const int tls_version_max = - (o->ssl_flags >>SSLF_TLS_VERSION_MAX_SHIFT ) - &SSLF_TLS_VERSION_MAX_MASK ; - -if (tls_version_max ==TLS_VER_UNSPEC || tls_version_max >TLS_VER_1_1 ) - { -msg (M_WARN ,"Warning: cryptapicert used, setting maximum TLS " -"version to 1.1." );src/openvpn/options.c options_postprocess_mutate(struct options *o) · line 3023
Quoted 10 lines
src/openvpn/options.c +0
-7- o->ssl_flags &= ~(SSLF_TLS_VERSION_MAX_MASK - <<SSLF_TLS_VERSION_MAX_SHIFT ); - o->ssl_flags |= (TLS_VER_1_1 << SSLF_TLS_VERSION_MAX_SHIFT); - } - } -#endif /* ENABLE_CRYPTOAPI */ -#if P2MP /* * Save certain parms before modifying options via --pull Code looks good, and now nicely connects using TLS 1.2 with a certificate from the windows certificate store, nice!
Quoted 2 lines
I don't know how to trigger the 'legacy store' code path. Have you tested that yourself? If you can confirm that you've tested it:
Yes, tested again using an old e-token which triggers this:
Sun Jan 21 09:39:23 2018 us=632921 WARNING: cryptoapicert: private key is in a legacy store. Restricting TLS version to 1.1
(on Windows 10).
and connects with
Sun Jan 21 09:39:53 2018 us=955878 Control Channel: TLSv1.1, cipher TLSv1.0 ECDHE-RSA-AES256-SHA, 1024 bit RSA
If the patch-author is allowed to say
Quoted 3 lines
Thanks,
Selva
[Openvpn-devel] [PATCH applied] Re: TLS v1.2 support for cryptoapicert -- RSA only
In reply to [email protected]...Your patch has been applied to the master and release/2.4 branch.
I have tested on unix (where it obviously did not make a difference), compile-tested on my old ubuntu 14.04 build environment (fails with CERT_NCRYPT_KEY_SPEC not being defined, because mingw *there* needs "_WIN32_WINNT >= 0x0601") and successfully built on a brand new ubuntu 16.04 build environment, which adds a new "wincrypt.h" file with new #if WINAPI_FAMILY_PARTITION fun... :-)
[short summary: throw away your 14.04 build systems, we have decided to
break them, and that's what they are: broken!]
There is one thing I'm not sure I understand in the code, which might warrant a typo-fix patch:
+ msg(M_WARN,"WARNING: cryptoapicert: private key is in a legacy store."
+ " Restricting TLS version to 1.1");
+ if (!SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_1_VERSION))
+ {
+ msg(M_NONFATAL,"ERROR: cryptoapicert: unable to set max TLS version"
+ " to 1.1. Try config option --tls-version-min 1.1");
+ goto err;
+ }
should that be "--tls-version-*max* 1.1"?
commit 51d57d7dad6c6380df7b76bbec1897ea4f98474d (master) commit 6c54745b8d417a534a6081588b1ecc7ff01fa9f7 (release/2.4)
Author: Selva Nair Date: Fri Jan 19 23:52:54 2018 -0500
TLS v1.2 support for cryptoapicert -- RSA only
Signed-off-by: Selva Nair <[email protected]...>
Acked-by: Steffan Karger <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/[email protected]/msg16288.html
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering
Re: [Openvpn-devel] [PATCH applied] Re: TLS v1.2 support for cryptoapicert -- RSA only
Hi,
Quoted 24 lines · Gert Doering
Your patch has been applied to the master and release/2.4 branch.
I have tested on unix (where it obviously did not make a difference), compile-tested on my old ubuntu 14.04 build environment (fails with
CERT_NCRYPT_KEY_SPECnot being defined, because mingw *there* needs "_WIN32_WINNT>=0x0601") and successfully built on a brand new ubuntu 16.04 build environment, which adds a new "wincrypt.h" file with new #ifWINAPI_FAMILY_PARTITIONfun... :-)[short summary: throw away your 14.04 build systems, we have decided to
break them, and that's what they are: broken!]There is one thing I'm not sure I understand in the code, which might warrant a typo-fix patch:
src/openvpn/cryptoapi.c +8
-0+msg (M_WARN ,"WARNING: cryptoapicert: private key is in a legacy store." +" Restricting TLS version to 1.1" ); +if (!SSL_CTX_set_max_proto_version (ssl_ctx,TLS1_1_VERSION )) + { +msg (M_NONFATAL ,"ERROR: cryptoapicert: unable to set max TLS version" +" to 1.1. Try config option --tls-version-min 1.1" ); + goto err; + }
Quoted 1 line
should that be "--tls-version-*max* 1.1"?
In short, there was a reasoning behind that "min" and not "max" but it turns out to be not really useful. For details (which is more of a note to self) read on:
--tls-version-max 1.1 will make the same call as what we do here, so if this fails that would also.
The reason for that hint to set min version instead of max was this:
If the user has set tls-version-min to, say, 1.2 and openssl enforces max >= min, the call could fail and asking the user to set the min version to 1.1 (or lower) was thought of as a helpful hint.
But it turns out openssl does not impose max >= min (though such a situation will eventually cause a handshake failure), and are unlikely to add such a check for legitimate reasons. In openssl 1.1.0, that call could fail only if min==max==1.1 and 1.1 is explicitly disabled -- hard to tell the user what to do in that case as it just should not happen in our current code base.
A more useful thing to do is to catch the case of user setting min = 1.2 (as in trac 977) and we changing max to 1.1 when key is in a legacy key store. That would cause a handshake failure with a cryptic error. Better to catch early with a helpful warning. Will consider a patch for that and trim down the above log message at that time.
Selva
Re: [Openvpn-devel] [PATCH applied] Re: TLS v1.2 support for cryptoapicert -- RSA only
Quoted 5 lines · Selva Nair
A more useful thing to do is to catch the case of user setting min = 1.2 (as in trac 977) and we changing max to 1.1 when key is in a legacy key store. That would cause a handshake failure with a cryptic error. Better to catch early with a helpful warning. Will consider a patch for that and trim down the above log message at that time.
Ah, I actually have a patch ready for that in my local master branch, but still needed to send it out. So if you haven't started yet, I can send that out to the list. Otherwise I'll happily discard mine :)
-Steffan
Re: [Openvpn-devel] [PATCH applied] Re: TLS v1.2 support for cryptoapicert -- RSA only
Hi,
Quoted 10 lines · Steffan Karger
Quoted 5 lines · Selva Nair
A more useful thing to do is to catch the case of user setting min = 1.2 (as in trac 977) and we changing max to 1.1 when key is in a legacy key store. That would cause a handshake failure with a cryptic error. Better to catch early with a helpful warning. Will consider a patch for that and trim down the above log message at that time.
Ah, I actually have a patch ready for that in my local master branch, but still needed to send it out. So if you haven't started yet, I can send that out to the list. Otherwise I'll happily discard mine :)
No, I'll defer to you, I haven't written one. Was just looking into it and realized it may require a better get_min_proto_version compatibility function (current one returns 0).
Thanks,
Selva
PATCH 1/1 Warn if tls-version-max < tls-version-min
This adds warnings for when a user or our code tries to set a maximum TLS version that's smaller then the current configured minimum TLS version.
(And fixes some related whitespace now I touch it anyway.)
--- src/openvpn/cryptoapi.c | 12 +++++++++--- src/openvpn/openssl_compat.h | 22 ++++++++++++++++++---- src/openvpn/options.c | 12 ++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-)
src/openvpn/cryptoapi.c +9 -3
@@ -610,12 +610,18 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)if ((!max_version || max_version >TLS1_1_VERSION ) && cd->key_spec !=CERT_NCRYPT_KEY_SPEC ) { -msg (M_WARN ,"WARNING: cryptoapicert: private key is in a legacy store." +msg (M_WARN ,"WARNING: cryptoapicert: private key is in a legacy store." " Restricting TLS version to 1.1" ); +if (SSL_CTX_get_min_proto_version (ssl_ctx) >TLS1_1_VERSION ) + { +msg (M_NONFATAL , +"ERROR: cryptoapicert: min TLS version larger than 1.1." +" Try config option --tls-version-min 1.1" ); + goto err; + }if (!SSL_CTX_set_max_proto_version (ssl_ctx,TLS1_1_VERSION )) { -msg (M_NONFATAL ,"ERROR: cryptoapicert: unable to set max TLS version" -" to 1.1. Try config option --tls-version-min 1.1" ); +msg (M_NONFATAL ,"ERROR: cryptoapicert: set max TLS version failed" ); goto err; } }
src/openvpn/openssl_compat.h +18 -4
@@ -662,10 +662,24 @@ EC_GROUP_order_bits(const EC_GROUP *group)#endif #ifndef SSL_CTX_get_min_proto_version -/** Dummy SSL_CTX_get_min_proto_version for OpenSSL < 1.1 (not really needed) */ +/** Return the min SSL protocol version currently enabled in the context. +* If no valid version >= TLS1.0 is found, return 0. */ static inline int SSL_CTX_get_min_proto_version (SSL_CTX *ctx) { +long sslopt =SSL_CTX_get_options (ctx); +if (!(sslopt & SSL_OP_NO_TLSv1)) + { +return TLS1_VERSION ; + } +if (!(sslopt & SSL_OP_NO_TLSv1_1)) + { +return TLS1_1_VERSION ; + } +if (!(sslopt & SSL_OP_NO_TLSv1_2)) + { +return TLS1_2_VERSION ; + }return 0 ; }#endif /* SSL_CTX_get_min_proto_version */
@@ -679,15 +693,15 @@ SSL_CTX_get_max_proto_version(SSL_CTX *ctx)long sslopt =SSL_CTX_get_options (ctx);if (!(sslopt & SSL_OP_NO_TLSv1_2)) { -return TLS1_2_VERSION ; +return TLS1_2_VERSION ; }if (!(sslopt & SSL_OP_NO_TLSv1_1)) { -return TLS1_1_VERSION ; +return TLS1_1_VERSION ; }if (!(sslopt & SSL_OP_NO_TLSv1)) { -return TLS1_VERSION ; +return TLS1_VERSION ; }return 0 ; }
src/openvpn/options.c +12 -0
@@ -2522,6 +2522,18 @@ options_postprocess_verify_ce(const struct options *options, const struct connec "in the configuration file, which is the recommended approach."); } +const int tls_version_max = + (options->ssl_flags >>SSLF_TLS_VERSION_MAX_SHIFT ) + &SSLF_TLS_VERSION_MAX_MASK ; +const int tls_version_min = + (options->ssl_flags >>SSLF_TLS_VERSION_MIN_SHIFT ) + &SSLF_TLS_VERSION_MIN_MASK ; + +if (tls_version_max >0 && tls_version_max < tls_version_min) + { +msg (M_USAGE ,"--tls-version-min bigger than --tls-version-max" ); + } +if (options->tls_server || options->tls_client) {#ifdef ENABLE_PKCS11
Signature · 1 line
2.14.1
[Openvpn-devel] [PATCH applied] Re: Warn if tls-version-max < tls-version-min
Your patch has been applied to the master and release/2.4 branch.
(Re-sending this mail until it gets through... so the time stamps wrt commit/push will be odd)
commit f8a92a4393aae32fc44e03241b5cc891ca6e58a4 (master) commit 2d705accea3e538a555631ef7c39eb4bc4fd4acf (release/2.4)
Author: Steffan Karger Date: Sat Feb 24 18:04:49 2018 +0100
Warn if tls-version-max < tls-version-min
Signed-off-by: Steffan Karger <[email protected]...>
Acked-by: Selva Nair <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/[email protected]/msg16545.html
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering
[Openvpn-devel] [PATCH applied] Re: Warn if tls-version-max < tls-version-min
In reply to Steffan KargerYour patch has been applied to the master and release/2.4 branch.
(Re-sending this mail until it gets through... so the time stamps wrt commit/push will be odd)
commit f8a92a4393aae32fc44e03241b5cc891ca6e58a4 (master) commit 2d705accea3e538a555631ef7c39eb4bc4fd4acf (release/2.4)
Author: Steffan Karger Date: Sat Feb 24 18:04:49 2018 +0100
Warn if tls-version-max < tls-version-min
Signed-off-by: Steffan Karger <[email protected]...>
Acked-by: Selva Nair <[email protected]...>
Message-Id: <[email protected]...>
URL: https://www.mail-archive.com/[email protected]/msg16545.html
Signed-off-by: Gert Doering <[email protected]...>--
kind regards,
Gert Doering
2018-01-08 7:21 GMT+05:00 <[email protected]...>:
Quoted 14 lines
From: Selva Nair <[email protected]...>
Hi,
I am not sure how receptive the crypto maintaineres are to the idea of adding more code into
cryptoapi.c, but here goes:I've been wanting to add TLS 1.2 support for certs in the Windows cert store using management external key. But that's a lot more work than extending cryptoapicert support. And, rather surprsingly, it turns out that the CNG API for signing is easy to use (well after some groping in the dark..) and doesn't take much to implement.
Quoted 6 lines
So these patches..
The first patch is not really related and to make the existing code "openssl-1.1 ready" (missed by past patches as no one probably builds Windows binary with 1.1..).
there was an agreement on one of the recent community meetings to gracefully deprecate both libressl and openssl-1.0.X in favour of openssl-1.1.X
so, we should learn how to build windows binary with 1.1.X :)
Quoted 31 lines
The second patch is not dependent on this, but close-by code paths are touched by both.
Selva
Selva Nair (2):
Bringcryptoapi.cupto speed with openssl 1.1
TLS v1.2 support for cryptoapicert -- RSA onlyconfigure.ac | 1 + src/openvpn/Makefile.am | 2 +- src/openvpn/cryptoapi.c | 155 ++++++++++++++++++++++++++++++ ++++--------- src/openvpn/openssl_compat.h | 14 ++++ src/openvpn/options.c | 18 ----- 5 files changed, 140 insertions(+), 50 deletions(-)--
2.1.4------------------------------------------------------------
------------------
Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel
Hi,
Quoted 26 lines · Илья Шипицин
2018-01-08 7:21 GMT+05:00 <[email protected]...>:
Quoted 15 lines
From: Selva Nair <[email protected]...>
Hi,
I am not sure how receptive the crypto maintaineres are to the idea of adding more code into
cryptoapi.c, but here goes:I've been wanting to add TLS 1.2 support for certs in the Windows cert store using management external key. But that's a lot more work than extending cryptoapicert support. And, rather surprsingly, it turns out that the CNG API for signing is easy to use (well after some groping in the dark..) and doesn't take much to implement.
Quoted 5 lines
So these patches..
The first patch is not really related and to make the existing code "openssl-1.1 ready" (missed by past patches as no one probably builds Windows binary with 1.1..).
Quoted 5 lines
there was an agreement on one of the recent community meetings to gracefully deprecate both libressl and openssl-1.0.X in favour of openssl-1.1.X
so, we should learn how to build windows binary with 1.1.X :)
I had tested the patch with 1.1 and needed only minor changes to build script. See https://github.com/selvanair/openvpn-build
pkcs11-helper build showed some warnings but I did not check further as I usually disable it.
Selva
Hi,
Quoted 10 lines · On 8 January 2018 at 03:21, <[email protected]...> wrote:
I am not sure how receptive the crypto maintaineres are to the idea of adding more code into
cryptoapi.c, but here goes:I've been wanting to add TLS 1.2 support for certs in the Windows cert store using management external key. But that's a lot more work than extending cryptoapicert support. And, rather surprsingly, it turns out that the CNG API for signing is easy to use (well after some groping in the dark..) and doesn't take much to implement.
Quoted 8 lines
So these patches..
The first patch is not really related and to make the existing code "openssl-1.1 ready" (missed by past patches as no one probably builds Windows binary with 1.1..).
The second patch is not dependent on this, but close-by code paths are touched by both.
This would fix a major shortcoming in our current cryptoapi code, so I'm definitely open to accepting these patches. Just need to find some time to do the review and dreaded windows testing :)
I actually tried this myself a while ago, but quickly gave up after getting lost in and demotivated by the MSDN maze. So more than grateful that you wrestled your way through!
-Steffan