Creating a Digital Certificate for Betfair Login

During the writing of my book Programming for Betfair I tried to make things as simple as possible out of respect for newcomers to programming. I utilised the standard login procedure for Betfair, consisting of a username/password pair. However, when programming a new algorithmic trading platform for Betfair the most annoying aspect of the process is having to log into the servers every time you test the software. During the course of a day that is a lot of logging in, especially if you use two-factor authentication.

I have been requested by a reader to provide a tutorial for authentication with a digital certificate and so I have written this article. The process was a lot easier than I had imagined. For readers of my book I also provide code which replaces the LoginForm.

Firstly, I shall point out that the certificate that you will create will be self-signed so don't imagine that you are now a certification authority and can start handing out certificates to anyone. The certificates on your browser, which authenticate websites to you, are signed by trusted third-parties. These third-parties have gone through rigorous procedures to permit their certificates to be installed inside your browser. Your certificate will be self-signed and used only for authenticating your application to Betfair and nothing else.

Betfair trusts you to sign your own certificate for your own account and no more. It is up to the user of the certificate to ensure that their security had not been compromised. That means if your account has been broken into because you have allowed someone to gain access to your private key then you are at fault and not Betfair.

The Process of Creating a Self-Signed Digital Certificate

These instructions are for Windows users. If you use another operating system then it is up to you to decipher these instructions. I cannot provide any help with that.

1) Download the OpenSSL package from Shining Light Productions. Choose the topmost Win32 OpenSSL v*.*.* Light version of the software. I have a 64-bit operating system and running the 32-bit version of the software is not going to make any difference. OpenSSL provides all the tools for creating your own certificates.

2) After downloading the package (and virus checking it) install the application, as the installer suggests, at the root of your C: drive. If asked where to copy the OpenSSL DLLs then make sure they go into the Windows system directory. The final dialog of the installer asks if you want to make a donation. If you want to then do so but if you don't then make sure you untick the check box before clicking the Finish button otherwise you will be frog-marched off to the donation site. OpenSSL is now installed.

3) Click on your Windows menu and then right-click on Computer so that you can choose its Properties. You will then see a dialog, click on Advanced system settings on the left-hand side and the following dialog will be displayed. In Windows 10, Advanced System Settings can be found by right clicking the window on the task bar and selecting system in the menu.

Now click the Environment Variables button.


4) In the next dialog click on the New button in the System variables section, as in the following picture. Add the variable name OPENSSL_CONF, variable value C:\OpenSSL-Win32\bin\openssl.cfg and click the OK button. OpenSSL is now fully configured. If you use a different operating system to Windows 7 then consult Google.


5) Replace the text in the file at C:\OpenSSL-Win32\bin\openssl.cfg with the following text. This file commands OpenSSL to create a client side certificate rather than a server side certificate.


#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME			= .
RANDFILE		= $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file		= $ENV::HOME/.oid
oid_section		= new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions		= 
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca', 'req' and 'ts'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
# Policies used by the TSA examples.
tsa_policy1 = 1.2.3.4.1
tsa_policy2 = 1.2.3.4.5.6
tsa_policy3 = 1.2.3.4.5.7
####################################################################
[ ca ]
default_ca	= CA_default		# The default ca section
####################################################################
[ CA_default ]
dir		= ./demoCA		# Where everything is kept
certs		= $dir/certs		# Where the issued certs are kept
crl_dir		= $dir/crl		# Where the issued crl are kept
database	= $dir/index.txt	# database index file.
#unique_subject	= no			# Set to 'no' to allow creation of
					# several ctificates with same subject.
new_certs_dir	= $dir/newcerts		# default place for new certs.
certificate	= $dir/cacert.pem 	# The CA certificate
serial		= $dir/serial 		# The current serial number
crlnumber	= $dir/crlnumber	# the current crl number
					# must be commented out to leave a V1 CRL
