Recently I needed to do some performance testing of an SSL instance on a VM. I considered using JMeter, but decided to use OpenSSL to get a rudimentary picture instead.
To obtain a basic result, we connect to the server and pull the /index.php file. You can specify whatever file you’d like to download, or none at all if you simply want to test connections.1
openssl s_time -www /index.php -new -connect www.trustwave.com:443
Your result will look something like this:
No CIPHER specified Collecting connection statistics for 30 seconds ttttttttttttttttttttttttttttttttttttttttttttttttttttttttt 159 connections in 5.82s; 27.32 connections/user sec, bytes read 62328 159 connections in 31 real seconds, 392 bytes read per connection
If you’d like to get more specific with performance testing you can even use the -ciphers parameter to explicitly choose the negotiated cipher. You can obtain a list of available ciphers with “openssl ciphers”.
OpenSSL provides several tools that allow you to RSA encrypt/sign arbitrary data files. Of course, directly RSA encrypting large volumes of data is impractical because the encrypted/signed data cannot exceed the size of the key material. This is one of the reasons why SSL connections typically handshake and then pass an AES (or RC4, et cetera) key to do symmetric encryption thereafter.1
Generate a private key. You can change the last number to the preferred modulus size. Keys greater than 4096-bit will take a long time to generate.2
openssl genrsa -out private.pem 4096
With the private key we can now encrypt the data.
openssl rsautl -encrypt -inkey private.pem -in publicfile -out privatefile
To decrypt just reverse it.
openssl rsautl -decrypt -inkey private.pem -in privatefile -out publicfile
If you would rather sign the data…
openssl rsautl -sign -inkey private.pem -in filetosign -out signed_data
To verify the signature just use -verify.3
openssl rsautl -verify -inkey private.pem -in signed_data
Continuing the howto nature of this blog (and its peculiar obsession with OpenSSL), here’s a primer on packaging an arbitrary number of certificates into a single PKCS7 container. These files are quite useful for installing multiple certificates on Windows servers. They differ from PKCS12 (PFX) files in that they can’t store private keys. If you need to generate a PKCS12 then head to that article instead.
This example assumes that you have 2 different certificate files, each in PEM (Base64) format. You can add as many -certfile elements as you want to package in the file. Additionally, concatenated certificate chains are supported. 1
openssl crl2pkcs7 -nocrl -certfile cert1.cer -certfile cert2.cer -out outfile.p7b
If you deal with SSL/TLS long enough you will run into situations where you need to examine what certificates are being presented by a server to the client. The best way to examine the raw output is via (what else but) OpenSSL.1
First let’s do a standard webserver connection (-showcerts dumps the PEM encoded certificates themselves for more extensive parsing if you desire. The output below snips them for readability.):
openssl s_client -showcerts -connect www.domain.com:443
CONNECTED(00000003) --snip-- --- Certificate chain 0 s:/C=US/ST=Texas/L=Carrollton/O=Woot Inc/CN=*.woot.com i:/C=US/O=SecureTrust Corporation/CN=SecureTrust CA -----BEGIN CERTIFICATE----- --snip-- -----END CERTIFICATE----- 1 s:/C=US/O=SecureTrust Corporation/CN=SecureTrust CA i:/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority -----BEGIN CERTIFICATE----- --snip-- -----END CERTIFICATE----- --- Server certificate subject=/C=US/ST=Texas/L=Carrollton/O=Woot Inc/CN=*.woot.com issuer=/C=US/O=SecureTrust Corporation/CN=SecureTrust CA --- No client certificate CA names sent --- SSL handshake has read 2123 bytes and written 300 bytes --- New, TLSv1/SSLv3, Cipher is RC4-MD5 Server public key is 1024 bit --snip--
There’s a lot of data here so I have truncated several sections to increase readability. Points of interest:
But what if you want to connect to something other than a bog standard webserver on port 443? Well, if you need to use starttls that is also available. As of OpenSSL 0.9.8 you can choose from smtp, pop3, imap, and ftp as starttls options.
openssl s_client -showcerts -starttls imap -connect mail.domain.com:139
If you need to check using a specific SSL version (perhaps to verify if that method is available) you can do that as well. -ssl2, -ssl3, -tls1, and -dtls1 are all choices here.2
openssl s_client -showcerts -ssl2 -connect www.domain.com:443
You can also present a client certificate if you are attempting to debug issues with a connection that requires one.3
openssl s_client -showcerts -cert cert.cer -key cert.key -connect www.domain.com:443
And for those who really enjoy playing with SSL handshakes, you can even specify acceptable ciphers.4
openssl s_client -showcerts -cipher DHE-RSA-AES256-SHA -connect www.domain.com:443
The cipher used above should work for almost any Apache server, but will fail on IIS since it doesn’t support 256-bit AES encryption.
Have you ever wondered how big the “large primes” that RSA encryption is based on really are? What exactly does a “1024-bit” key mean anyway? And if the difficulty of RSA is partially based on factoring large numbers, how do we create these large primes without determining primality via factorization?
The easiest way to demonstrate these concepts is with a simple script, so let’s take a look at a large random number generator I wrote1 using Python. As is typical for this blog, you’ll note that the random numbers are not cryptographically secure. To make these examples simple I don’t want to introduce external dependencies, but please keep that issue in mind for any serious applications of the code presented here.
import random import math import sys def rabinMiller(n): s = n-1 t = 0 while s&1 == 0: s = s/2 t +=1 k = 0 while k<128: a = random.randrange(2,n-1) #a^s is computationally infeasible. we need a more intelligent approach #v = (a**s)%n #python's core math module can do modular exponentiation v = pow(a,s,n) #where values are (num,exp,mod) if v != 1: i=0 while v != (n-1): if i == t-1: return False else: i = i+1 v = (v**2)%n k+=2 return True def isPrime(n): #lowPrimes is all primes (sans 2, which is covered by the bitwise and operator) #under 1000. taking n modulo each lowPrime allows us to remove a huge chunk #of composite numbers from our potential pool without resorting to Rabin-Miller lowPrimes = [3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97 ,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179 ,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269 ,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367 ,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461 ,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571 ,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661 ,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773 ,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883 ,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997] if (n >= 3): if (n&1 != 0): for p in lowPrimes: if (n == p): return True if (n % p == 0): return False return rabinMiller(n) return False def generateLargePrime(k): #k is the desired bit length r=100*(math.log(k,2)+1) #number of attempts max r_ = r while r>0: #randrange is mersenne twister and is completely deterministic #unusable for serious crypto purposes n = random.randrange(2**(k-1),2**(k)) r-=1 if isPrime(n) == True: return n return "Failure after "+`r_` + " tries." print generateLargePrime(1024)
This code is very slow, but does not represent an entirely naïve approach.2 To generate a prime we first create a random integer in the range (2k-1,2k), then the following rules are applied:
If the number passes all these tests it is returned as a valid prime number. Of course, if you don’t trust the output from this script you can always check it with openssl…
openssl prime 7337488745629403488410174275830423641502142554560856136484326749638755396267050319392266204256751706077766067020335998122952792559058552724477442839630133 8C18E5DC98684E2A15B84535635A95C4A192B73B40A780AB4CB0C58BDB9C31EF970C3AC6D804712B830FB6F1B140693A251E989F89B687EBA62781AD031D5135 is prime
Click for an example of an 8192-bit prime created with the generateLargePrime() function. You can also check out a 1024-bit prime as well. 1024-bit keys are the minimum size recommended for end entity certificates using RSA (SSL certificates) at this time.3
Of course, a large prime is nice, but it isn’t necessarily an RSA prime. Look for another entry soon explaining the additional restrictions imposed by RSA.
On rare occasions you may find yourself with a self-signed internal CA that has expired while you are still using certificates issued from the CA. One potential solution to this problem is to self-sign a new cert with identical fields using the private key from the old certificate.1
You can fill in almost all the fields using the interactive prompt, but to ensure maximum compatibility be sure every field matches exactly. You will also need to set the serial number of the certificate via the -set_serial parameter (openssl takes this argument in decimal form, not hex)2.
openssl req -new -x509 -key previousprivatekey.pem -set_serial 0000 -out newroot.cer
You now have a new root certificate that will work with your previously issued certificates!