Total Pageviews

Monday, 7 March 2016

使用 openssl 制作一个包含 SAN(Subject Alternative Name)的证书

什么是 SAN

SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。
先来看一看 Google 是怎样使用 SAN 证书的,下面是 Youtube 网站的证书信息:

这里可以看到这张证书的 Common Name 字段是 *,那么为什么这张证书却能够被 这个域名所使用呢。原因就是这是一张带有 SAN 扩展的证书,下面是这张证书的 SAN 扩展信息:

这里可以看到,这张证书的 Subject Alternative Name 段中列了一大串的域名,因此这张证书能够被多个域名所使用。对于 Google 这种域名数量较多的公司来说,使用这种类型的证书能够极大的简化网站证书的管理。

使用 openssl 生成带有 SAN 扩展的证书请求文件(CSR)

首先我们将 openssl 的配置文件复制一份作临时使用,CentOS6 中 openssl 的配置文件在 /etc/pki/tls/openssl.cnf,将这个文件复制到 /tmp 下。
此文件的格式是类似 ini 的配置文件格式,找到 [ req ] 段落,加上下面的配置:
req_extetions = v3_req
这段配置表示在生成 CSR 文件时读取名叫 v3_req 的段落的配置信息,因此我们再在此配置文件中加入一段名为 v3_req 的配置:
[ v3_req ]
# Extensions to add to a certificate request

basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
这段配置中最重要的是在最后导入名为 alt_names 的配置段,因此我们还需要添加一个名为 [ alt_names ]的配置段:
[ alt_names ]
DNS.1 =
DNS.2 =
这里填入需要加入到 Subject Alternative Names 段落中的域名名称,可以写入多个。
$ openssl req -new -nodes -keyout ustack.key -out ustack.csr -config /tmp/openssl.cnf
$ openssl req -text -noout -in ustack.csr
可以看到此证书请求文件中会包含 Subject Alternative Names 字段,并包含之前在配置文件中填写的域名。

使用 openssl 签署带有 SAN 扩展的证书请求

假设使用本机作为子签署 CA 对此证书请求进行签署,签署的方式为:
$ openssl ca -policy policy_anything -out ustack.crt -config /tmp/openssl.cnf -extensions v3_req -infiles ustack.csr
$ openssl x509 -text -noout -in ustack.crt


觉得上面的方式太麻烦了?使用命令一步生成带 SAN 扩展的证书请求文件:
$ openssl req -new -sha256 \
    -key ustack.key \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=UnitedStack/OU=Devops/" \
    -reqexts SAN \
    -config <(cat /etc/pki/tls/openssl.cnf \
        <(printf "[SAN]\,")) \
    -out ustack.csr
C  => Country
ST => State
L  => City
O  => Organization
OU => Organization Unit
CN => Common Name (证书所请求的域名)
emailAddress => main administrative point of contact for the certificate
$ openssl ca -in ustack.csr \
    -extensions SAN \
    -config <(cat /etc/pki/tls/openssl.cnf \
        <(printf "[SAN]\,")) \ 
    -out ustack.crt
$openssl x509 -text -noout -in ustack.crt


Creating and signing an SSL cert with alternative names

In my previous post I outlined how you can create your own self-signed CA.  With that CA in hand, you should be able to deploy your CA public certificate in any place you want certificates signed by it to be trusted, and then create your own SSL certificates.

Signing an existing CSR (no Subject Alternative Names)

Making an SSL certificate is pretty easy, and so is signing a CSR (Certificate Signing Request) that you’ve gotten from something else.  Essentially, you do this;
openssl ca -policy policy_anything -out -infiles
You should then have three main artifacts from that process –
  • CSR file – This can now be deleted.  The CSR is only used for the signing process, to decouple the private key from the signed public key.
  • KEY file – This is the private key that was generated when you created the CSR.  This bit is secret, and should be kept safe.  Notably, the machine doing the signing does not need the KEY at any time, this is what the CSR is for.
  • CRT file – This is the public certificate signed by your CA which you add to your service.
Now, that’s all peachy, but what happens when you have a certificate with Subject Alternative Names (SANs) attached?  It’s possible for a CSR to contain SANs, but the signer does not have to include all SANs requested, and can add SANs themselves when they sign.  How do we handle that?

Creating a CSR with embedded SANs

In order to do this, you’ll need to copy /etc/pki/tls/openssl.cnf somewhere, and then edit it.  Let’s assume you’ve called it ‘‘.
Go and find the [ req ] section, and add the following;
req_extensions = v3_req
This causes the v3_req section to be read when you make a request (ie, when you generate a CSR using this config file).  Then, make the [ v3_req ] section look something like this;
[ v3_req ]
# Extensions to add to a certificate request

basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
These constraints prevent this certificate being used to make a new CA (a bloody good idea for most things), and specify some normal key usage restrictions – notably this certificate can’t be used for code signing.  Typically you won’t need that for most purposes.  And notably, we import the alt_names section to define all the Subject Alternative Names.
And lastly, make a new section called [ alt_names ] which looks something like this (include ALL the Subject Names you may want, you can have up to a hundred of them);
[ alt_names ]
DNS.1 =
DNS.2 =
Then, you create the key and CSR like this;
openssl req -new -nodes -keyout -out -config
You’ll wind out with a new CSR which will have the embedded SANs.

Signing a CSR with embedded or desired SANs

Remember how I said that a signer doesn’t have to use the SANs embedded in the CSR?  This is where things might get annoying, but fortunately because you have the openssl.cnf you used to create the CSR, it’s actually pretty easy.
First up, let’s have a look at the CSR and see what SANs were requested;
openssl req -text -noout -verify -in
Scroll down and look for the X509v3 Subject Alternative Name section.  Now, if you want to include all those SANs, then the openssl.cnf you used to sign will have to have all those SANs already defined.  Plus you can add some more if you want (like, if someone forgot to request as a SAN).  Let’s assume that you’re just going to sign the CSR you made from above, and you already have an openssl.cnf that’s all good.
Sign the CSR using a custom openssl.cnf file;
openssl ca -policy policy_anything -out -config -extensions v3_req -infiles
Doing this forces the v3_req section to be included (it normally wouldn’t), which then enforces all the SANs specified in the alt_names section you defined above.
And then finally, check the resulting certificate;
openssl x509 -text -noout -in
You should see the SANs defined in the X509v3 Subject Alternative Name section, along with all the other constraints.