crl		= $dir/crl.pem 		# The current CRL
private_key	= $dir/private/cakey.pem# The private key
RANDFILE	= $dir/private/.rand	# private random number file
x509_extensions	= usr_cert		# The extentions to add to the cert
# Comment out the following two lines for the "traditional"
# (and highly broken) format.
name_opt 	= ca_default		# Subject Name options
cert_opt 	= ca_default		# Certificate field options
# Extension copying option: use with caution.
# copy_extensions = copy
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crlnumber must also be commented out to leave a V1 CRL.
# crl_extensions	= crl_ext
default_days	= 365			# how long to certify for
default_crl_days= 30			# how long before next CRL
default_md	= default		# use public key default MD
preserve	= no			# keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy		= policy_match
# For the CA policy
[ policy_match ]
countryName		= match
stateOrProvinceName	= match
organizationName	= match
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName		= optional
stateOrProvinceName	= optional
localityName		= optional
organizationName	= optional
organizationalUnitName	= optional
commonName		= supplied
emailAddress		= optional
####################################################################
[ req ]
default_bits		= 2048
default_keyfile 	= privkey.pem
distinguished_name	= req_distinguished_name
attributes		= req_attributes
x509_extensions	= v3_ca	# The extentions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options. 
# default: PrintableString, T61String, BMPString.
# pkix	 : PrintableString, BMPString (PKIX recommendation before 2004)
# utf8only: only UTF8Strings (PKIX recommendation after 2004).
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
string_mask = utf8only
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName			= Country Name (2 letter code)
countryName_default		= AU
countryName_min			= 2
countryName_max			= 2
stateOrProvinceName		= State or Province Name (full name)
stateOrProvinceName_default	= Some-State
localityName			= Locality Name (eg, city)
0.organizationName		= Organization Name (eg, company)
0.organizationName_default	= Internet Widgits Pty Ltd
# we can do this but it is not needed normally :-)
#1.organizationName		= Second Organization Name (eg, company)
#1.organizationName_default	= World Wide Web Pty Ltd
organizationalUnitName		= Organizational Unit Name (eg, section)
#organizationalUnitName_default	=
commonName			= Common Name (e.g. server FQDN or YOUR name)
commonName_max			= 64
emailAddress			= Email Address
emailAddress_max		= 64
# SET-ex3			= SET extension number 3
[ req_attributes ]
challengePassword		= A challenge password
challengePassword_min		= 4
challengePassword_max		= 20
unstructuredName		= An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType			= server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment			= "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl		= http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This is required for TSA certificates.
# extendedKeyUsage = critical,timeStamping
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always
[ proxy_cert_ext ]
# These extensions should be added when creating a proxy certificate
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType			= server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment			= "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# An alternative to produce certificates that aren't
# deprecated according to PKIX.
# subjectAltName=email:move
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl		= http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
# This really needs to be in place for it to be a proxy certificate.
proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
####################################################################
[ tsa ]
default_tsa = tsa_config1	# the default TSA section
[ tsa_config1 ]
# These are used by the TSA reply generation only.
dir		= ./demoCA		# TSA root directory
serial		= $dir/tsaserial	# The current serial number (mandatory)
crypto_device	= builtin		# OpenSSL engine to use for signing
signer_cert	= $dir/tsacert.pem 	# The TSA signing certificate
					# (optional)
certs		= $dir/cacert.pem	# Certificate chain to include in reply
					# (optional)
signer_key	= $dir/private/tsakey.pem # The TSA private key (optional)
default_policy	= tsa_policy1		# Policy if request did not specify it
					# (optional)
other_policies	= tsa_policy2, tsa_policy3	# acceptable policies (optional)
digests		= md5, sha1		# Acceptable message digests (mandatory)
accuracy	= secs:1, millisecs:500, microsecs:100	# (optional)
clock_precision_digits  = 0	# number of digits after dot. (optional)
ordering		= yes	# Is ordering defined for timestamps?
				# (optional, default: no)
tsa_name		= yes	# Must the TSA name be included in the reply?
				# (optional, default: no)
ess_cert_id_chain	= no	# Must the ESS cert id chain be included?
				# (optional, default: no)
[ ssl_client ]
basicConstraints = CA:FALSE
nsCertType = client
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth


6) Now create a batch file with the following text and call it createcert.bat


cd "C:\OpenSSL-Win32\bin"

openssl genrsa -out client-2048.key 2048

openssl req -new -config openssl.cfg -key client-2048.key -out client-2048.csr

openssl x509 -req -days 365 -in client-2048.csr -signkey client-2048.key -out client-2048.crt -extfile openssl.cfg -extensions ssl_client 

openssl pkcs12 -export -in client-2048.crt -inkey client-2048.key -out client-2048.p12

pause


This file will automatically create a self-signed digital certificate for you. Once downloaded right-click the file and run it as an administrator. You won't be able to create a certificate unless you are doing so as an administrator.

A command line interpreter window will open during the process. At some point you will be requested to enter some data, as in the following example

Country Name (2 letter code) [AU]: - e.g. GB (for Great Britain) etc.
State or Province Name (Full Name) [Some-State]: - England or whatever
Locality Name (eg, city) []: - London or whatever
Organization Name (eg, company) [Internet Widgits Pty Ltd]: - leave blank and hit return
Organizational Unit Name (eg, section) []:- leave blank and hit return
Common Name (e.g. server FQDN or YOUR name) []: your real name as known by Betfair
Email Address []: the one known to Betfair

