This step-by-step guide shows the way to smoothly build FIPS capable OpenSSL library for use in the FIPS 140-2 compliant Tomcat server on Windows machines.

What is FIPS 140-2?

The Federal Information Processing Standard 140-2 is a security standard published by the National Institute of Standards and Technology (NIST), covering specification of security requirements for implementing cryptographic modules. Cryptographic module may be either a library, a component of a product or application, or a complete product.

The specifications include e.g. a list of approved algorithms, module inputs and outputs, physical security, cryptographic key management and more areas related to the secure design.

NIST manages a list of FIPS 140-1 and FIPS 140-2 validated cryptographic modules, i.e. modules tested, validated and certified under the Cryptographic Module Validation Program. The complete list can be found here: http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140val-all.htm.

FIPS 140-2 compliant Tomcat

The compliance (unlike FIPS validation) means that only FIPS approved algorithms and validated modules are used in the product, but the product itself was not validated.

Apache Tomcat, an open source Java application server, can use two different implementations of the SSL/TLS protocol, and thus there are two options for achieving FIPS 140-2 compliance:

  • JSSE, the Java implementation – it is needed to have enabled only FIPS validated Java cryptographic providers along with the correct setting of the ciphers and algorithms in the Tomcat HTTPS connector
  • Apache Portable Runtime, the OpenSSL implementation – if FIPS 140-2 is supported by the linked OpenSSL library, so called FIPS Mode can be enabled in the Tomcat settings

OpenSSL FIPS Object Module

OpenSSL library is not FIPS validated. A special software component called OpenSSL FIPS Object Module was created instead.

OpenSSL being compiled with the OpenSSL FIPS Object Module embedded inside is so called FIPS capable OpenSSL. It provides the standard, non-FIPS API as well as a FIPS 140-2 Approved Mode, a setting in products using this library in which only FIPS 140-2 validated cryptography is used and non-FIPS approved algorithms are disabled.

Current version of OpenSSL FIPS Object Module is 2.0 and is compatible with standard OpenSSL 1.0.1 and 1.0.2 distributions.

Step zero: Prerequisites

For the whole following building process, the Developer Command Prompt for Visual Studio is required. It is one of the optional choices offered during VS installation. When installing VS, check the following option (example for VS 2015):

  • Programming languages\Visual C++\Common Tools for Visual C++ 2015

In case Visual Studio is already installed without Developer Command Prompt, you can add this feature by program modification:

Start -> Programs and Features -> Microsoft Visual Studio 2015 -> Change -> Modify

The following window should appear. Again, check the aforementioned option.

The guide was tested using Visual Studio Professional 2015. Both, the aforementioned option for the Developer Command Prompt for Visual Studio installation and batch files needed in the following process, may differ in other versions.

Step one: Getting the source codes

Download the Windows sources for:

Unpack:

  • Tomcat Native
  • OpenSSL FIPS Object Module to a directory outside Tomcat Native.
  • OpenSSL sources to tomcat-native-X\native\srclib\openssl
  • Apache Portable Runtime sources to tomcat-native-X\native\srclib\apr

Step two: Building the OpenSSL FIPS Object Module

Prerequisites:

  • Developer Command Prompt for Visual Studio
  • Extracted OpenSSL FIPS Object Module files
  • Perl installed and location added to the PATH system variable

Compilation (64-bit version):

  1. Open Developer Command Prompt:
    Start -> Developer Command Prompt for VS2015
  2. Add variables for desired environment:
    cd vc
    vcvarsall x64
    
  3. Navigate to the extracted OpenSSL FIPS Object Module sources:
    cd openssl-fips-X\
    
  4. Set needed variables:
    Set PROCESSOR_ARCHITECTURE=AMD64
    Set FIPSDIR=absolute\path\to\Openssl-fips-X
    
  5. [Optional] In case you use Cygwin Perl, you may encounter an error (“No rule for …”) during the build process. In order to prevent this issue, open the openssl-fips-X\util\mk1mf.pl file in text editor, find the first chop; command and add the following to the next row:
    s/\s*$//;
    
  6. Build the OpenSSL FIPS Object Module
    ms\do_fips
    

The compilation process for the 32-bit version:

cd vc
vcvarsall x86
cd openssl-fips-X\
Set PROCESSOR_ARCHITECTURE=x86
Set FIPSDIR=absolute\path\to\Openssl-fips-X
ms\do_fips

Step three: Building the FIPS capable OpenSSL

