5 posts categorized "evnark"

10/09/2010

Groovy: EVNark v0.2 - Validate SSL EV Certs

Well, it's been a couple of months since the initial release and I've updated evnark to version 0.2.  There are a few things new in this version:

  • Added AC Camerfirma SA to list of known EV OIDS
  • Added D-Trust to the ist of known EV OIDS
  • Added TC Trustcenter but check is still disabled until verification can be made.  The Certificate Policy extension id that I was able to find for TC Trustcenter extended validation certs looks odd.  If anyone out there uses an extended validation ssl certificate signed by TC Trustcenter and you know the certificate policy extension ID "1.2.276.0.44.1.1.1.4" is valid, please let me know.
  • Added UnknownHostException handler so that a lookup failure on a host exits gracefully
  • Added a socket timeout exception.  Initially set to just 5 seconds, the script won't wait forever trying to connect.
  • Cleaned up the output a little more

NOTE REGARDING CERTIFICATE PEER VALIDATION:  EVNark was written in the Groovy programming language.  EVNark executes as a groovy script running inside your system's default java viryual machine.  Successful peer validation of any CA signed ssl certificate is dependent entirely upon whether or not your jvm vendor has included that CA's root certificates within your jvm's cacerts file, whether you've imported a vendor's root certificates within your cacerts store, or if you are using a custom cacerts store.  Results may vary.  I'm using the default cacerts store provided by Apple bundled with their 1.6.0 JVM in Snow Leopard (OS X 10.6).  I personally know that 1.5 Apple JVM's running on Leopard (OS X 10.5) were unable to successfully validate GlobalSign's EV certs.

Downloads are now available from the main EVNark page

#!/usr/bin/env groovy
// usage: 'evnark [-h/--host "hostname"] [-p/--port "port"]'
//
// v0.2 
//   + Adds hostname lookup exception handling
//   + Adds connection Timeout exception handling
//   + Modified the output so that it will now state
//     whether or not an EV cert was found.

import java.security.*
import javax.net.ssl.*
import sun.security.x509.* 

/* This section sets up the command 
   line arguments portion of this script. */ 

def cli = new CliBuilder( usage: 'evnark [-h/--host "hostname"] [-p/--port "port"]' )
  cli.h( longOpt:'host', args:1, required:true, type:GString, 'The host or site you want to test' )
  cli.p( longOpt:'port', args:1, required:false, type:GString, 'Optional. Defaults to port 443')

def opt = cli.parse(args)
  if (!opt) return
  if (opt.h) host = opt.h

def port = 443
  if (opt.p) port = Integer.parseInt(opt.p)

// Create the socket
def factory = SSLSocketFactory.getDefault()

try {
  socket = factory.createSocket()
  socketaddr = new InetSocketAddress(host, port)
  socket.connect(socketaddr, 5000)
  } catch(UnknownHostException ex) {
      println "Hostname Resolution Failed.  Is ${host} a valid host?"
      return
  } catch(SocketTimeoutException ex) {
      println "Connection Timed Out. Is ${host} up or available?"
      return
  } 

