OpenSSL: Generate ECC certificate & verify on Apache server

In this article we will explore Elliptic Curve Cryptography (ECC) and generate ECC certificates using OpenSSL. We will be creating CA certificate, server and client certificates using ECC private key and later we will use this certificate with Apache server for demonstration.

 

1. Overview on Elliptic Curve Cryptography (ECC)

  • Elliptic Curve Cryptography (ECC) is an encryption technique that provides public-key encryption similar to RSA.
  • While the security strength of RSA is based on very large prime numbers, ECC uses the mathematical theory of elliptic curves and achieves the same security level with much smaller keys.
  • ECC does not replace RSA for authenticating the communication partners, but is used for generating the ephemeral DH session key with the help of an EC private key. RSA is still used for providing authentication.
  • The main advantage of Elliptic Curve Cryptography with Diffie-Hellman (ECDHE-RSA) over plain Diffie-Hellman (DHE-RSA) is better performance and the same level of security with less key bits.
  • A disadvantage is the additional effort for creating and maintaining the EC key.

 

2. RSA vs ECC keys

Compared to traditional algorithms such as RSA, ECC makes it possible to create smaller keys, with obvious advantages both in terms of computational efficiency and the required working memory.

Advertisement

In the following table we have a comparison between different size of the RSA and ECC keys to achieve the same level of security:

RSA size (in bits)ECC size
1024160
2048224
3072256
7680384
15360521

 

However, ECC is mainly used in the management of key exchange and digital signatures, rather than in encryption. Even in the case of ECC, once a secure key exchange protocol has been established between two counterparts, it is possible to share a symmetric encryption key to carry out efficient communication encoding.

 

3. List available ECC curves

ECC is based on domain parameters defined by various standards. You can see the list of all available standards defined and recommended elliptical cryptography curves using the following openssl command.

[root@server ~]# openssl ecparam -list_curves
  secp256k1 : SECG curve over a 256 bit prime field
  secp384r1 : NIST/SECG curve over a 384 bit prime field
  secp521r1 : NIST/SECG curve over a 521 bit prime field
  prime256v1: X9.62/SECG curve over a 256 bit prime field

The recommended ECC key size is 256-bit so we wll use prime256v1 to generate all our ECC private keys in this tutorial. If greater encryption strength is required, your other private key option is secp384r1.

I will be using following version of openssl for this article:

[root@server ~]# rpm -q openssl
openssl-1.0.2k-19.el7.x86_64

 

4. Lab Environment

I will be using two virtual machines to generate and validate the ECC certificates. These virtual machines are running on Oracle VirtualBox and installed with CentOS 7 and 8. Out of these two VMs, one will act as a server will the other will act as a client. I will use the server node to generate all the certificates. The hostname of the server node is server.example.com with an IP address of 192.168.0.114 while the hostname of client node is server-2.example.com with an IP address of 192.168.0.152

Advertisement

 

5. Create CA certificate with ECC Key

First we would need a CA certificate required to sign the server and client certificate. We will use ECC private key to generate the root CA certificate. For this purpose I will create a separate directory structure to store the CA certificate, keys and index database:

[root@server ~]# mkdir /root/tls
[root@server tls]# cd /root/tls

In this directory I will create separate directory to store the private key and the CA certificate

[root@server tls]# mkdir private cert

Additionally we also need a serial and index.txt to track of the certificates that have been issued by the CA.

[root@server tls]# touch index.txt
[root@server tls]# echo 01 > serial

We will also need an openssl configuration file. You can take openssl.cnf file available inside /etc/pki/CA. I have created a new file for me inside /root/tls with the following content. You can read more about each section at Configure openssl.cnf for Root CA Certificate

[root@server tls]# cat /root/tls/openssl.cnf
#
# 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             = /root/tls             # Where everything is kept
certs           = $dir/certs            # Where the issued certs are kept
database        = $dir/index.txt        # database index file.
                                        # several certs with same subject.
new_certs_dir   = $dir/certs            # default place for new certs.
certificate     = $dir/certs/ec-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
private_key     = $dir/private/ec-cakey.pem # The private key

name_opt        = ca_default            # Subject Name options
cert_opt        = ca_default            # Certificate field options

default_days    = 365                   # how long to certify for
default_crl_days= 30                    # how long before next CRL
default_md      = sha256                # use SHA-256 by default
preserve        = no                    # keep passed DN ordering
policy          = policy_match

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

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