Prerequisites:

  • Developer Command Prompt for Visual Studio
  • Compiled FIPS module
  • OpenSSL 1.0.1 or 1.0.2 sources extracted in the tomcat-native-X\native\srclib\openssl folder
  • Perl installed and location added to the PATH system variable (note that Cygwin Perl may have issues with backslash in addresses)
  • NASM (Netwide Assembler)  installed and location added to the PATH system variable

Compilation (64-bit version):

  1. Open Developer Command Prompt:
    Start -> Developer Command Prompt for VS2015
  2. Add variables for desired environment:
    cd vc
    vcvarsall x64
    
  3. Navigate to the extracted OpenSSL sources:
    cd native\srclib\openssl\
    
  4. Configure and make:
    perl Configure VC-WIN64A fips --with-fipsdir=absolute\path\to\Openssl-fips-X
    ms\do_win64a
    nmake -f ms\nt.mak
    

The compilation process for the 32-bit version:

cd vc
vcvarsall x86
cd native\srclib\openssl\
perl Configure VC-WIN32 fips --with-fipsdir=absolute\path\to\Openssl-fips-X
ms\do_nasm
nmake -f ms\nt.mak

Version check:

FIPS capable OpenSSL contains information about this fact in its version info. Check the version of your compiled OpenSSL library:

Step four: Building APR

Prerequisites:

  • Developer Command Prompt for Visual Studio
  • Apache Portable Runtime sources extracted in the tomcat-native-X\native\srclib\apr folder

Compilation (64-bit version):

  1. Open Developer Command Prompt:
    Start -> Developer Command Prompt for VS2015
  2. Add variables for desired environment:
    cd vc
    vcvarsall x64
    
  3. Navigate to the extracted APR sources:
    cd native\srclib\apr\
    
  4. Build Apache Portable Runtime:
    nmake -f NMAKEmakefile BUILD_CPU=x64 APR_DECLARE_STATIC=1
    nmake -f NMAKEmakefile BUILD_CPU=x64 APR_DECLARE_STATIC=1 install
    

The compilation process for the 32-bit version:

cd vc
vcvarsall x86
cd native\srclib\apr\
nmake -f NMAKEmakefile BUILD_CPU=x86 APR_DECLARE_STATIC=1
nmake -f NMAKEmakefile BUILD_CPU=x86 APR_DECLARE_STATIC=1 install

By default, the compiled files should appear in C:\include\ and C:\lib\ folders.

Step four and a half: Cleaning the mess

It is recommended to create an appropriate file system structure before proceeding to the compilation of the Tomcat Native library.

Create the following folders:

  • deps
  • deps\openssl
  • deps\openssl\lib
  • deps\openssl\include
  • deps\apr
  • deps\apr\lib
  • deps\apr\include

And copy the following files:

  • native\srclib\openssl\out32\openssl.exe to deps\openssl
  • native\srclib\openssl\out32\ssleay32.lib, native\srclib\openssl\out32\libeayfips32.lib and native\srclib\openssl\out32\libeaycompat32.lib to deps\openssl\lib
  • content of native\srclib\openssl\inc32\ to deps\openssl\include
  • C:\lib\apr-1.lib to deps\apr-1\lib
  • content of C:\include\apr-1\ to deps\apr\include

Step five: Building Tomcat Native library

Prerequisites:

  • Developer Command Prompt for Visual Studio
  • Compiled FIPS capable OpenSSL and APR
  • Java installed and JAVA_HOME system variable leading to the location set

Compilation (64-bit version):

  1. Open Developer Command Prompt:
    Start -> Developer Command Prompt for VS2015
  2. Add variables for desired environment:
    cd vc
    vcvarsall x64
    
  3. Navigate to the extracted Tomcat Native sources:
    cd tomcat-native-X\native\
    
  4. Set needed variables:
    Set CPU=X64
    Set FIPSDIR=absolute\path\to\Openssl-fips-X
    
  5. Build FIPS capable Tomcat Native library
    nmake -f NMAKEMakefile WITH_APR=path\to\deps\apr WITH_OPENSSL=path\to\deps\openssl APR_DECLARE_STATIC=1 [ENABLE_OCSP=1] WITH_FIPS=1
    

The compilation process for the 32-bit version:

cd vc
vcvarsall x86
cd tomcat-native-X\native\
Set CPU=X86
Set FIPSDIR=absolute\path\to\Openssl-fips-X
nmake -f NMAKEMakefile WITH_APR=path\to\deps\apr WITH_OPENSSL=path\to\deps\openssl APR_DECLARE_STATIC=1 [ENABLE_OCSP=1] WITH_FIPS=1

