I am creating simple two certificates; one is a root certificate and the other one is a server certificate. The latter has the rootcert.pem
and rootprivkey.pem
assigned to be used in the flags -CA
and CAkey
respectively. I have installed the root certificate in system and ran sudo update-cacertificates
as well. After many tries I couldn't bring openssl to verify the server certificate against the self signed certificate. It gives me this error:
error 20 at 0 depth lookup: unable to get local issuer certificate
error servercrt.pem: verification failed
Note : I don't have intermediate certificates.
Now, how do I solve this problem?
EDIT:
Commands used for generation and verification
openssl req -new -newkey rsa:4096 -keyout rootprivkey.pem -out rootreq.pem -config openssl.cnf -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1
openssl ca -out rootcrt.pem -days 2652 -keyfile rootprivkey.pem -selfsign -config openssl.cnf -infiles rootreq.pem
openssl req -new -newkey rsa:4096 -keyout serverprivkey.pem -out serverreq.pem -config openssl.cnf
openssl x509 -req -in serverreq.pem -days 1200 -CA rootcrt.pem -CAkey rootprivkey.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 -out servercrt.pem -set_serial 01
openssl verify -CAfile rootcrt.pem servercrt.pem
openssl.cnf
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# You might want to copy this into /etc/ssl/ or define OPENSSL_CONF
#
# 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 = . # Where everything is kept
certs = $dir # Where the issued certs are kept
crldir = $dir # Where the issued crl are kept
database = $dir # database index file.
unique_subject = yes # Set to 'no' to allow creation of
# several ctificates with same subject.
new_certs_dir = $certs # default place for new certs.
certificate = $certs/rootcrt.pem # The CA certificate
serial = $dir/serial.txt # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $crldir/crl.pem # The current CRL
private_key = $dir/private/rootprivkey.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 = 4096
default_keyfile = priv.key.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca
req_extensions = v3_req
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default =
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default =
localityName = Locality Name (eg, city)
localityName_default =
0.organizationName = Organization Name (eg, company)
0.organizationName_default =
# 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
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ 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:always
basicConstraints = CA:true
Best Answer
The above config file fails to generate the Root CA certificate in the first place because it doesn't have a
CommonName
entry. I'm assuming that was a typo or copy/paste error and the rest of the config file is the actual one used.Your certificate verification fails because your Root CA has the
BasicConstraint
extensions set toCA:False
. That is, it is not a CA and therefore as far as verification is concerned, cannot be used to verify the digital signature on other certificates.You need to alter your config file so that the command used to generate the CA certificate uses:
basicConstraints = CA:true
. To make it RFC 5280 compliant, you should also add the critical flag and usebasicConstraints = critical,CA:true
.Your approach is somewhat disjointed. Try the following:
Create an OpenSSL config file for the CA (
./openssl.cnf
)Next, create your request using a similar command to the one you used:
Note that the
-sigopt
options have been removed as the signature at this point is the request signature used for proof of possession of private key and not the signature of the certificate itself - that's later.Next, sign it to create the self-signed CA certificate:
Note that this uses
openssl ca
instead ofopenssl x509
which means you can refer to a customopenssl.cnf
file. Also note the use of the-extensions
option to point the command to a specific section of the config file. Finally, note that the-sigopt
options have been moved here as this is the command that signs your CA certificate and therefore should have your PSS scheme.Next, create a separate OpenSSL config file for your server/end-entity certificate (./server.cnf).
Run a similar command to the one you used, but with the config file changed.
Finally, sign it with the CA:
Note that there is no
-extensions
option on this one, so OpenSSL defaults to the section pointed to by thex509_extensions =
option inopenssl.cnf
.You can now verify the certificates: