Request Client Certificate And Pass To Application

Problem this snippet solves:

We are using BigIP to dynamically request a client certificate. This example differs from the others available in that it actually passes the x509 certificate to the server for processing using a custom http header.

The sequence of event listeners required to accomplish this feat is: HTTP_REQUEST, which invokes CLIENTSSL_HANDSHAKE, which is followed by HTTP_REQUEST_SEND

The reason is that CLIENTSSL_HANDSHAKE occurs after HTTP_REQUEST event is processed entirely, but HTTP_REQUEST_SEND occurs after it. The certificate appears in PEM encoding and is slightly mangled; you need to emit newlines to get back into proper PEM format:


This certificate can be converted to DER encoding by jettisoning the BEGIN and END markers and doing base64 decode on the string.

Code :

# Initialize the variables on new client tcp session.
    set collecting 0
    set renegtried 0

# Runs for each new http request
    # /_hst name and ?_hst=1 parameter triggers client cert renegotiation
    if { $renegtried == 0
         and [SSL::cert count] == 0
         and    ([HTTP::uri] matches_regex {^[^?]*/_hst(\?|/|$)} 
              or [HTTP::uri] matches_regex {[?&]_hst=1(&|$)}) } {

# Collecting means buffering the request. The collection goes on
# until SSL::renegotiate occurs, which happens after the HTTP
# request has been received. The maximum data buffered by collect
# is 1-4 MB.
        set collecting 1
        SSL::cert mode request

# After a handshake, we log that we have tried it. This is to prevent
# constant attempts to renegotiate the SSL session. I'm not sure of this
# feature; this may in fact be a mistake, but we can change it at any time.
# It is transparent if we do: the connections only work slower. It would,
# however, make BigIP detect inserted smartcards immediately. Right answer
# depends on the way the feature is used by applications.
    if { $collecting == 1 } {
        set renegtried 1
# Release allows the request processing to occur normally from this
# point forwards. The next event to fire is HTTP_REQUEST_SEND.

# Inject headers based on earlier renegotiations, if any.
    clientside {
# Security: reject any user-submitted headers by our magic names.

# if certificate is available, send it. Otherwise, send a header
# indicating a failure, if we have already attempted a renegotiate.
if { [SSL::cert count] > 0 } {
    HTTP::header insert "X-ENV-SSL_CLIENT_CERTIFICATE" [X509::whole [SSL::cert 0]]
} elseif { $renegtried == 1 } {
    # This header has some debug value: if the FAILED header is not
    # present, BigIP is probably not configured to do client certs
    # at all.
    HTTP::header insert "X-ENV-SSL_CLIENT_CERTIFICATE_FAILED" "true"
Published Mar 18, 2015
Version 1.0

Was this article helpful?


  • I am no expert here, but I have an issue with the comment above:


    "The reason is that CLIENTSSL_HANDSHAKE occurs after HTTP_REQUEST event is processed entirely, but HTTP_REQUEST_SEND occurs after it."


    This does not seem accurate based on my personal observations that I am able to set variables in CLIENTSSL_HANDSHAKE and access them in HTTP_REQUEST. This "request after handshake" event order is also supported by the excellent event flowchart here:




  • I think the OP meant that in the context of -this rule- the HTTP_REQUEST event handler fires before the CLIENTSSL_HANDSHAKE due to the SSL::renegotiate command.