Compiled files should appear in the tomcat-native-X\native\WINXP_X64_DLL_RELEASE or tomcat-native-X\native\WINXP_X86_DLL_RELEASE folder.

Tomcat settings

Now that we have FIPS capable Tomcat Native library, the last action needed is the configuration of Tomcat to use the FIPS validated implementation.

  1. Copy the compiled tcnative-1.dll to your tomcat\bin folder.
  2. In the tomcat\conf\server.xml file edit following tags:
    Enable FIPS Mode for the APR listener:

    <Listener
        className="org.apache.catalina.core.AprLifecycleListener"
        SSLEngine="on"
        FIPSMode="on"
    />
    

    Configure the HTTPS connector to use Native (OpenSSL) implementation of SSL/TLS protocol:

    <Connector
        protocol="org.apache.coyote.http11.Http11AprProtocol"
        …
    />
    
  3. Restart the Apache Tomcat service

And that’s it! Your Tomcat is now using only FIPS approved algorithms and FIPS validated implementations.

Highest tested versions

This guide was tested with the following component versions:

  • Apache Portable Runtime 1.5.2
  • OpenSSL 1.0.2l
  • OpenSSL FIPS Object Module 2.0.16
  • Tomcat Native 1.2.12

Most systems today need to handle the user authentication. That means, the password entered during user registration must be stored in the system for later comparison.

It is obvious that the passwords must not be stored in plain-text form. In that case, if an attacker succeeded in getting access to the database, where these passwords are stored (e.g. using SQL Injection), he would obtain the whole list of user names with their corresponding passwords. Then it is very simple for him to impersonate a valid user.

Hashing

However, to check, if the password entered by the user is correct, we do not need the original password. It is enough to have a suitable information, which uniquely identifies it and can be easily computed from each password entering the system.

Such information is the password hash. Hash algorithm is a one-way function, generating a fixed-length string from the inputs (in this case from the given password) with no possibility to derive these inputs back from the computed string. Another property of a cryptographic hash function is that change of one input bit leads to change of many bits in the resulting hash. When the hash function is collision-free, we can assume that the identical hashes imply the identical inputs, from which these hashes are computed.

So instead of the password itself, only its hash will be stored in the system. Every time a user tries to login to the system, hash of the password entered is computed and compared to the stored one.

Slow hashing

However, cryptographic hash functions such as MD5 or SHA are not appropriate. The purpose of these functions is calculation of digest of large amount of data to ensure its integrity. This digest needs to be computed in as short time as possible, and thus these hash functions are designed to be fast. This property is, however, not desirable for password hashing.

As an example take the MD5 function. One 2.13GHz core is able to compute cca 6 million MD5 hashes per second using Cain & Abel tool. Trying every single possible 8 character long lowercase alphanumeric password then takes approximately 130 hours. And that is only one core. Modern computers use more of them, for example with six such cores a password can be cracked in less than a day. Furthermore, we can definitely assume that an attacker has much better equipment.

In order to prevent an attacker from trying millions of hashes per second, we need to use a slow cryptographic hash function for password hashing. Several hash functions were specifically designed for this purpose. These functions include: PBKDF2, bcrypt, scrypt.

Work factor parameters

These hash functions are not only slow, they also come up with work factor parameters defining how expensive the hash computation will be. Although the scrypt function is the youngest one (designed in 2009), it has an advantage over the older ones – it not only defines the CPU cost, but also the memory requirements. That is why scrypt is recommended function for password storage and this article talks mainly about it.

Scrypt uses following work factor parameters:

  • N – number of iterations, related to both memory and CPU cost
  • r – size of the RAM block needed, related to memory cost
  • p – parallelization, defines maximum number of threads, related to CPU cost

These parameters allow to set the memory needed and time it takes to compute one hash. The approximate memory usage for a single hash generation can be computed from the parameters using the following formula:

memory  =  N  ·  2  ·  r  ·  64

The time, on the other hand, is platform-dependent. The graph below shows dependency of time needed for single hash computation on the work factor parameters N and r. The parallelization parameter is set to 1 in all cases. The values in the graph were measured using CryptSharp, the C# implementation of scrypt function, on Windows Server 2012 with four 2.2GHz cores.

csharp_Server12_scrypt

It is needed to specify the computation time as a compromise between the usability and security provided. For example, if we have a system with only one login at a time and high security is needed, we set the parameters to make computation take cca one second. However, in case of many parallel logins this time needs to be set to only few milliseconds.