####################################################################
[ req ]
default_bits            = 2048
default_md              = sha256
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

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = IN
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
stateOrProvinceName_default     = Some-State
localityName                    = Locality Name (eg, city)
localityName_default            = BANGALORE
0.organizationName              = Organization Name (eg, company)
0.organizationName_default      = GoLinuxCloud
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name (eg, your name or your server\'s hostname)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20
unstructuredName                = An optional company name


[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]
# Extensions for a typical CA
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = critical,CA:true

[ crl_ext ]
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always

 

5.1 Create ECC Private key

We will create ECC private key using openssl command:

[root@server tls]# openssl ecparam -out private/ec-cakey.pem -name prime256v1 -genkey

By default, when generating a private key, openssl will only store the name of the curve in the generated parameters or key file, not the full set of explicit parameters associated with that name. For example:

[root@server tls]# openssl ecparam -in private/ec-cakey.pem -text -noout
ASN1 OID: prime256v1
NIST CURVE: P-256

 

5.2 Generate CA certificate

Next we will generate CA certificate using the ECC private key we created earlier.

[root@server tls]# openssl req -new -x509 -days 3650 -config openssl.cnf -extensions v3_ca -key private/ec-cakey.pem -out certs/ec-cacert.pem

Sample output snippet from my terminal:
OpenSSL: Generate ECC certificate & verify on Apache server

Advertisement

Next you can verify the content of the CA certificate and the signing algorithm used:

[root@server tls]# openssl x509 -noout -text -in certs/ec-cacert.pem | grep -i algorithm
    Signature Algorithm: ecdsa-with-SHA256
            Public Key Algorithm: id-ecPublicKey
    Signature Algorithm: ecdsa-with-SHA256

As you can see, we have used ECDSA Signature Algorithm to create our rootCA certificate instead of RSA Encryption algorithm.

 

5.3 Verify the CA certificate with private key

If you wish to verify a certificate with an private key (including ECDSA key) using openssl then get the public key from the certificate:

[root@server tls]# openssl x509 -noout -pubkey -in certs/ec-cacert.pem

Sample output from my terminal:
OpenSSL: Generate ECC certificate & verify on Apache server

Similarly, get the public key from the private key:

[root@server tls]# openssl pkey -pubout -in private/ec-cakey.pem

Sample output from my terminal:
OpenSSL: Generate ECC certificate & verify on Apache server

Now you can match both the public keys from certificate and the private key. As you can check, both are exactly same in my case.

 

6. Generate server certificate

We will now create server certificate using ECC private key. To store these server certificates I will create another directory /root/server_certs:

[root@server tls]# mkdir /root/server_certs/

[root@server tls]# cd /root/server_certs/

 

6.1 Generate ECC private key

We would need a private key for the server certificate. We will again use prime256v1 curve to generate this ECC key:

[root@server server_certs]# openssl ecparam -out server.key -name prime256v1 -genkey

Verify the name of the curve used in the private key:

[root@server server_certs]# openssl ecparam -in server.key -text -noout
ASN1 OID: prime256v1
NIST CURVE: P-256

 

6.2 Create Certificate Signing Request (CSR)

Next we need to create a CSR to sign the server certificate. The following command will prompt for multiple details such as Country Name, State or Province name, Locality name etc. It is important that you fill this data properly.

These data would be matched against the CA certificate while creating the server certificate. Our CA certificate was created using v3_ca extension from the openssl.cnf which contains following:

# For the CA policy
[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

So here the CA certificate will match Country Name, state or Province Name and Organization Name. SO the values you provide for your CSR of server certificate must match with what we gave earlier for the CA certificate.

The Common Name must be the hostname/FQDN name of the server node. The Name is the one which will be used by the client to connect and authenticate with the server. If you feel there can be more than one FQDN of the server, for example a client can send request via IP address of the server, or only the hostname or using a FQDN so in such case you should create a SAN certificate.

Execute the following command to generate the CSR:

[root@server server_certs]# openssl req -new -key server.key -out server.csr -sha256

Sample output from my terminal:
OpenSSL: Generate ECC certificate & verify on Apache server

 

6.3 Create server certificate

Now we will create our server certificate which would require the ECC CA private key, CA certificate to sign the certificate along with server.csr which we created in the previous step:

[root@server server_certs]# openssl ca -keyfile /root/tls/private/ec-cakey.pem -cert /root/tls/certs/ec-cacert.pem -in server.csr -out server.crt -config /root/tls/openssl.cnf
Using configuration from /root/tls/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Apr  9 18:09:14 2021 GMT
            Not After : Apr  9 18:09:14 2022 GMT
        Subject:
            countryName               = IN
            stateOrProvinceName       = Karnataka
            organizationName          = GoLinuxCloud
            organizationalUnitName    = Test
            commonName                = server
            emailAddress              = admin@golinuxcloud.com
Certificate is to be certified until Apr  9 18:09:14 2022 GMT (365 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

 

6.4 Validate the server certificate

Verify the server certificate against the CA certificate:

[root@server server_certs]# openssl verify -CAfile /root/tls/certs/ec-cacert.pem server.crt
server.crt: OK

You can also verify the Signature Algorithm to make sure it is using ECC Private Key:

[root@server server_certs]# openssl x509 -noout -text -in server.crt | grep -i algorithm
Signature Algorithm: ecdsa-with-SHA256
Public Key Algorithm: id-ecPublicKey
Signature Algorithm: ecdsa-with-SHA256

Verify the index.txt contain which should contain the detail of the server certificate which we just signed using the CA certificate. Here 01 is the serial which we have assigned at the starting of this article. Henceforth every time a certificate is signed, the serial file value will be incremented.

[root@server client_certs]# cat /root/tls/index.txt
V       220409180914Z           01      unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Test/CN=server/emailAddress=admin@golinuxcloud.com

 

7. Generate client certificate

We will also create a client certificate to have mutual TLS authentication (MTLS). If you only have a requirement of server certificate then you can ignore this step.

I will create a separate directory to create and store the client certificate:

[root@server ~]# mkdir /root/client_certs/

[root@server ~]# cd /root/client_certs/

 

7.1 Generate ECC private key

We would again need a private key for the client certificate. Since this article is all about generating ECC certificates so our private key should be of ECC format:

[root@server client_certs]# openssl ecparam -out client.key -name prime256v1 -genkey

Verify the name of the curve used in the private key:

[root@server server_certs]# openssl ecparam -in client.key -text -noout
ASN1 OID: prime256v1
NIST CURVE: P-256

 

7.2 Create Certificate Signing Request (CSR)

We will now create a certificate signing request for the client certificate. All the rules I explained under "Creating CSR for server certificate" section, also applies here. If you are using the openssl.cnf which I have shared then make sure the Country Name, State or Province Name and Organization Name matches with what we gave in the CA certificate.

[root@server client_certs]# openssl req -new -key client.key -out client.csr -sha256

The Common Name must contain the FQDN or hostname of the client node. In my case the FQDN of my client node is server-2.example.com which will send the authentication request to the server. You may also choose to create a SAN certificate with multiple Subject Alternative Names.

Sample output from my terminal:
OpenSSL: Generate ECC certificate & verify on Apache server

 

7.3 Create client certificate

Now we have everything needed to create our client certificate using openssl:

[root@server client_certs]# openssl ca -keyfile /root/tls/private/ec-cakey.pem -cert /root/tls/certs/ec-cacert.pem -in client.csr -out client.crt -config /root/tls/openssl.cnf
Using configuration from /root/tls/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: Apr  9 18:13:16 2021 GMT
            Not After : Apr  9 18:13:16 2022 GMT
        Subject:
            countryName               = IN
            stateOrProvinceName       = Karnataka
            organizationName          = GoLinuxCloud
            organizationalUnitName    = Dummy
            commonName                = server-2.example.com
            emailAddress              = admin@golinuxcloud.com
Certificate is to be certified until Apr  9 18:13:16 2022 GMT (365 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

You can also verify the index.txt to make sure, the database is updated and it contains the detail of server-2.example.com getting signed from the CA certificate:

[root@server client_certs]# cat /root/tls/index.txt
V       220409180914Z           01      unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Test/CN=server/emailAddress=admin@golinuxcloud.com
V       220409181316Z           02      unknown /C=IN/ST=Karnataka/O=GoLinuxCloud/OU=Dummy/CN=server-2.example.com/emailAddress=admin@golinuxcloud.com

 

8. Configure Apache with SSL (HTTPS) with ECC certificates

We will validate our ECC certificates using a simple apache server.

 

8.1 Install Apache packages

So first let us install the required packages to setup an HTTPS web server:

[root@server ~]# yum -y install httpd mod_ssl

 

8.2 Configure apache server

I will create a new directory certs under /etc/httpd/conf.d where I will store all the server certificates and the same path is provided in our httpd.conf

[root@server ~]# mkdir /etc/httpd/conf.d/certs
[root@server ~]# cd /etc/httpd/conf.d/certs

Next we will copy the server certificates to this location:

[root@server certs]# cp  -v /root/server_certs/* .
‘/root/server_certs/server.crt’ -> ‘./server.crt’
‘/root/server_certs/server.csr’ -> ‘./server.csr’
‘/root/server_certs/server.key’ -> ‘./server.key’

If you are setting up apache on a different server then you can copy these certificates to different node using scp or rsync accordingly.

We would also need the CA certificate here:

[root@server certs]# cp -v /root/tls/certs/ec-cacert.pem .
‘/root/tls/certs/ec-cacert.pem’ -> ‘./ec-cacert.pem’

We will change the port number of the apache server to 8443 in /etc/httpd/conf/httpd.conf:

Listen 8443

Also add the following content to the end of /etc/httpd/conf/httpd.conf file to configure virtual hosting:

<VirtualHost *:8443>
   SSLEngine On
   SSLCertificateFile /etc/httpd/conf.d/certs/server.crt
   SSLCertificateChainFile /etc/httpd/conf.d/certs/ec-cacert.pem
   SSLCertificateKeyFile /etc/httpd/conf.d/certs/server.key
   ServerAdmin root@server.example.com
   DocumentRoot /var/www/html
   ServerName server.example.com
   ErrorLog logs/server.example.com-error_log
   CustomLog logs/server.example.com-access_log common
</VirtualHost>

You can check the man page of mod_ssl to get more details on these individual parameters.

 

Enable port 8443 in the firewall on the server node, I am using firewalld in my setup:

[root@server certs]# firewall-cmd --add-port=8443/tcp --permanent
success

[root@server certs]# firewall-cmd --reload
success

I will add some content to my index.html on the web server:

[root@server certs]# echo "Welcome to server.example.com" > /var/www/html/index.html

Restart the apache service to activate the changes:

[root@server certs]# systemctl restart httpd

Make sure port 8443 is in LISTEN state used by httpd:

[root@server certs]# netstat -ntlp | grep 8443
tcp6       0      0 :::8443                 :::*                    LISTEN      7832/httpd

 

9. Configure client node for authentication

We will copy our client certificates over to the client node. I have already created a directory /root/certs on the client node to store the certificates and private key:

[root@server certs]# scp -r /root/client_certs/* 192.168.0.152:/root/certs/
root@192.168.0.152's password:
client.crt                                                                          100% 2084     3.6MB/s   00:00
client.csr                                                                          100%  554     1.1MB/s   00:00
client.key                                                                          100%  302   637.3KB/s   00:00

We would also need the CA certificate for the mutual TLS authentication:

[root@server certs]# scp -r /root/tls/certs/ec-cacert.pem  192.168.0.152:/root/certs/
root@192.168.0.152's password:
ec-cacert.pem                                                                       100%  940     1.5MB/s   00:00

 

10. Validate mutual TLS authentication with ECC certificates

Since I don't have any internal DNS to resolve the hostname of the server and client node, I will update the /etc/hosts of the client node with the server details:

[root@server-2 ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.0.114   server          server.example.com

Now we will use curl to connect to our web server from the client node:

[root@server-2 ~]# curl --key /root/certs/client.key  --cert /root/certs/client.crt --cacert /root/certs/ec-cacert.pem  https://server:8443 -v
* Rebuilt URL to: https://server:8443/
*   Trying 192.168.0.114...
* TCP_NODELAY set
* Connected to server (192.168.0.114) port 8443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /root/certs/ec-cacert.pem
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=IN; ST=Karnataka; O=GoLinuxCloud; OU=Test; CN=server; emailAddress=admin@golinuxcloud.com
*  start date: Apr  9 18:09:14 2021 GMT
*  expire date: Apr  9 18:09:14 2022 GMT
*  common name: server (matched)
*  issuer: C=IN; ST=Karnataka; L=BANGALORE; O=GoLinuxCloud; OU=GoLinuxCloud; CN=ca-server; emailAddress=admin@golinuxcloud.com
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: server:8443
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Fri, 09 Apr 2021 19:41:07 GMT
< Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips
< Last-Modified: Fri, 19 Mar 2021 09:40:58 GMT
< ETag: "1e-5bde083680e36"
< Accept-Ranges: bytes
< Content-Length: 30
< Content-Type: text/html; charset=UTF-8
<
Welcome to server.example.com
* Connection #0 to host server left intact

As you can see the connection was successful. So our ECC cretifates are working as expected.

 

11. Summary

In this tutorial we covered following topics

  • We learned about Elliptic Curve Cryptography (ECC) encryption algorithm.
  • We understood the difference between RSA and ECC keys
  • We created ECC private keys andverify the algorithm
  • We created CA certificate, server and client certificate using ECC private keys
  • We then validated our certificate authentication using an apache server.

 

12. Further Readings

What is Elliptic Curve Cryptography?
Command Line Elliptic Curve Operations
Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)
OpenSSL ECDSA

 

Related Searches: openssl ecdsa, generate ecdsa certificate openssl, openssl ecdsa example, openssl ecdsa sha256, openssl generate ecc certificate, openssl generate ecc certificate, generate ecdsa certificate openssl, ecdsa certificate example, openssl ec private key, ecdsa key generation

Didn't find what you were looking for? Perform a quick search across GoLinuxCloud

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can either use the comments section or contact me form.

Thank You for your support!!

Leave a Comment