21

I am creating an API that only certain computers should have access to. Communication will be via SSL between the server and the clients. In order to verify that a client has access, I would like to create a certificate for each client, that is signed by the server. Any clients that do not provide the certificate that is signed by the server, should be denied access.

I have an SSL certificate and key from GoDaddy. I thought I should be able to create client certificates using this information, but everywhere I look, it seems like I need the CA certificate (from GoDaddy) to sign the client certificate and not my specific server's certificate/key.

This doesn't make sense to me, because it seems strange that I should have to go to GoDaddy and get a new certificate for every client. I imagine that this isn't the case and that I am either doing something wrong, or don't understand completely.

So - How can I create a client certificate that is signed by my server certificate/password?

If your could provide commands (I am using openssl) to generate the client certificate from my server certificate, it would be greatly appreciated too.

Thanks!

zsalzbank
  • 313
  • 1
  • 2
  • 6
  • See: http://security.stackexchange.com/questions/24961/self-sign-ssl-certificate-for-my-mobile-app/ –  Dec 17 '12 at 17:46
  • All you need is a public key on file for each client alongside a client id of some kind. When a client talks to you, send the session key encrypted by their public key. If they are able to decrypt it, they are who they claim to be. – lynks Dec 17 '12 at 17:49

3 Answers3

20

In order to sign client certificates, you will need a CA certificate you control. Paying for one is out of the question in most cases, as global-trusted CA certificates are a security hazard for the rest of the Internet. So in these cases you have to make your own CA and create your own server and client certificates.

So let's start with a basic openssl.conf file that we will use for generation of all these certificates:

[ ca ]
default_ca  = CA_default                # The default ca section

[ CA_default ]
certs          = certs                  # Where the issued certs are kept
crl_dir        = crl                    # Where the issued crl are kept
database       = database.txt           # database index file.
new_certs_dir  = certs                  # default place for new certs.
certificate    = cacert.pem             # The CA certificate
serial         = serial.txt             # The current serial number
crl            = crl.pem                # The current CRL
private_key    = private\cakey.pem      # The private key
RANDFILE       = private\private.rnd    # private random number file

x509_extensions  = v3_usr               # The extentions to add to the cert
default_days     = 365
default_crl_days = 30                   # how long before next CRL
default_md       = sha256               # which md to use.
preserve         = no                   # keep passed DN ordering
policy           = policy_match
email_in_dn      = 

[ policy_match ]
commonName      = supplied

[ req ]
default_bits        = 2048
default_keyfile     = privkey.pem
distinguished_name  = req_distinguished_name
x509_extensions     = v3_ca

[ v3_ca ]
basicConstraints     = CA:TRUE
subjectKeyIdentifier = hash

[ v3_usr ]
basicConstraints     = CA:FALSE
subjectKeyIdentifier = hash

[ server ]
basicConstraints       = CA:FALSE
nsCertType             = server
nsComment              = "Server Certificate"
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
extendedKeyUsage       = serverAuth
keyUsage               = digitalSignature, keyEncipherment

[ client ]
basicConstraints       = CA:FALSE
nsCertType             = client
nsComment              = "Client Certificate"
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer:always
extendedKeyUsage       = clientAuth
keyUsage               = digitalSignature

[ req_distinguished_name ]

This config file is made for automatic generation of certificates from a batch script. If you need more control or naming options, you need to adapt it to your situation.

So for generating a CA, go to the directory where you want to make your CA, put openssl.conf there and:

PASSWORD="PUT_YOUR_CA_PASSWORD_HERE"

# Make the config CA specific
cat openssl.conf > use.conf
echo "CN=PUT_CA_NAME_HERE" >> use.conf

# Create the necessary files
mkdir keys requests certs 
touch database.txt
echo 01 > serial.txt

# Generate your CA key (Use appropriate bit size here for your situation)
openssl genrsa -aes256 -out keys/ca.key -passout pass:$PASSWORD 2048

# Generate your CA req
openssl req -config use.conf -new -key keys/ca.key -out requests/ca.req -passin pass:$PASSWORD

# Make your self-signed CA certificate
openssl ca  -config use.conf -selfsign -keyfile keys/ca.key -out certs/ca.crt -in requests/ca.req -extensions v3_ca -passin pass:$PASSWORD -batch

# Cleanup
rm requests/ca.req use.conf

Now to generate a server certificate (e.g. for your web server):

PASSWORD="PUT_YOUR_CA_PASSWORD_HERE"
NAME="PUT_THE_NAME_OF_SERVER_TO_GENERATE_HERE"