We can take the above example of password hash cracking. Using scrypt function (CryptSharp implementation) with parameters N=210, r=4 and p=1, hashing of one password takes approximately 10ms, i.e. this 2.2GHz core is able to compute 100 hashes per second. Then computation of all possible 8 character long lowercase alphanumeric passwords takes 895 years.

Attacker goals

Imagine an attacker, who obtained the list of user names and corresponding password hashes. There are now three goals he can have:

  • Crack a password of one specific user (e.g. admin)
  • Crack a password of any user
  • Crack passwords of a longer list of users

Attacks

In the first option the attacker has a password hash and wants to find the corresponding password it was computed from. He can use brute force or dictionary attack, i.e. try many possible inputs to the hashing function and compare the results with the obtained password hash.
An effective method for trying so many hashes is usage of lookup tables. The general idea is to pre-compute hashes of possible passwords and store them in a lookup table data structure (or Rainbow tables for lower memory requirements). Comparison of these pre-computed values with given hash is much faster than hash computation.

The second option is simpler. The only thing needed is to compute hashes of possible inputs and compare each result with all password hashes in the obtained list. Sooner or later the attacker will hit some match.

For cracking a longer list of hashes the attacker does not need to crack one password at a time, he will instead compare each computed hash with all hashes from the list. This way cracking of a hashes list takes approximately the same time as cracking only one specific password.

Salt

The above attacks work because each password is hashed the same way, the same password always results in the same hash. The simplest way of preventing against this is salting. That means, a random string (salt) is generated for each password and used together with it to create a hash.
It is needed to ensure uniqueness of the salts, thus they really need to be randomly generated. Any random number generator can be used, however, cryptographically secure RNGs, such as RNGCryptoServiceProvider in C# or SecureRandom in Java, are recommended.

The salt is a non-secret value, it needs to be stored together with the password hash to ensure its availability to the hash function. Thus, if someone gets access to the hashes, he automatically gets also all the salts. However, the salt power is not in its secrecy, but in randomness.

With different salt, same passwords result in different hashes. Pre-computed hash attack is infeasible due to a large additional memory requirements – an attacker needs to store pre-computed hashes for each possible salt.

Cracking password of any user is reduced to cracking password of a specific one, since the salt for each user password is different.

Also cracking of a larger list of hashes is more complicated with different salt for each password, the attacker has no other choice than cracking one password at a time.

Pepper

In order to increase security even more, we can use another randomly generated string – pepper. In comparison to the salt, pepper needs to be kept secret as it is used as an HMAC key. HMAC is a one-way algorithm based on hash function generating fixed-length string from the input message and a secret key, which in our case is generated pepper.

Since pepper is a secret key, it needs to be generated using a cryptographically secure random number generator, such as RNGCryptoServiceProvider.

public static byte[] GeneratePepper()
{
    byte[] pepper = new byte[32];
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetBytes(pepper);
    return pepper;
}

When generated, the pepper must be stored separately in a configuration file with restricted access.

Although an attacker had enough resources to be able to crack the hash function, he would still need this secret value for obtaining the user password. And with pepper randomly generated for each system instance, if one instance is compromised, other remain secure.

Overall scheme

The overall hashing of the password with both salt and pepper looks as follows:

scrypt ( Base64 ( HMAC ( ‘SHA256’, password, pepper ) ), salt, workFactors )

And the C# implementation of this scheme using CryptSharp library:

public byte[] HashPassword(String password, byte[] pepper, byte[] salt)
{
    if (salt == null)
    {
        Console.WriteLine("Password hash not created - salt is null.");
        return null;
    }

    String encodedHmac = HmacBase64(password, pepper);
    return CryptSharp.Utility.SCrypt.ComputeDerivedKey(Encoding.UTF8.
         GetBytes(encodedHmac), salt, n, r, p, null, HASH_LENGTH);
}

private static string HmacBase64(string password, byte[] pepper)
{
    if (pepper == null)
    {
        Console.WriteLine("Password hash not created - pepper is null.");
        return null;
    }
    HMACSHA256 hmac = new HMACSHA256(pepper);
    hmac.Initialize();
    byte[] buffer = Encoding.UTF8.GetBytes(password);
    byte[] rawHmac = hmac.ComputeHash(buffer);
    return System.Convert.ToBase64String(rawHmac);
}

Conclusion

User passwords must never be stored as plain text, always compute its hash using a slow cryptographic hash function. To each password generate random salt and use this value together with the password for hash computation. For higher level of security generate random secret pepper for each system instance.

Of course, security of the user password depends on the password itself. An attacker could still try frequently used passwords such as “123456”, however, with secure storage we can protect him from trying too many of them and from obtaining the strong ones.