How do you sign a Certificate Signing Request with your Certification Authority?
Asked Answered
T

4

283

During my search, I found several ways of signing a SSL Certificate Signing Request:

  1. Using the x509 module:

    openssl x509 -req -days 360 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
    
  2. Using the ca module:

    openssl ca -cert ca.crt -keyfile ca.key -in server.csr -out server.crt
    

Note: I am unsure of the use of the right parameters for this one. Please advise correct usage if I am to use it.

What way should one use to sign certificate requests with your Certification Authority? Is one method better than the other (for example, one being deprecated)?

Thamora answered 23/1, 2014 at 0:41 Comment(5)
possible duplicate of How to build a self-signed certificate with openssl?Sidman
From what I can see, ca is for cases when you're more serious about becoming a CA.Jago
You might find my answer interesting.Jago
Stack Overflow is a site for programming and development questions. This question appears to be off-topic because it is not about programming or development. See What topics can I ask about here in the Help Center. Perhaps Super User or Unix & Linux Stack Exchange would be a better place to ask.Sidman
The accepted answer is great, also check my little addition to it https://mcmap.net/q/35902/-how-do-you-sign-a-certificate-signing-request-with-your-certification-authorityTzar
S
631
1. Using the x509 module
openssl x509 ...
...

2 Using the ca module
openssl ca ...
...

You are missing the prelude to those commands.

This is a two-step process. First you set up your CA, and then you sign an end entity certificate (a.k.a server or user). Both of the two commands elide the two steps into one. And both assume you have a an OpenSSL configuration file already setup for both CAs and Server (end entity) certificates.


First, create a basic configuration file:

$ touch openssl-ca.cnf

Then, add the following to it:

HOME            = .
RANDFILE        = $ENV::HOME/.rnd

####################################################################
[ ca ]
default_ca    = CA_default      # The default ca section

[ CA_default ]

default_days     = 365          # How long to certify for
default_crl_days = 30           # How long before next CRL
default_md       = sha256       # Use public key default MD
preserve         = no           # Keep passed DN ordering

x509_extensions = ca_extensions # The extensions to add to the cert

email_in_dn     = no            # Don't concat the email in the DN
copy_extensions = copy          # Required to copy SANs from CSR to cert

####################################################################
[ req ]
default_bits       = 4096
default_keyfile    = cakey.pem
distinguished_name = ca_distinguished_name
x509_extensions    = ca_extensions
string_mask        = utf8only

####################################################################
[ ca_distinguished_name ]
countryName         = Country Name (2 letter code)
countryName_default = US

stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = Maryland

localityName                = Locality Name (eg, city)
localityName_default        = Baltimore

organizationName            = Organization Name (eg, company)
organizationName_default    = Test CA, Limited

organizationalUnitName         = Organizational Unit (eg, division)
organizationalUnitName_default = Server Research Department

commonName         = Common Name (e.g. server FQDN or YOUR name)
commonName_default = Test CA

emailAddress         = Email Address
emailAddress_default = [email protected]

####################################################################
[ ca_extensions ]

subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid:always, issuer
basicConstraints       = critical, CA:true
keyUsage               = keyCertSign, cRLSign

The fields above are taken from a more complex openssl.cnf (you can find it in /usr/lib/openssl.cnf), but I think they are the essentials for creating the CA certificate and private key.

Tweak the fields above to suit your taste. The defaults save you the time from entering the same information while experimenting with configuration file and command options.

I omitted the CRL-relevant stuff, but your CA operations should have them. See openssl.cnf and the related crl_ext section.

Then, execute the following. The -nodes omits the password or passphrase so you can examine the certificate. It's a really bad idea to omit the password or passphrase.

$ openssl req -x509 -config openssl-ca.cnf -days 365 -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEM

After the command executes, cacert.pem will be your certificate for CA operations, and cakey.pem will be the private key. Recall the private key does not have a password or passphrase.

You can dump the certificate with the following.

