Forum Discussion

RiverFish's avatar
RiverFish
Icon for Altostratus rankAltostratus
Nov 27, 2012

Generate SHA1 thumbprint of incoming SSL cert

Greetings! I have a request from a developer (below). I was hoping one of you could please help me come up with a solution?

 

---------------

 

The F5 needs to generate an SHA1 thumbprint of the incoming SSL certificate and add the output hexadecimal encoded string as a new HTTP header to be passed along to the application. The generated thumbprint is a standard SHA1 thumbprint for identification purposes.

 

 

Example HTTP Header:

 

ClientCert-Thumbprint: a448327eff9283928b9d9993049f0386

 

---------------

 

Below is the existing iRule that is in place currently:

 

when CLIENTSSL_CLIENTCERT {

 

set cert_subject [X509::subject [SSL::cert 0]]

 

if { $cert_subject == "" }

 

{ log "[IP::client_addr]:[TCP::client_port]: No client cert found!"}

 

}

 

when HTTP_REQUEST {

 

if { [info exist cert_subject] } {

 

HTTP::header insert SSLClientCertSubject $cert_subject

 

return

 

}

 

}

 

 

Thanks!

 

20 Replies

  • So this:

     

     

    binary scan [sha1 [SSL::cert 0]] H* cert_hex

     

    HTTP::header insert ClientCert-Thumbprint $cert_hex

     

     

    is the same as this:

     

     

    when CLIENTSSL_CLIENTCERT {

     

    set client_cert [SSL::cert 0]

     

    log local0 "Cert Thumbprint - [X509::subject $client_Hash]"

     

    set cert_hash [X509::hash $client_cert]

     

    }

     

    when HTTP_REQUEST {

     

    if { [info exist cert_hash] } {

     

    HTTP::header insert ClientCertThumbprint $cert_hash

     

    return

     

    }

     

    }

     

     

    ?
  • X509::hash will return the md5 hash of the binary cert. sha1 will return the sha1 hash.

     

     

    Also, a client can resume an existing SSL session and not present the client cert in a new handshake for every TCP connection or HTTP request. TMM automatically stores the cert for the life of the SSL session and makes it accessible using [SSL::cert 0]. So it's better to get the value on each HTTP request instead of storing it in a local variable which is specific to that one TCP connection.

     

     

    Long story short...the last example I posted should work for your scenario.

     

     

    Aaron
  • Hoolio, I ran your iRule accross the dev guys one last time to confirm they were good with it. They came back with the following question:

     

     

    So long as “binary scan [sha1 [SSL::cert 0]] H* cert_hex” gives us the thumbprint out of the certificate and not a SHA1 hash generated across the bytes of the presented certificate…then yes.

     

     

    Could you please confirm?
  • Here's a quick test to show the results:

    
    when CLIENTSSL_CLIENTCERT {
    
    log local0. "Got [SSL::cert count] certs"
    
    if {[SSL::cert 0] eq ""}{
    log local0. "cert 0 empty"
    } else {
    binary scan [SSL::cert 0] H* whole_cert_hex
    log local0. "\$whole_cert_hex: $whole_cert_hex"
    
    binary scan [sha1 [SSL::cert 0]] H* fingerprint
    log local0. "sha1: $fingerprint"
    
    binary scan [md5 [SSL::cert 0]] H* fingerprint
    log local0. "md5 manual: $fingerprint"
    
    log local0. "md5 from X509::hash: [X509::hash [SSL::cert 0]]"
    }
    }
    
    And the log results:
    
    : sha1: 591fb5e98f012218dbe946780197e061206ab35f
    : md5 manual: 39b06fdf22022ff0c25672cbee2263f1
    : md5 from X509::hash: 39:b0:6f:df:22:02:2f:f0:c2:56:72:cb:ee:22:63:f1
    
    And the openssl results for the same client cert:
    
     openssl x509 -in client1.example.com.crt -sha1 -noout -fingerprint
    SHA1 Fingerprint=59:1F:B5:E9:8F:01:22:18:DB:E9:46:78:01:97:E0:61:20:6A:B3:5F
    
     openssl x509 -in client1.example.com.crt -md5 -noout -fingerprint
    MD5 Fingerprint=39:B0:6F:DF:22:02:2F:F0:C2:56:72:CB:EE:22:63:F1
    

    Aaron
  • There's also an enhancement request to update X509::hash to allow you to specify the hash type instead of it being hardcoded to md5:

     

     

    Bug 380676 - RFE: Enhance X509::hash to use a specified hash, and default to hash used in cert

     

     

    You could open a case with F5 Support to request this feature be built.

     

     

    Aaron
  • Just wanted to post an update. Hoolio's iRule made it through test, QA, and is now in production. It takes the SHA1 thumbprint of the incoming SSL cert, converts it to hex, and inserts it into the header. It also takes the cert subject of the incoming SSL cert and inserts it into the header. Lastly, it scrubs any pre-existing headers before inserting the new ones as an extra measure of security. Thanks again Hoolio, and thanks to everyone else who pitched in. Here is the iRule again in final form:

     

     

    when HTTP_REQUEST {

     

    if { [SSL::cert count] > 0 } {

     

    HTTP::header remove SSLClientCertSubject

     

    HTTP::header insert SSLClientCertSubject [X509::subject [SSL::cert 0]]

     

     

    binary scan [sha1 [SSL::cert 0]] H* cert_hex

     

    HTTP::header remove ClientCertThumbprint

     

    HTTP::header insert ClientCertThumbprint $cert_hex

     

    }

     

    }
  • That's good to hear. Thanks for posting the final iRule.

     

     

    Aaron
  • Actually, you might want to remove the cert headers regardless of whether the client presented a cert for the SSL session to prevent a client from injecting their own headers.

    
    when HTTP_REQUEST {
    HTTP::header remove SSLClientCertSubject
    HTTP::header remove ClientCertThumbprint
    if { [SSL::cert count] > 0 } {
    HTTP::header insert SSLClientCertSubject [X509::subject [SSL::cert 0]]
    binary scan [sha1 [SSL::cert 0]] H* cert_hex
    HTTP::header insert ClientCertThumbprint $cert_hex
    }
    }
    

    Aaron