How to Validate a Certificate Chain
We have the X.509 certificate for an end user 'Enid'. Can we trust that Enid's certificate really is the one issued to her?
In this example, Enid's certificate is issued by an "intermediate" authority Ian, whose certificate is in turn issued by the ultimate certification authority (CA), Carl. Carl's certificate is a self-signed root certificate.
End user: Enid
Certificate: EnidRSASignedByIan.cer
^
issued by |
|
Intermediate CA: Ian
Certificate: IanRSASignedByCarl.cer
^
issued by |
|
Certification Authority: Carl
Certificate: CarlRSASelf.cer
^
issued by |
|
Carl
To ensure that Enid's certificate is valid, we need to
As a further check with the root certificate, we can compare its "thumbprint" - the message digest value of the certificate file itself - with a separate value that we have hard-coded somewhere. This prevents someone substituting a false certificate for the root certificate and then re-creating the entire chain.
Public Function TestTheChain() As Boolean
Dim nRet As Long
Dim strCertName As String
Dim strIssuerCert As String
Dim strThumbPrint As String
' Chain: [Enid] issued by [Ian] issued by [Carl] self-issued by [Carl].
' Given the three certs, can we trust that Enid's certificate really is the one issued to her?
' Assumes we trust the CA's certificate and all certificates issued by it.
' Does not deal with certificate revokation (CRL) issues.
' 1. Is Enid's certificate currently valid?
strCertName = "EnidRSASignedByIan.cer"
nRet = X509_CertIsValidNow(strCertName, 0)
If nRet > 0 Then
Debug.Print "Error: " & nRet & " " & pkiGetLastError()
ElseIf nRet < 0 Then
MsgBox "Validation error: cert '" & strCertName & "' is no longer valid at this time.", vbCritical
Exit Function
Else
Debug.Print "Cert '" & strCertName & "' is currently valid."
End If
' 2. Was Enid's certificate issued by Ian?
strIssuerCert = "IanRSASignedByCarl.cer"
nRet = X509_VerifyCert(strCertName, strIssuerCert, 0)
If nRet > 0 Then
Debug.Print "Error: " & nRet & " " & pkiGetLastError()
ElseIf nRet < 0 Then
MsgBox "Validation error: cert '" & strCertName & "' was not issued by '" & strIssuerCert & "'.", vbCritical
Exit Function
Else
Debug.Print "Verified that cert '" & strCertName & "' was issued by '" & strIssuerCert & "'."
End If
' Continuing up the chain...
' 3. Is Ian's certificate currently valid?
strCertName = "IanRSASignedByCarl.cer"
nRet = X509_CertIsValidNow(strCertName, 0)
If nRet > 0 Then
Debug.Print "Error: " & nRet & " " & pkiGetLastError()
ElseIf nRet < 0 Then
MsgBox "Validation error: cert '" & strCertName & "' is no longer valid at this time.", vbCritical
Exit Function
Else
Debug.Print "Cert '" & strCertName & "' is currently valid."
End If
' 4. Was Ian's certificate issued by Carl?
strIssuerCert = "CarlRSASelf.cer"
nRet = X509_VerifyCert(strCertName, strIssuerCert, 0)
If nRet > 0 Then
Debug.Print "Error: " & nRet & " " & pkiGetLastError()
ElseIf nRet < 0 Then
MsgBox "Validation error: cert '" & strCertName & "' was not issued by '" & strIssuerCert & "'.", vbCritical
Exit Function
Else
Debug.Print "Verified that cert '" & strCertName & "' was issued by '" & strIssuerCert & "'."
End If
' At the top of the chain we have a self-signed certificate...
' 5. Is Carl's certificate currently valid?
strCertName = "CarlRSASelf.cer"
nRet = X509_CertIsValidNow(strCertName, 0)
If nRet > 0 Then
Debug.Print "Error: " & nRet & " " & pkiGetLastError()
ElseIf nRet < 0 Then
MsgBox "Validation error: cert '" & strCertName & "' is no longer valid at this time.", vbCritical
Exit Function
Else
Debug.Print "Cert '" & strCertName & "' is currently valid."
End If
' 6. Was Carl's certificate issued by Carl?
strIssuerCert = "CarlRSASelf.cer"
nRet = X509_VerifyCert(strCertName, strIssuerCert, 0)
If nRet > 0 Then
Debug.Print "Error: " & nRet & " " & pkiGetLastError()
ElseIf nRet < 0 Then
MsgBox "Validation error: cert '" & strCertName & "' was not issued by '" & strIssuerCert & "'.", vbCritical
Exit Function
Else
Debug.Print "Verified that cert '" & strCertName & "' was issued by '" & strIssuerCert & "'."
End If
' Finally, we can hard-code the "thumbprint" (hash digest) of the ultimate CA's certificate
' and check that it matches what we have in hand
' (you can get this value using CERTMGR.EXE).
' 7. Is Carl's certificate the one we expected?
Const HARD_CODED_THUMBPRINT As String = "4110908F77C64C0EDFC2DE6273BFA9A98A9C5CE5"
strCertName = "CarlRSASelf.cer"
strThumbPrint = String(PKI_SHA1_CHARS, " ")
nRet = X509_CertThumb(strCertName, strThumbPrint, Len(strThumbPrint), PKI_HASH_SHA1)
Debug.Print "ThumbPrint(SHA-1, '" & strCertName & "')=" & strThumbPrint
If UCase(strThumbPrint) = HARD_CODED_THUMBPRINT Then
Debug.Print "CA cert's thumbprint matches what we expect."
Else
MsgBox "Validation error: cert '" & strCertName & "' does not have the thumbprint we expect.", vbCritical
Exit Function
End If
' If we got to here, we have validated the entire chain
Debug.Print "OK, certificate chain has been validated."
' RETURN SUCCESS
TestTheChain = True
End Function
The full VB code is in X509_TestCerts.bas (8 kB).
Test certificates for this example are in X509_TestCerts.zip (1.4 kB).
Carl's certificate and private key are from
RFC 4134 Examples of S/MIME Messages.
To run the example code you will need to install the
CryptoSys PKI Toolkit
available from http://www.cryptosys.net/pki/
and include the module basCrPKI in your VB6/VBA project.
VALIDATE_CERT_CHAIN(certs, root_thumb)
A sequence certs of X.509 certificate files [cert_1, cert_2, ..., cert_n] where cert_i is issued by the holder of cert_{i+1} and cert_n is a trusted self-signed root certificate; the message digest hash root_thumb of a trusted copy of the root certificate cert_n.
Returns true if all certificates are valid as at the current date and each certificate cert_i was signed by the holder of cert_{i+1} and the message digest of the file cert_n matches root_thumb. Otherwise returns false.
X509_VerifyCert
X509_CertThumb
For more information, please Email Us.
This page last updated: 21 February 2009