try {
  socket.addHandshakeCompletedListener( new listener() )
  
  socket.startHandshake() 
  } catch(SSLHandshakeException ex) {
    println "CERTIFICATE PEER COULD NOT BE VERIFIED"
  } catch(SSLException ex) {
      println "The port number you specified (${port}) does not appear to be an ssl port"
  }

  class listener implements HandshakeCompletedListener {
  void handshakeCompleted(HandshakeCompletedEvent e) {

  def ev_oids = [
    // 'A-Trust GmbH'  => ' Doesn't appear to offer the .yet ',
    'AC Camerfirma SA'  => ' 1.3.6.1.4.1.17326.10.14.2',
    'Buypass AS':'2.16.578.1.26.1.3.3',
    // 'Certum':' Doesn't appear to offer them yet ',
    'Comodo CA Limited':'1.3.6.1.4.1.6449.1.2.1.5.1',
    'Cybertrust, Inc':'1.3.6.1.4.1.6334.1.100.1',
    'D-TRUST GmbH':'1.3.6.1.4.1.4788.2.202.1',
    // 'DanID':' Doesn't appear to offer the yet ',
    'DigiCert Inc':'2.16.840.1.114412.2.1',
    'DigiNotar':'2.16.528.1.1001.1.1.1.12.6.1.1.1',
    // 'Echoworx Corporation':' Doesn't appear to offer them yet ',
    'Entrust, Inc.':'2.16.840.1.114028.10.1.2',
    'GeoTrust Inc.':'1.3.6.1.4.1.14370.1.6',
    // 'Getronics PinkRoccade':'Doesn't appear to offer them yet  ',
    'GlobalSign nv-sa':'1.3.6.1.4.1.4146.1.1',
    'The Go Daddy Group, Inc.':'2.16.840.1.114413.1.7.23.3',
    // 'IdenTrust, Inc.':' Doesn't appear to offer the yet ',
    // 'IpsCA, IPS Certification Authority s.l.':' ',
    'Izenpe S.A.':'1.3.6.1.4.1.14777.6.1.1',
    'Network Solutions L.L.C.':'1.3.6.1.4.1.782.1.2.1.8.1',
    'QuoVadis Limited':'1.3.6.1.4.1.8024.0.2.100.1.2',
    // 'RSA Security, Inc.':' ',
    'SECOM Trust Systems CO.,LTD.':'1.2.392.200091.100.721.1',
    'SecureTrust Corporation':'2.16.840.1.114404.1.1.2.4.1',
    // 'Skaitmeninio sertifikavimo centras (SSC)':' ',
    'StartCom Ltd.':'1.3.6.1.4.1.23223.2',
    'Starfield Technologies':'2.16.840.1.114414.1.7.23.3',
    'SwissSign AG':'2.16.756.1.89.1.2.1.1',
    // 'T-Systems Enterprise Services GmbH':' ',
    // 'TC TrustCenter GMBh':'1.2.276.0.44.1.1.1.4',
    'thawte, Inc.':'2.16.840.1.113733.1.7.48.1',
    // 'Trustis Limited':Doesn't appear to offer them yet ',
    'ValiCert, Inc.':'2.16.840.1.114414.1.7.23.3',
    'VeriSign, Inc.':'2.16.840.1.113733.1.7.23.6',
    'Wells Fargo WellsSecure':'2.16.840.1.114171.500.9'
  ]

  def certs = e.getPeerCertificates()
  def crt = certs[0]
  def intcrt = certs[1]

  def ext = crt.getCertificatePoliciesExtension()
  def policies = ext.get(CertificatePoliciesExtension.POLICIES)
    for ( PolicyInformation info in policies ) {
      CertificatePolicyId id = info.getPolicyIdentifier()
        def certpolicyid = id.getIdentifier().toString()
        //println ""
        //println "Found Certificate Policy ID: ${certpolicyid}"
      if ( ev_oids.any { it.value == certpolicyid } )
        println "This host uses an Extended Validation Cert\nThe Certficate Policy ID is: ${certpolicyid}\n"
        else 
        println "This host does NOT use an Extended Validation Cert\nThe Certificate Policy ID is: ${certpolicyid}\n"

}
    }
  }
  socket.close()

08/10/2010

Groovy: Extended Validation Certificate Determination

Many people come to this site looking for information on testing and verifying extended validation certificates.  Despite my low opinion of them, extended validation certificates are here to stay, so I thought it would be fun and interesting to see just how difficult it would be to write some script to determine whether a certificate is an extended validation one or not.  

The Wikipedia article on extended validation certificates suggests that all you really need to do is retrieve the Object ID (OID) for the Certificate Policy ID in the Certificate Policies extension section of the certificate.  If a certain pattern is matched, the certificate is an extended validation certificate.  If not, then it's a traditional cert.  The Wikipedia article even provides a listing of OID's to get started with.

Figuring out what needs to be done seemed to be fairly straightforward but based upon Firefox's problems with EV certs last year, perhaps my assumptions on determining whether an EV cert is actually an EV cert are way too simplistic:  Establish a connection to a remote host, download the certificate, parse the Certificate Policy Id from the Certificate Policies extension field, look it up within an array, map, or collection of known Certificate Policy ID's, and print out if it is EV or not.  Sounds simple, right?  It turns out that, for me, it was freakishly hard!  To give you an idea how hard, I first began writing this post in July 2009!  Granted, there have been lots of other things pulling me away from this over the past year but that's still quite a while.  I'm glad that no one was paying me for this!

Below is my first pass at a new script written in groovy, using no add-on modules other than what is available in a standard Groovy 1.7.4 installation.  For now, I'm calling the script evnark.  All you need to run it is a groovy installation and a jvm.  I've only tested this on a mac running groovy 1.7.4 and OS X's 1.6.20 JVM.  When run, the script will output the Certificate Policy Id found within the certificate.  If the certificate policy id matches known Certificate Policy Id's for Extended Validation certificates, then a second line of output is displayed:  "This is an EV cert."  If the certificate policy id is not matched, no additional output is provided.

I've spent much of my free time over the last couple of weeks really trying to get the parsing to work so there will probably be a revised version of this script in the future.  Right now, I'm not at all happy with how the script is comparing the value of the Certificate Policy Id to the the map and hope that someone out there reading this will comment with a better way of doing it.  Also, I'm wondering if there is some cleaner way of drilling down into the certificate polices section to retrieve the ID that doesn't involve 8 lines of code setting variables.  This version of the script will not fail gracefully with self-signed certificates, expired certificates, certificate names that do not match domain names, and other types of typical ssl warnings or errors.  I've saving the better exception handling for release 0.2.  Finally, the ev_oids map lists all known (to me) Certificate Authorities but many of these CA's either don't offer Extended Validation certificates at this time OR I was unable to determine what the Certificate Policy Id was for EV certs from those CA's.  That work is still ongoing.