# Make the config Server specific
cat openssl.conf > use.conf
echo "CN=$NAME" >> use.conf

openssl req -new -nodes -extensions server -out "requests/$NAME.req" -keyout "$NAME.key" -config use.conf -passin pass:$PASSWORD )
openssl ca -batch -extensions server -keyfile keys/ca.key -cert certs/ca.crt -config use.conf -out "certs/$NAME.crt" -passin pass:$PASSWORD -infiles "requests/$NAME.req"

# Cleanup
rm "requests/$NAME.req" use.conf

Now to generate a client certificate:

PASSWORD="PUT_YOUR_CA_PASSWORD_HERE"
NAME="PUT_THE_NAME_OF_CLIENT_TO_GENERATE_HERE"

# Make the config Client specific
cat openssl.conf > use.conf
echo "CN=$NAME" >> use.conf

openssl req -new -nodes -extensions client -out "requests/$NAME.req" -keyout "$NAME.key" -config use.conf -passin pass:$PASSWORD )
openssl ca -batch -extensions client -keyfile keys/ca.key -cert certs/ca.crt -config use.conf -out "certs/$NAME.crt" -passin pass:$PASSWORD -infiles "requests/$NAME.req"

# Cleanup
rm "requests/$NAME.req" use.conf

The only difference between generating keys and certificates for clients and servers it to prevent that a stolen client certificate can also be used to play a server and 'fool' other clients to connect to it (this only works as long as your applications support the client and server extension in certificates).

Paul
  • 386
  • 1
  • 4
  • Though not simple for newcomers like me, very elegant solution. Thank you! – 7hi4g0 Mar 20 '14 at 16:23
  • 1
    There are three CNs here. Does it matter what you name them? Do they have to match domain names? – Nick Retallack Sep 04 '14 at 08:04
  • 1
    I see a stray close parethesis in the ```openssl req``` lines in both the client and server key steps. – Nick Retallack Sep 04 '14 at 08:14
  • Awesome, it worked! But only with openssl s_client, not with curl. Strange. Btw, when i run these, openssl prompts me for the CN again even though I already specified it in the NAME variable, and it errors out if I don't type something at that prompt. – Nick Retallack Sep 04 '14 at 08:38
10

A certification authority is an entity which issues certificates. You want to issue certificates (to the clients), therefore you want to be a CA.

Whoever tries to validate a certificate needs to trust the CA key (that's a priori trust: think of the CA public key being hardcoded in the software or operating system). In your case, you want your server to validate the certificates, so your server will have to keep a copy of the CA public key.

Existing CA like GoDaddy succeeded in getting a copy of their public key into all Web browsers -- their CA are trusted by everybody. To achieve that, they had to prove to the browser vendors (e.g. Microsoft) that they were trustworthy and serious (private key is in a bunker, they have procedures for everything, they have sufficient financial backup to ensure continuity of operations...) and it was expensive, which is why they will not give you sub-CA power for free (or even for cheap). However, in your situation, you do not care if the CA key is known by the whole world: you only need a CA that your server (that you entirely control) will trust. A CA that you maintain yourself will be fine.

EJBCA is a free, opensource CA implementation which should be easy to use. Alternatively, you can throw together some scripts around OpenSSL.

Note: certificates are for authentication, not for authorization. They are meant to identify users. I suggest that you separate the two roles in your server:

  • When you issue a certificate to client X, put the name "X" in the certificate.
  • Keep on the server a table of "allowed clients" (by name).
  • When a client connects, validate its certificate, then extract the client name from the certificate, and check the name in the table of allowed clients.

This separation will be extremely useful the day you want to revoke the access rights of a given client.

Thomas Pornin
  • 320,799
  • 57
  • 780
  • 949
  • I have searched the far ends of the internet for an answer and you sir have answered it. You deserve a beer. – matt. Apr 11 '15 at 22:18
1

As far as I know you cannot create a valid client certificate with your SSL you bought from GoDaddy. The reason behind it has to do with the private key of the CA certificate from GoDaddy, which they will not give out for good reason.

Now, if you have all the computers under your controll the easiest, and cheapest, thing to do would be to generate your own CA certificate and sign your clients. Add your CA to each of the Trusted Root stores on each individual computer.

You can get a CA to sign a certificate that allows you to be a CA, however, these certificates are not cheap, as in "contact us for a quote."

If you need a tool I believe XCA is a GUI that will make it easier for you.

Travis Pessetto
  • 670
  • 3
  • 6