From 5bf63191474afe708b7fa18cbbc62619b81629a5 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Jul 29 2016 10:47:35 +0000 Subject: Added a section on usage of Hardware Security Modules (HSM). The NSS parts were contributed by Robert Relyea. --- diff --git a/en-US/Defensive_Coding.xml b/en-US/Defensive_Coding.xml index 58d9121..a8b6caa 100644 --- a/en-US/Defensive_Coding.xml +++ b/en-US/Defensive_Coding.xml @@ -27,6 +27,7 @@ Implementing Security Features + diff --git a/en-US/Features-HSM.xml b/en-US/Features-HSM.xml new file mode 100644 index 0000000..005161d --- /dev/null +++ b/en-US/Features-HSM.xml @@ -0,0 +1,187 @@ + + + + Hardware Security Modules and Smart Cards + + Hardware Security Modules (HSMs) are specialized hardware intended + to protect private keys on server systems. They store internally + the private keys (e.g., RSA keys), and provide access to operations + with the keys without exposing the keys. That access, is provided using + a standardized API, which across Fedora is PKCS#11. + + + Smart cards are small cards with a micro processor, often combined with a + USB reader ressembling a USB stick. They are very similar in nature with + HSMs as they can also be used to protect private keys and are almost + universally accessed via the PKCS#11 API. The main distinguishers from HSMs + is their inferior performance and often, the available hardware protection mechanisms. + + + Typically a smart card or HSM relies on a shared library to provide functionality. + This shared library follows the PKCS#11 API and thus is often referred to as + a PKCS#11 module. In Fedora the opensc + shared module (opensc-pkcs11.so) can be used for the majority + of smart cards available in the market. By convention these modules are located + at /usr/lib64/pkcs11. They can be used directly, or via + a higher level library. + + + All the major crypto libraries (NSS, GnuTLS and OpenSSL in Fedora) support + hardware security modules and smart cards, by providing wrappers over the + PKCS#11 API. However, the level of support varies, as well as the ease of + use of such modules and its integration to the overall library API. + + + + + The PKCS#11 API does provide an API to access HSMs or smart cards, but + does not provide any method of discovering which HSMs or smart cards are + available in the system. In Fedora and modules are registered via p11-kit + configuration files, stored at /etc/pkcs11/modules/. For applications using + engine_pkcs11 or GnuTLS the registered modules are + available without further configuration. Other applications will have to load + the p11-kit-proxy.so module. + + + + + Most crypto libraries support the PKCS#11 URLs scheme + to identify objects stored in an HSM, however that support is not yet universal. + Some support transparent usage of PKCS#11 objects, e.g., specifying + a PKCS#11 object instead of a file, while others require to use + specialized APIs for such objects. + + + + + Objects stored in an HSM or smart card can be protected with a PIN. As such, + libraries typically require to set a PIN handling function for accessing private keys, + or the PIN can be passed along with a PKCS#11 URL and the pin-value parameter. + + + + + Obtaining a Hardware Security Module, or including it on a continuous integration + testing is not always feasible. For testing purposes smart cards supported by the OpenSC + project can be used, as well as software modules like softhsm which + provides a tool to setup a software HSM, and a PKCS#11 library. + + + + + The PKCS#11 API requires applications that use fork to reinitialize the used PKCS#11 + modules. This is an uncommon requirement, which has led to several bugs across + applications in Fedora which used PKCS#11 directly. To make things more complicated + software PKCS#11 module like softhsm do not require this re-initialization + leading to applications working against software modules but failing with hardware + modules or smart cards. The wrapper PKCS#11 APIs provided by NSS, GNUTLS and + engine_pkcs11 (OpenSSL) handle the reinitialization after fork requirement transparently. + + + +
+ OpenSSL HSM Support + + OpenSSL does not have native support for PKCS#11. It can + provide PKCS#11 support through the OpenSC's project + pkcs11 engine (formerly known as engine_pkcs11). + As such software intended to use HSMs, must utilize that engine. + + + Engine pkcs11 supports loading stored objects via PKCS#11 URLs. + If no PKCS#11 module is specified the engine will use the system-wide registered + modules via p11-kit-proxy.so. + + + Possible conflict with SELinux + + The p11-kit-proxy.so module relies on code generation (function rewrite), + and as such it may fail to operate in contexts where pages can be written but not executed. + This is often the SELinux context under which servers like apache are executed. + + + + The following example demonstrates the initialization of the pkcs11 engine + and its usage to sign data. + + + Signing data with HSM and OpenSSL + + + +
+
+ GNUTLS HSM Support + + GNUTLS supports PKCS#11 natively. Most of the API functions + accepting certificate files, can also accept PKCS#11 URLs, thus + requiring minor or no modifications to applications in order + to support HSMs. In most cases applications must be modified + to install a PIN callback function. + + + The following example demonstrates the initialization of the pkcs11 engine + and its usage to sign data. + + + Signing data with HSM and GnuTLS + + + + The PIN callback function can be either set globally as in + the example above or locally by utilizing functions such as gnutls_privkey_set_pin_function. + An example PIN callback function is shown below. + + + An example PIN callback with GNUTLS + + +
+
+ NSS HSM Support + + NSS supports PKCS#11 natively. In fact all NSS crypto operations, + including builtin operations, go through PKCS #11 modules. NSS provides + its own software PKCS #11 module called softoken. NSS automatically + loads any PKCS #11 module specified in its module database, which can + be manipulated with the modutil command. NSS uses the PKCS #11 module + that contains the requested keys to do the crypto operations. As long as + the application opens an NSS database and properly sets a pin callback. If + it runs with native NSS, it should be able to use HSMs that provide PKCS #11 + modules. Modules can also be loaded programatically, though this is less common. + + + The following example demonstrates a typical NSS application for signing. + + + Signing data with HSM and NSS + + + + To use the example above with an HSM or smart card you will need to do the following. + + + +# add your HSM or token library to an NSS database (in the sample code the database is +# located in the current directory'.') +$ modutil -add "My HSM" -libfile ${path_to_pkcs11_file} -dbdir . +# Find the token name on your HSM +$ modutil -list -dbdir . +# find the cert on your token +$ certutil -L -h ${token_name} -d . +# pass the cert to your signing program +$ NSS_Sign_Example "${token_name}:${cert_name}" + + + + An example PIN callback with NSS + + +
+
diff --git a/publican.cfg b/publican.cfg index 66e64a8..3f09e82 100644 --- a/publican.cfg +++ b/publican.cfg @@ -1,6 +1,6 @@ xml_lang: en-US #brand: RedHat-EngServices -brand: fedora +brand: common chunk_section_depth: 3 #product: Defensive_Coding #mainfile: Defensive_Coding diff --git a/src/HSM-GNUTLS.c b/src/HSM-GNUTLS.c new file mode 100644 index 0000000..4c53b5e --- /dev/null +++ b/src/HSM-GNUTLS.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include + +//+ Features HSM-GNUTLS-PIN +int pin_function(void *userdata, int attempt, const char *token_url, + const char *token_label, unsigned flags, char *pin, size_t pin_max) +{ + if (flags & GNUTLS_PIN_FINAL_TRY) + printf("This is the final try before locking!\n"); + if (flags & GNUTLS_PIN_COUNT_LOW) + printf("Only few tries left before locking!\n"); + if (flags & GNUTLS_PIN_WRONG) + printf("Wrong PIN has been provided in the previous attempt\n"); + + /* userdata is the second value passed to gnutls_pkcs11_set_pin_function() + * in this example we passed the PIN as a null terminated value. + */ + snprintf(pin, pin_max, "%s", (char*)userdata); + return 0; +} +//- + +/* This program accepts on the command line: + * 1. A PKCS#11 URL specifying a private key + * 2. A PIN + * 3. A PKCS#11 shared module (optional) + * + * And signs test data with the provided key. + * + * Example: ./a.out "pkcs11:object=myobject" 1234 /usr/lib64/pkcs11/opensc-pkcs11.so + */ +int main(int argc, char **argv) +{ + gnutls_privkey_t private_key; + char *private_key_name; + char *key_pass = NULL; + const char *module_path = NULL; + gnutls_datum_t testdata = {(void*)"TESTDATA", sizeof("TESTDATA")-1}; + gnutls_datum_t signature; + int ret; + + if (argc < 2) { + fprintf(stderr, "usage: %s [private key URL] [PIN] [module]\n", argv[0]); + fprintf(stderr, "\n"); + exit(1); + } + + private_key_name = argv[1]; + key_pass = argv[2]; + if (argc >= 3) + module_path = argv[3]; + + //+ Features HSM-GNUTLS + if (module_path) { + ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL); + if (ret < 0) { + fprintf(stderr, "error in %d: %s\n", __LINE__, gnutls_strerror(ret)); + exit(1); + } + + ret = gnutls_pkcs11_add_provider(module_path, NULL); + if (ret < 0) { + fprintf(stderr, "error in %d: %s\n", __LINE__, gnutls_strerror(ret)); + exit(1); + } + } + + if (key_pass) + gnutls_pkcs11_set_pin_function(pin_function, key_pass); + + ret = gnutls_privkey_init(&private_key); + if (ret < 0) { + fprintf(stderr, "error in %d: %s\n", __LINE__, gnutls_strerror(ret)); + exit(1); + } + + ret = gnutls_privkey_import_url(private_key, private_key_name, 0); + if (ret < 0) { + fprintf(stderr, "error in %d: %s\n", __LINE__, gnutls_strerror(ret)); + exit(1); + } + + ret = gnutls_privkey_sign_data(private_key, GNUTLS_DIG_SHA256, 0, + &testdata, &signature); + if (ret < 0) { + fprintf(stderr, "error in %d: %s\n", __LINE__, gnutls_strerror(ret)); + exit(1); + } + + gnutls_privkey_deinit(private_key); + gnutls_free(signature.data); + //- + + return 0; +} diff --git a/src/HSM-NSS.c b/src/HSM-NSS.c new file mode 100644 index 0000000..7032ae4 --- /dev/null +++ b/src/HSM-NSS.c @@ -0,0 +1,158 @@ +/* Example code to illustrate PKI crypto ops (encrypt with public key, + * decrypt with private key) + * + * Code assumes that you have set up a NSS database with a certificate + * and a private key. + * Here is one way of doing it: + * # create CA cert db, if -f not provided, prompts for passwd + * $ certutil -N -d . + * + * # load your hsm (optional) To do so you need to know where your + * # pkcs #11 module lives. replace {path_to_pkcs11_library} with + * # the actual path and library name (example /usr/lib64/libcoolkeypk11.so + * $ modultil -add "HSM Module" -libfile {path_to_pkcs11_library} -dbdir . + * + * # create CA cert, self-signed, generates key-pair, prompts for key + * # type, cert type etc + * # {token_name} is the name of your PKCS #11 token. You can find + * # the token name using 'modutil --list -dbdir .'. If you are using + * # softoken you can skip the -h {token_name} + * # answers for prompts: 5,9,n,y,-1,n,5,6,7,9,n + * $ certutil -S -s -h {token_name} \ + * "CN=Test CA, O=BOGUS Inc, L=Mtn View, ST=CA, C=US" \ + * -n TestCA -t CTu,CTu,CTu -v 60 -x -d . -1 -2 -5 + * + * Run the program with "{token_name}:TestCA" as the command line argument. + * You will be prompted for the pin. + * + * There are many ways to setup a public/private key to use - this + * example shows one of them. + * + * This example does not do any padding. It simply encrypts/decrypts a block + * of length equal to modulus length of the public/private key. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *pin = NULL; +/* this callback is responsible for returning the password to the NSS + * key database. for example purposes, this function hardcodes the password. + * In a real app, this function should obtain the password using secure means + * such as prompting an operator, or retrieving it over a secure communication + * channel + */ +char *passwdcb(PK11SlotInfo * info, PRBool retry, void *arg); + +int main(int argc, char **argv) +{ + //+ Features HSM-NSS + SECStatus rv; + CERTCertificate *cert = NULL; + SECKEYPrivateKey *pvtkey = NULL; + SECItem signature = { siBuffer, NULL, 0 }; + SECOidTag algTag; + int r = 1; + unsigned char buf[] = "test data to sign"; + const char *cert_name; + unsigned i; + + if (argc < 3) { + fprintf(stderr, "usage: %s [cert name] [PIN]\n\n", argv[0]); + exit(1); + } + + cert_name = argv[1]; + pin = argv[2]; + + PK11_SetPasswordFunc(passwdcb); + NSS_InitializePRErrorTable(); + rv = NSS_Init("."); + if (rv != SECSuccess) { + fprintf(stderr, "NSS initialization failed (err %d)\n", PR_GetError()); + goto cleanup; + } + + cert = PK11_FindCertFromNickname(cert_name, NULL); + if (cert == NULL) { + fprintf(stderr, "Couldn't find cert %s in NSS db (err %d: %s)\n", + cert_name, PR_GetError(), PORT_ErrorToString(PR_GetError())); + goto cleanup; + } + + fprintf(stderr, "Buffer being signed = \n%s\n", buf); + + pvtkey = PK11_FindKeyByAnyCert(cert, NULL); + if (pvtkey == NULL) { + fprintf(stderr, "Couldn't find private key for cert %s (err %d: %s)\n", + cert_name, PR_GetError(), PORT_ErrorToString(PR_GetError())); + goto cleanup; + } + + /* get the algtag. Pick the default hash algorithm */ + algTag = SEC_GetSignatureAlgorithmOidTag(pvtkey->keyType, SEC_OID_UNKNOWN); + + fprintf(stderr, "Signing with alg = %s (%d)\n", + SECOID_FindOIDTagDescription(algTag), algTag); + + rv = SEC_SignData(&signature, buf, sizeof(buf)-1, pvtkey, algTag); + if (rv != SECSuccess) { + fprintf(stderr, "sign with Private Key failed (err %d: %s)\n", + PR_GetError(), PORT_ErrorToString(PR_GetError())); + goto cleanup; + } + //- + + fprintf(stderr, "Signature len = %d\n", signature.len); + fprintf(stderr, "Signature data = "); + /* dump signature.data */ + for (i = 0; i < signature.len; i++) { + if ((i & 0xf) == 0) + printf("\n"); + printf("%02x ", signature.data[i]); + } + printf("\n"); + + r = 0; + + cleanup: + if (cert) + CERT_DestroyCertificate(cert); + if (pvtkey) + SECKEY_DestroyPrivateKey(pvtkey); + if (signature.data) + SECITEM_FreeItem(&signature, PR_FALSE); + exit(r); +} + +//+ Features HSM-NSS-PIN +char *passwdcb(PK11SlotInfo * slot, PRBool retry, void *arg) +{ + if (!isatty(STDIN_FILENO) && retry) { + /* we're just reading from a file, and the value is known to be wrong, + * don't keep bounding the token with the wrong password. */ + return NULL; + } + + if (retry) { + printf("Warning: Wrong PIN has been provided in the previous attempt\n"); + if (PK11_IsHW(slot)) { + printf + (" NOTE: multiple pin failures could result in locking your device\n"); + } + } + + if (pin == NULL) + return pin; + else + return strdup(pin); +} +//- diff --git a/src/HSM-OpenSSL.c b/src/HSM-OpenSSL.c new file mode 100644 index 0000000..9950deb --- /dev/null +++ b/src/HSM-OpenSSL.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void display_openssl_errors(int l) +{ + const char *file; + char buf[120]; + int e, line; + + if (ERR_peek_error() == 0) + return; + fprintf(stderr, "At %s:%d:\n", __FILE__, l); + + while ((e = ERR_get_error_line(&file, &line))) { + ERR_error_string(e, buf); + fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line); + } +} + +/* This program accepts on the command line: + * 1. A PKCS#11 URL specifying a private key + * 2. A PIN + * 3. A PKCS#11 shared module (optional) + * + * And signs test data with the provided key. + * + * Example: ./a.out "pkcs11:object=myobject" 1234 /usr/lib64/pkcs11/opensc-pkcs11.so + */ +int main(int argc, char **argv) +{ + char *private_key_name; + unsigned char buf[4096]; + const EVP_MD *digest_algo; + EVP_PKEY *private_key; + char *key_pass = NULL; + unsigned n; + ENGINE *e; + EVP_MD_CTX ctx; + const char *module_path = NULL; + + if (argc < 2) { + fprintf(stderr, "usage: %s [private key URL] [PIN] [module]\n", argv[0]); + fprintf(stderr, "\n"); + exit(1); + } + + private_key_name = argv[1]; + key_pass = argv[2]; + if (argc >= 3) + module_path = argv[3]; + + //+ Features HSM-OpenSSL + OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + ERR_clear_error(); + ENGINE_load_builtin_engines(); + + e = ENGINE_by_id("pkcs11"); + if (!e) { + display_openssl_errors(__LINE__); + exit(1); + } + + if (module_path) { + fprintf(stderr, "loading: %s\n", module_path); + if (!ENGINE_ctrl_cmd_string(e, "MODULE_PATH", module_path, 0)) { + display_openssl_errors(__LINE__); + exit(1); + } + } + + if (!ENGINE_init(e)) { + display_openssl_errors(__LINE__); + exit(1); + } + + if (key_pass && !ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0)) { + display_openssl_errors(__LINE__); + exit(1); + } + + private_key = ENGINE_load_private_key(e, private_key_name, NULL, NULL); + if (!private_key) { + fprintf(stderr, "cannot load: %s\n", private_key_name); + display_openssl_errors(__LINE__); + exit(1); + } + + display_openssl_errors(__LINE__); + + digest_algo = EVP_get_digestbyname("sha256"); + + EVP_MD_CTX_init(&ctx); + if (EVP_DigestInit(&ctx, digest_algo) <= 0) { + display_openssl_errors(__LINE__); + exit(1); + } + + EVP_SignInit(&ctx, digest_algo); + +#define TEST_DATA "test data" + if (EVP_SignUpdate(&ctx, TEST_DATA, sizeof(TEST_DATA) - 1) <= 0) { + display_openssl_errors(__LINE__); + exit(1); + } + + n = sizeof(buf); + if (EVP_SignFinal(&ctx, buf, &n, private_key) <= 0) { + display_openssl_errors(__LINE__); + exit(1); + } + + EVP_PKEY_free(private_key); + ENGINE_finish(e); + //- + + return 0; +} diff --git a/src/src.mk b/src/src.mk index 18bd592..f2047ac 100644 --- a/src/src.mk +++ b/src/src.mk @@ -32,13 +32,17 @@ JCFLAGS_TLSClientOpenJDK = -source 1.6 -target 1.6 # List fiels which will be compiled and linked, together with # additional dependencies. compile_and_link += C-String-Functions -compile_and_link += TLS-Client-OpenSSL +compile_and_link += TLS-Client-OpenSSL HSM-OpenSSL LIBS_TLS-Client-OpenSSL = -lssl -lcrypto -compile_and_link += TLS-Client-GNUTLS +LIBS_HSM-OpenSSL = -lssl -lcrypto +compile_and_link += TLS-Client-GNUTLS HSM-GNUTLS LIBS_TLS-Client-GNUTLS = -lgnutls -compile_and_link += TLS-Client-NSS +LIBS_HSM-GNUTLS = -lgnutls +compile_and_link += TLS-Client-NSS HSM-NSS CFLAGS_TLS-Client-NSS = -I/usr/include/nspr4 -I/usr/include/nss3 LIBS_TLS-Client-NSS = -lnss3 -lnspr4 -lssl3 +CFLAGS_HSM-NSS = -I/usr/include/nss3 -I/usr/include/nspr4 +LIBS_HSM-NSS = -lnss3 -lnspr4 -lssl3 -lnssutil3 compile_and_link += XML-Parser-Expat LIBS_XML-Parser-Expat = -lexpat compile_and_link += XML-Parser-Qt