Downloads are now available from the main EVNark page

#!/usr/bin/env groovy
// usage: 'evnark [-h/--host "hostname"] [-p/--port "port"]'

import java.security.*
import javax.net.ssl.*
import sun.security.x509.* 

/* This section sets up the command 
   line arguments portion of this script. */ 

def cli = new CliBuilder( usage: 'evnark [-h/--host "hostname"] [-p/--port "port"]' )
  cli.h( longOpt:'host', args:1, required:true, type:GString, 'The host or site you want to test' )
  cli.p( longOpt:'port', args:1, required:false, type:GString, 'Optional. Defaults to port 443')

def opt = cli.parse(args)
  if (!opt) return
  if (opt.h) host = opt.h

def port = 443
  if (opt.p) port = Integer.parseInt(opt.p)

// Create the socket
def factory = SSLSocketFactory.getDefault() 
def socket = factory.createSocket("$host", port)

try {
  socket.addHandshakeCompletedListener( new listener() )
  
  socket.startHandshake() 
  } catch(SSLHandshakeException ex) {
    println "CERTIFICATE PEER COULD NOT BE VERIFIED"
  } catch(SSLException ex) {
      println "The port number you specified (${port}) \ndoes not appear to be an ssl port"
  }

  class listener implements HandshakeCompletedListener {
  void handshakeCompleted(HandshakeCompletedEvent e) {

  def ev_oids = [
    // 'A-Trust GmbH'  => ' Doesn't appear to offer them...yet ',
    // 'AC Camerfirma SA'  => ' Doesn't appear to offer them...yet ',
    'Buypass AS':'2.16.578.1.26.1.3.3',
    // 'Certum':' Doesn't appear to offer them...yet ',
    'Comodo CA Limited':'1.3.6.1.4.1.6449.1.2.1.5.1',
    'Cybertrust, Inc':'1.3.6.1.4.1.6334.1.100.1',
    // 'D-TRUST GmbH':' Doesn't appear to offer them...yet ',
    // 'DanID':' Doesn't appear to offer them...yet ',
    'DigiCert Inc':'2.16.840.1.114412.2.1',
    'DigiNotar':'2.16.528.1.1001.1.1.1.12.6.1.1.1',
    // 'Echoworx Corporation':' Doesn't appear to offer them...yet ',
    'Entrust, Inc.':'2.16.840.1.114028.10.1.2',
    'GeoTrust Inc.':'1.3.6.1.4.1.14370.1.6',
    // 'Getronics PinkRoccade':'Doesn't appear to offer them...yet  ',
    'GlobalSign nv-sa':'1.3.6.1.4.1.4146.1.1',
    'The Go Daddy Group, Inc.':'2.16.840.1.114413.1.7.23.3',
    // 'IdenTrust, Inc.':' Doesn't appear to offer them...yet ',
    // 'IpsCA, IPS Certification Authority s.l.':' ',
    // 'Izenpe S.A.':' ',
    'Network Solutions L.L.C.':'1.3.6.1.4.1.782.1.2.1.8.1',
    'QuoVadis Limited':'1.3.6.1.4.1.8024.0.2.100.1.2',
    // 'RSA Security, Inc.':' ',
    'SECOM Trust Systems CO.,LTD.':'1.2.392.200091.100.721.1',
    'SecureTrust Corporation':'2.16.840.1.114404.1.1.2.4.1',
    // 'Skaitmeninio sertifikavimo centras (SSC)':' ',
    'StartCom Ltd.':'1.3.6.1.4.1.23223.2',
    'Starfield Technologies':'2.16.840.1.114414.1.7.23.3',
    'SwissSign AG':'2.16.756.1.89.1.2.1.1',
    // 'T-Systems Enterprise Services GmbH':' ',
    // 'TC TrustCenter GMBh':' ',
    'thawte, Inc.':'2.16.840.1.113733.1.7.48.1',
    // 'Trustis Limited':' ',
    'ValiCert, Inc.':'2.16.840.1.114414.1.7.23.3',
    'VeriSign, Inc.':'2.16.840.1.113733.1.7.23.6',
    'Wells Fargo WellsSecure':'2.16.840.1.114171.500.9'
  ]

  def certs = e.getPeerCertificates()
  def crt = certs[0]
  def intcrt = certs[1]

  def ext = crt.getCertificatePoliciesExtension()
  def policies = ext.get(CertificatePoliciesExtension.POLICIES)
    for ( PolicyInformation info in policies ) {
      CertificatePolicyId id = info.getPolicyIdentifier()
        def certpolicyid = id.getIdentifier().toString()
        println ""
        println "Found Certificate Policy ID: ${certpolicyid}"
        ev_oids.each {
            if (certpolicyid == it.value) {
              println "This is an EV Cert signed by ${it.key}"
              
              }
        }
    }
  }
}
  socket.close()