$ openssl x509 -in cacert.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 11485830970703032316 (0x9f65de69ceef2ffc)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=MD, L=Baltimore, CN=Test CA/[email protected]
        Validity
            Not Before: Jan 24 14:24:11 2014 GMT
            Not After : Feb 23 14:24:11 2014 GMT
        Subject: C=US, ST=MD, L=Baltimore, CN=Test CA/[email protected]
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    00:b1:7f:29:be:78:02:b8:56:54:2d:2c:ec:ff:6d:
                    ...
                    39:f9:1e:52:cb:8e:bf:8b:9e:a6:93:e1:22:09:8b:
                    59:05:9f
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                4A:9A:F3:10:9E:D7:CF:54:79:DE:46:75:7A:B0:D0:C1:0F:CF:C1:8A
            X509v3 Authority Key Identifier:
                keyid:4A:9A:F3:10:9E:D7:CF:54:79:DE:46:75:7A:B0:D0:C1:0F:CF:C1:8A

            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Key Usage:
                Certificate Sign, CRL Sign
    Signature Algorithm: sha256WithRSAEncryption
         4a:6f:1f:ac:fd:fb:1e:a4:6d:08:eb:f5:af:f6:1e:48:a5:c7:
         ...
         cd:c6:ac:30:f9:15:83:41:c1:d1:20:fa:85:e7:4f:35:8f:b5:
         38:ff:fd:55:68:2c:3e:37

And test its purpose with the following (don't worry about the Any Purpose: Yes; see "critical,CA:FALSE" but "Any Purpose CA : Yes").

$ openssl x509 -purpose -in cacert.pem -inform PEM
Certificate purposes:
SSL client : No
SSL client CA : Yes
SSL server : No
SSL server CA : Yes
Netscape SSL server : No
Netscape SSL server CA : Yes
S/MIME signing : No
S/MIME signing CA : Yes
S/MIME encryption : No
S/MIME encryption CA : Yes
CRL signing : Yes
CRL signing CA : Yes
Any Purpose : Yes
Any Purpose CA : Yes
OCSP helper : Yes
OCSP helper CA : Yes
Time Stamp signing : No
Time Stamp signing CA : Yes
-----BEGIN CERTIFICATE-----
MIIFpTCCA42gAwIBAgIJAJ9l3mnO7y/8MA0GCSqGSIb3DQEBCwUAMGExCzAJBgNV
...
aQUtFrV4hpmJUaQZ7ySr/RjCb4KYkQpTkOtKJOU1Ic3GrDD5FYNBwdEg+oXnTzWP
tTj//VVoLD43
-----END CERTIFICATE-----

For part two, I'm going to create another configuration file that's easily digestible. First, touch the openssl-server.cnf (you can make one of these for user certificates also).

$ touch openssl-server.cnf

Then open it, and add the following.

HOME            = .
RANDFILE        = $ENV::HOME/.rnd

####################################################################
[ req ]
default_bits       = 2048
default_keyfile    = serverkey.pem
distinguished_name = server_distinguished_name
req_extensions     = server_req_extensions
string_mask        = utf8only

####################################################################
[ server_distinguished_name ]
countryName         = Country Name (2 letter code)
countryName_default = US

stateOrProvinceName         = State or Province Name (full name)
stateOrProvinceName_default = MD

localityName         = Locality Name (eg, city)
localityName_default = Baltimore

organizationName            = Organization Name (eg, company)
organizationName_default    = Test Server, Limited

commonName           = Common Name (e.g. server FQDN or YOUR name)
commonName_default   = Test Server

emailAddress         = Email Address
emailAddress_default = [email protected]

####################################################################
[ server_req_extensions ]

subjectKeyIdentifier = hash
basicConstraints     = CA:FALSE
keyUsage             = digitalSignature, keyEncipherment
subjectAltName       = @alternate_names
nsComment            = "OpenSSL Generated Certificate"

####################################################################
[ alternate_names ]

DNS.1  = example.com
DNS.2  = www.example.com
DNS.3  = mail.example.com
DNS.4  = ftp.example.com

If you are developing and need to use your workstation as a server, then you may need to do the following for Chrome. Otherwise Chrome may complain a Common Name is invalid (ERR_CERT_COMMON_NAME_INVALID). I'm not sure what the relationship is between an IP address in the SAN and a CN in this instance.

# IPv4 localhost
IP.1     = 127.0.0.1

# IPv6 localhost
IP.2     = ::1

Then, create the server certificate request. Be sure to omit -x509*. Adding -x509 will create a certificate, and not a request.

$ openssl req -config openssl-server.cnf -newkey rsa:2048 -sha256 -nodes -out servercert.csr -outform PEM

After this command executes, you will have a request in servercert.csr and a private key in serverkey.pem.

And you can inspect it again.

$ openssl req -text -noout -verify -in servercert.csr
Certificate:
    verify OK
    Certificate Request:
        Version: 0 (0x0)
        Subject: C=US, ST=MD, L=Baltimore, CN=Test Server/[email protected]
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:ce:3d:58:7f:a0:59:92:aa:7c:a0:82:dc:c9:6d:
                    ...
                    f9:5e:0c:ba:84:eb:27:0d:d9:e7:22:5d:fe:e5:51:
                    86:e1
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Key Identifier:
                1F:09:EF:79:9A:73:36:C1:80:52:60:2D:03:53:C7:B6:BD:63:3B:61
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:example.com, DNS:www.example.com, DNS:mail.example.com, DNS:ftp.example.com
            Netscape Comment:
                OpenSSL Generated Certificate
    Signature Algorithm: sha256WithRSAEncryption
         6d:e8:d3:85:b3:88:d4:1a:80:9e:67:0d:37:46:db:4d:9a:81:
         ...
         76:6a:22:0a:41:45:1f:e2:d6:e4:8f:a1:ca:de:e5:69:98:88:
         a9:63:d0:a7

Next, you have to sign it with your CA.


You are almost ready to sign the server's certificate by your CA. The CA's openssl-ca.cnf needs two more sections before issuing the command.

First, open openssl-ca.cnf and add the following two sections.

####################################################################
[ signing_policy ]
countryName            = optional
stateOrProvinceName    = optional
localityName           = optional
organizationName       = optional
organizationalUnitName = optional
commonName             = supplied
emailAddress           = optional

####################################################################
[ signing_req ]
subjectKeyIdentifier   = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints       = CA:FALSE
keyUsage               = digitalSignature, keyEncipherment

Second, add the following to the [ CA_default ] section of openssl-ca.cnf. I left them out earlier, because they can complicate things (they were unused at the time). Now you'll see how they are used, so hopefully they will make sense.

base_dir      = .
certificate   = $base_dir/cacert.pem   # The CA certifcate
private_key   = $base_dir/cakey.pem    # The CA private key
new_certs_dir = $base_dir              # Location for new certs after signing
database      = $base_dir/index.txt    # Database index file
serial        = $base_dir/serial.txt   # The current serial number

unique_subject = no  # Set to 'no' to allow creation of
                     # several certificates with same subject.

Third, touch index.txt and serial.txt:

$ touch index.txt
$ echo '01' > serial.txt

Then, perform the following:

$ openssl ca -config openssl-ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles servercert.csr

You should see similar to the following:

Using configuration from openssl-ca.cnf
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName           :PRINTABLE:'US'
stateOrProvinceName   :ASN.1 12:'MD'
localityName          :ASN.1 12:'Baltimore'
commonName            :ASN.1 12:'Test CA'
emailAddress          :IA5STRING:'[email protected]'
Certificate is to be certified until Oct 20 16:12:39 2016 GMT (1000 days)
Sign the certificate? [y/n]:Y

1 out of 1 certificate requests certified, commit? [y/n]Y
Write out database with 1 new entries
Data Base Updated

After the command executes, you will have a freshly minted server certificate in servercert.pem. The private key was created earlier and is available in serverkey.pem.

Finally, you can inspect your freshly minted certificate with the following:

$ openssl x509 -in servercert.pem -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 9 (0x9)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=MD, L=Baltimore, CN=Test CA/[email protected]
        Validity
            Not Before: Jan 24 19:07:36 2014 GMT
            Not After : Oct 20 19:07:36 2016 GMT
        Subject: C=US, ST=MD, L=Baltimore, CN=Test Server
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:ce:3d:58:7f:a0:59:92:aa:7c:a0:82:dc:c9:6d:
                    ...
                    f9:5e:0c:ba:84:eb:27:0d:d9:e7:22:5d:fe:e5:51:
                    86:e1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                1F:09:EF:79:9A:73:36:C1:80:52:60:2D:03:53:C7:B6:BD:63:3B:61
            X509v3 Authority Key Identifier:
                keyid:42:15:F2:CA:9C:B1:BB:F5:4C:2C:66:27:DA:6D:2E:5F:BA:0F:C5:9E

            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:example.com, DNS:www.example.com, DNS:mail.example.com, DNS:ftp.example.com
            Netscape Comment:
                OpenSSL Generated Certificate
    Signature Algorithm: sha256WithRSAEncryption
         b1:40:f6:34:f4:38:c8:57:d4:b6:08:f7:e2:71:12:6b:0e:4a:
         ...
         45:71:06:a9:86:b6:0f:6d:8d:e1:c5:97:8d:fd:59:43:e9:3c:
         56:a5:eb:c8:7e:9f:6b:7a

Earlier, you added the following to CA_default: copy_extensions = copy. This copies extension provided by the person making the request.

If you omit copy_extensions = copy, then your server certificate will lack the Subject Alternate Names (SANs) like www.example.com and mail.example.com.

If you use copy_extensions = copy, but don't look over the request, then the requester might be able to trick you into signing something like a subordinate root (rather than a server or user certificate). Which means he/she will be able to mint certificates that chain back to your trusted root. Be sure to verify the request with openssl req -verify before signing.


If you omit unique_subject or set it to yes, then you will only be allowed to create one certificate under the subject's distinguished name.

unique_subject = yes            # Set to 'no' to allow creation of
                                # several ctificates with same subject.

Trying to create a second certificate while experimenting will result in the following when signing your server's certificate with the CA's private key:

Sign the certificate? [y/n]:Y
failed to update database
TXT_DB error number 2

So unique_subject = no is perfect for testing.


If you want to ensure the Organizational Name is consistent between self-signed CAs, Subordinate CA and End-Entity certificates, then add the following to your CA configuration files:

[ policy_match ]
organizationName = match

If you want to allow the Organizational Name to change, then use:

[ policy_match ]
organizationName = supplied

There are other rules concerning the handling of DNS names in X.509/PKIX certificates. Refer to these documents for the rules:

RFC 6797 and RFC 7469 are listed, because they are more restrictive than the other RFCs and CA/B documents. RFC's 6797 and 7469 do not allow an IP address, either.

Sidman answered 24/1, 2014 at 19:28 Comment(22)
Thanks for that extensive answer... However, I am kind of lost here. What I understood from what you wrote: openssl req is used to generate CSR, openssl req -x509 is used to generate CA certificate (I saw in some other place you could create self-signed certificate too), openssl ca is used to sign a CSR with a CA certificate. Right? What confuses me too is that the same parts of the openssl.cnf file are used with different values depending on the command... I think I am totally lost now.Thamora
First, openssl req -x509 is used to create the CA. Second, openssl req is used to create the server's CSR. Third, openssl ca is used to create the server certificate and certify it with the CA's signature.Sidman
"What confuses me too is that the same parts of the openssl.cnf..." - Right. That's why I broke them out for you into openssl-ca.cnf and openssl-server.cnf. After you get used to them and how the sections are invoked, you can combine them into a monstrosity like openssl.cnf.Sidman
Please validate or correct the 3 assertions I made in my previous comment to sse if I understood you answer. Thanks for the detailed help!Thamora
If I were to add DNS.5 = *.example.com and DNS.6 = *.*.*.*.example.com would these wildcards work as expected, for domains www.example.com and www.test.com.dev.example.com respectively?Soniasonic
@Soniasonic - I believe *.example.com will work under all user agents and issuing policies. *.*.*.*.example.com will fail under browsers because the CA/B forums only allow a wildcard in the leftmost label. The behavior of other user agents will vary. (Even more interesting, you can add DNS names like *.com, *.net, www.*.com and www.*.com and non-browser user agents will match them. Dooh....)Sidman
Thanks @jww. I've used several of your posts extensively to get things working for our dev server with many (sub)-sub-domains :)Soniasonic
I have another question. What does the default_days refer to? And in the CA the Validity seems to always default to a month. Does this reflect on how long self-signed certs will last?Soniasonic
why do you have x509_extensions = ca_extensions in both the [ CA_default ] and [ req ] sections?Ladoga
@JeffPuckettII - Its a common section. Its used by both the CA utility and the Req utility. They should be v3 extensions.Sidman
I pretty much follow all the steps but I am still getting the error The certificate is not trusted in all web browsers. You may need to install an Intermediate/chain certificate to link it to a trusted root certificate and end up having to buy from a ssl provider to get pass this problemPainting
Thanks @jww, nice description. One thing I dont follow is the duration of the CA certificate. openssl-ca.cnf says default_days 1000 but the validity of the cert is just over a month. Where does the 30 days come from?Insolate
@Insolate Use -days option for anything different from the default 30 days. OpenSSL docsZoe
Thanks for the explanations @jww! How would you do for Docker images, like 192.168.99.100 ? Would you be able to add the IP in the SNA section ?Anaemic
Can the signing of certificate using CA be done using GSKIT instead of openssl? I have my CA, have created the key and the csr. Need to sign it using gskit.Babettebabeuf
"-sha256" is not listed as an option for "openssl req" or "openssl genrsa" in either openssl version 1.0.2 nor 1.1.0. ( openssl.org/docs )Claqueur
This is like the best answer I've ever seen on stack overflow. You solved a 6 hour headache with this. Thanks!Virginia
@Sidman Great answer... but one hiccup: I think I've followed your steps correctly (minor tweaks for names etc.), but when I come to sign the request, I only get the message about using the .cnf file and asking for a passphrase (I omitted -nodes). I don't get Check that the request matches the signature or anything after it. (The pass-phrase is correct, because it complains if I deliberately enter it wrong). Any obvious things that might cause that? (OpenSSL=v1.1.1d)Astrodynamics
@Sidman I found the problem: PBCAC... specifically creating the "index.txt" As I was on Windows, and didn't have a touch command to hand, I (stupidly) just created an "empty" text file (CR-LF). This causes openssl to give-up somewhere after checking the private key but – annoyingly – with no indication of why. Creating a true zero-byte file for the index has made it spring into life!Astrodynamics
Great answer. Thanks for that. For anybody looking for including UPN in the client certificate (above answer is for server certificate, but configurations and procedure for client certificate also will be in the same lines), it can be done by subjectAltName=otherName:1.3.6.1.4.1.311.20.2.3;UTF8:[email protected] under req_extensions section, OR with otherName=1.3.6.1.4.1.311.20.2.3;UTF8:[email protected] under alternate_names section of openssl-client.cnf file (file name inspired by openssl-server.cnf)Irradiant
Very informative. In addition, for those who want to understand more about preserve and copy_extensions options, check this out: #48673435Cloyd
Thanks for answer. I posted a follow up question here: security.stackexchange.com/questions/274526/…. Basically why use the openssl-ca.cnf file over the -subj option when making certs for the CA.Chihli
D
33

Sometimes, such as for testing, you just want a simplistic means of generating a signed certificate, without setting up a full-blown CA configuration. This is possible using just the openssl req and openssl x509 commands. You would never use this method for production certificates, but since it is useful for some non-production situations, here are the commands.

Generate a self-signed signing certificate

First, create a self-signed certificate that will be used as the root of trust:

openssl req -x509 -days 365 -key ca_private_key.pem -out ca_cert.pem

Or equivalently, if you want to generate a private key and a self-signed certificate in a single command:

openssl req -x509 -days 365 -newkey rsa:4096 -keyout ca_private_key.pem -out ca_cert.pem

Generate a certificate request

Next, create a certificate request for the certificate to be signed:

openssl req -new -key my_private_key.pem -out my_cert_req.pem

Again, you may generate the private key and the request simultaneously, if needed:

openssl req -new -newkey rsa:4096 -keyout my_private_key.pem -out my_cert_req.pem

Generate a signed certificate

Finally, use the self-signed signing certificate to generate a signed certificate from the certificate request:

openssl x509 -req -in my_cert_req.pem -days 365 -CA ca_cert.pem -CAkey ca_private_key.pem -CAcreateserial -out my_signed_cert.pem
Dabchick answered 19/8, 2021 at 20:45 Comment(0)
T
23

In addition to answer of @jww, I would like to say that the configuration in openssl-ca.cnf,

default_days     = 1000         # How long to certify for

defines the default number of days the certificate signed by this root-ca will be valid. To set the validity of root-ca itself you should use '-days n' option in:

openssl req -x509 -days 3000 -config openssl-ca.cnf -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEM

Failing to do so, your root-ca will be valid for only the default one month and any certificate signed by this root CA will also have validity of one month.

Tzar answered 25/12, 2017 at 16:26 Comment(1)
To anyone doing iOS or macOS development be aware that certificates issued for 1,000 days will not be trusted in iOS 13+ (extendedKeyUsage is also required). I was able to finally sign my own trusted certs using both these answers in addition to the answer here: apple.stackexchange.com/questions/371725/…Hamachi
V
0

I've found a convenient script sscg for this task - https://github.com/sgallagher/sscg
Here is an example for Fedora linux:
Installation:

sudo yum install sscg

Example:

sscg \
--ca-key-password-prompt \
--lifetime=3650 \
--country=XX \
--state=myState \
--locality=myCity \
--organization=myOrg \
--organizational-unit=myUnit \
[email protected] \
--hostname=foo.example.com  \
--subject-alt-name=*.example.com \
--ca-key-file=CA.key \
--ca-file=CA.pem \
--cert-key-file=foo.example.com.key \
--cert-file=foo.example.com.pem
Valle answered 13/2, 2023 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.