You are then asked for a password. I didn't bother and just hit return. This password would have to be included in your authentication which already includes your username and password pair. 

If asked for an optional company name then don't give one hit return.

And if asked for an export password, again don't give one and hit return and again for confirmation of the password.

When the process is complete you will see four new files in the C:\OpenSSL-Win32\bin directory; 

client-2048.crt - your digital certificate
client-2048.csr - a certificate signing request
client-2048.key - your private key
client-2048.p12 - used to authenticate your application to Betfair

Your certificate file will be given to Betfair and your P12 moved to the root at C:\ and used in the login process. Copies of all should be saved somewhere safe offline.

7) Now login to the Betfair website. At the top of the screen, click My Account and then My Betfair Account in the dropdown menu. You will then see another dropdown menu called My details. Click on this and then Security settings. You will then see your security settings page. Click on the Edit link next to Automated Betting Program Access. You can now browse to your client-2048.crt file and upload it to Betfair. After the upload make sure the status is set to On.

8) For readers of my book you will need to alter your code for automatic authentication thus

a) Create a new Module called Authentication.vb (Use the module creation in the book as reference) and add the following code to it (remembering to replace the red words with your details as appropriate). You will notice that the certificate location is expected to be in the root of the directory so you must move the P12 file that you created to there. If you want the P12 to be elsewhere then you must change the code.

Imports System.IO
Imports System.Net
Imports System.Text
Imports Newtonsoft.Json
Imports System.Security.Cryptography
Imports System.Security.Cryptography.X509Certificates

Module Authentication
 
  Public Sub Login()

    Try

      Dim postData As String = _
"username=YOUR_USERNAME&password=YOUR_PASSWORD"

      Dim cert As New X509Certificate2("C:\
client-2048.p12", "")

      Dim request As HttpWebRequest = _
WebRequest.Create("https://identitysso.betfair.com/api/certlogin")

      request.Method = "POST"
      request.ContentType = "application/x-www-form-urlencoded"
      request.Headers.Add("X-Application: YOUR_APPKEY")
      request.ClientCertificates.Add(cert)
      request.Accept = "application/json"

      Using dataStream As Stream = request.GetRequestStream()

        Using writer As New _
StreamWriter(dataStream, Encoding.[Default])
                    
          writer.Write(postData)
                
        End Using
            
      End Using

      Using stream As Stream = DirectCast(request.GetResponse(), _
HttpWebResponse).GetResponseStream()
                
        Using reader As New StreamReader(stream, Encoding.[Default])
                    
          Dim loginResponse As LoginResponse = _
JsonConvert.DeserializeObject(Of LoginResponse)(reader.ReadToEnd())

          Form1.Print(loginResponse.sessionToken)

          SportsAPI.ssoid = loginResponse.sessionToken
          AccountsAPI.ssoid = loginResponse.sessionToken

        End Using

      End Using

      Catch ex As Exception
        Form1.Print(Now & " - Login Error: " & ex.Message)
      End Try

    End Sub

    'Class for non-interactive login
    Public Class LoginResponse
        Public sessionToken As String
        Public loginStatus As String
    End Class

End Module

Note

Some users have reported that the endpoint

https://identitysso.betfair.com/api/certlogin 

doesn't work for them and they had to change it to 

https://identitysso-cert.betfair.com/api/certlogin 

My original certificate still works so maybe there are now two endpoints; one for original users and another for newer users.

Please ensure that "username=YOUR_USERNAME&password=YOUR_PASSWORD" does not contain any spaces. The Print statement in the code above in blue is a test that will print out your ssoid on successful authentication. Delete or comment out this line after testing.

If you have yet to create the AccountsAPI module then comment out the line  AccountsAPI.ssoid = loginResponse.sessionToken and uncomment the line when you have created the module.

b) Now change the Form1_Load subroutine in Form1.vb as follows

    Private Sub Form1_Load(sender As Object, e As EventArgs) _
Handles MyBase.Load

        'LoginForm.Show()
        Login()
        initialise()


    End Sub


by commenting out the LoginForm.Show() statement as LoginForm.vb is no longer needed and then adding the call to the Login() subroutine in Authentication.vb, followed by the initialise() call that used to be in LoginForm.vb

You should now be able to access Betfair without having to type in your username/password pair. I recommend that you keep two-factor authentication on the manual login to the Betfair website as you cannot automatically login there. If there are any problems then let me know.

Note

The instructions in this article were tested in February 2017 and were found to be still working. If you can prove otherwise then I would be interested to know. Any problems are probably peculiar to your computer's set up and you should either contact the creators of the OpenSSL software or the wider OpenSSL community.

Further Reading


Programming for Betfair
A guide to creating sports trading applications, is now available on Amazon. You do not need any programming experience...