+
2024-01-13 new version 1.8.5 released
+
+
Bug fixes:
+
+ - Code update to latest OpenSSL version 3 API calls
+
+
2021-12-20 new version 1.8.4 released
Bug fixes:
diff --git a/readme.md b/readme.md
index 1dae757..da1ec52 100644
--- a/readme.md
+++ b/readme.md
@@ -1,6 +1,7 @@
## WebCert
![test](https://github.com/fm4dd/webcert/workflows/test/badge.svg)
+[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](https://opensource.org/licenses/MIT)
#### A Web Tool for Generation and Management of digital Certificates
diff --git a/scripts/update-crl.sh b/scripts/update-crl.sh
index bdce68b..664e3a3 100755
--- a/scripts/update-crl.sh
+++ b/scripts/update-crl.sh
@@ -5,7 +5,7 @@
# and publishes a new CRL file. Runs daily from cron.
##########################################################
# set debug: 0=off 1=normal 2=verbose
-DEBUG=2
+DEBUG=0
##########################################################
# binaries location
@@ -73,7 +73,7 @@ CHECK_UPDATE() {
##########################################################
NEW_CRL() {
if [ $update_crl == "1" ]; then
- $OSSL ca -cert /srv/app/webCA/cacert.pem -gencrl -crldays 90 \
+ $OSSL ca -config /srv/app/webCA/webCA.cnf -gencrl -crldays 90 \
-out $crlfile --passin file:/srv/app/webCA/private/passin.src 2>/dev/null
[ $DEBUG == "2" ] && echo "update-crl.sh: created new crl file $crlfile"
diff --git a/scripts/webCA.cnf b/scripts/webCA.cnf
new file mode 100644
index 0000000..8039674
--- /dev/null
+++ b/scripts/webCA.cnf
@@ -0,0 +1,390 @@
+#
+# OpenSSL example configuration file.
+# See doc/man5/config.pod for more info.
+#
+# This is mostly being used for generation of certificate requests,
+# but may be used for auto loading of providers
+
+# Note that you can include other files from the main configuration
+# file using the .include directive.
+#.include filename
+
+# This definition stops the following lines choking if HOME isn't
+# defined.
+HOME = .
+
+ # Use this in order to automatically load providers.
+openssl_conf = openssl_init
+
+# Comment out the next line to ignore configuration errors
+config_diagnostics = 1
+
+# 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
+
+# For FIPS
+# Optionally include a file that is generated by the OpenSSL fipsinstall
+# application. This file contains configuration data required by the OpenSSL
+# fips provider. It contains a named section e.g. [fips_sect] which is
+# referenced from the [provider_sect] below.
+# Refer to the OpenSSL security policy for more information.
+# .include fipsmodule.cnf
+
+[openssl_init]
+# providers = provider_sect
+
+# List of providers to load
+# [provider_sect]
+# default = default_sect
+# The fips section name should match the section name inside the
+# included fipsmodule.cnf.
+# fips = fips_sect
+
+# If no providers are activated explicitly, the default one is activated implicitly.
+# See man 7 OSSL_PROVIDER-default for more details.
+#
+# If you add a section explicitly activating any other provider(s), you most
+# probably need to explicitly activate the default provider, otherwise it
+# becomes unavailable in openssl. As a consequence applications depending on
+# OpenSSL may not work correctly which could lead to significant system
+# problems including inability to remotely access the system.
+# [default_sect]
+# activate = 1
+
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = /srv/app/webCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certs with same subject.
+new_certs_dir = $dir/certs # default place for new certs.
+
+certificate = $dir/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
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+
+x509_extensions = usr_cert # The extensions 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= 90 # 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 = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extensions to add to the self signed cert
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
+# utf8only: only UTF8Strings (PKIX recommendation after 2004).
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
+string_mask = utf8only
+
+# req_extensions = v3_req # The extensions to add to a certificate request
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+countryName_min = 2
+countryName_max = 2
+
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Some-State
+
+localityName = Locality Name (eg, city)
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = FM4DD
+
+# we can do this but it is not needed normally :-)
+#1.organizationName = Second Organization Name (eg, company)
+#1.organizationName_default = World Wide Web Pty Ltd
+
+organizationalUnitName = Organizational Unit Name (eg, section)
+organizationalUnitName_default = Webcert Support
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+# 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
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+# This is required for TSA certificates.
+# extendedKeyUsage = critical,timeStamping
+
+[ 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
+
+basicConstraints = critical,CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Include email address in subject alt name: another PKIX recommendation
+# subjectAltName=email:copy
+# Copy issuer details
+# issuerAltName=issuer:copy
+
+# DER hex encoding of an extension: beware experts only!
+# obj=DER:02:03
+# Where 'obj' is a standard or added object
+# You can even override a supported extension:
+# basicConstraints= critical, DER:30:03:01:01:FF
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+# These extensions should be added when creating a proxy certificate
+
+# 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
+
+# This is typical in keyUsage for a client certificate.
+# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+# subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+# subjectAltName=email:move
+
+# Copy subject details
+# issuerAltName=issuer:copy
+
+# This really needs to be in place for it to be a proxy certificate.
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+####################################################################
+[ tsa ]
+
+default_tsa = tsa_config1 # the default TSA section
+
+[ tsa_config1 ]
+
+# These are used by the TSA reply generation only.
+dir = ./demoCA # TSA root directory
+serial = $dir/tsaserial # The current serial number (mandatory)
+crypto_device = builtin # OpenSSL engine to use for signing
+signer_cert = $dir/tsacert.pem # The TSA signing certificate
+ # (optional)
+certs = $dir/cacert.pem # Certificate chain to include in reply
+ # (optional)
+signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
+signer_digest = sha256 # Signing digest to use. (Optional)
+default_policy = tsa_policy1 # Policy if request did not specify it
+ # (optional)
+other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
+digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory)
+accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
+clock_precision_digits = 0 # number of digits after dot. (optional)
+ordering = yes # Is ordering defined for timestamps?
+ # (optional, default: no)
+tsa_name = yes # Must the TSA name be included in the reply?
+ # (optional, default: no)
+ess_cert_id_chain = no # Must the ESS cert id chain be included?
+ # (optional, default: no)
+ess_cert_id_alg = sha1 # algorithm to compute certificate
+ # identifier (optional, default: sha1)
+
+[insta] # CMP using Insta Demo CA
+# Message transfer
+server = pki.certificate.fi:8700
+# proxy = # set this as far as needed, e.g., http://192.168.1.1:8080
+# tls_use = 0
+path = pkix/
+
+# Server authentication
+recipient = "/C=FI/O=Insta Demo/CN=Insta Demo CA" # or set srvcert or issuer
+ignore_keyusage = 1 # potentially needed quirk
+unprotected_errors = 1 # potentially needed quirk
+extracertsout = insta.extracerts.pem
+
+# Client authentication
+ref = 3078 # user identification
+secret = pass:insta # can be used for both client and server side
+
+# Generic message options
+cmd = ir # default operation, can be overridden on cmd line with, e.g., kur
+
+# Certificate enrollment
+subject = "/CN=openssl-cmp-test"
+newkey = insta.priv.pem
+out_trusted = apps/insta.ca.crt # does not include keyUsage digitalSignature
+certout = insta.cert.pem
+
+[pbm] # Password-based protection for Insta CA
+# Server and client authentication
+ref = $insta::ref # 3078
+secret = $insta::secret # pass:insta
+
+[signature] # Signature-based protection for Insta CA
+# Server authentication
+trusted = $insta::out_trusted # apps/insta.ca.crt
+
+# Client authentication
+secret = # disable PBM
+key = $insta::newkey # insta.priv.pem
+cert = $insta::certout # insta.cert.pem
+
+[ir]
+cmd = ir
+
+[cr]
+cmd = cr
+
+[kur]
+# Certificate update
+cmd = kur
+oldcert = $insta::certout # insta.cert.pem
+
+[rr]
+# Certificate revocation
+cmd = rr
+oldcert = $insta::certout # insta.cert.pem
diff --git a/src/certexport.c b/src/certexport.c
index e10d270..b17d3eb 100644
--- a/src/certexport.c
+++ b/src/certexport.c
@@ -99,9 +99,9 @@ int cgiMain() {
fprintf(cgiOut, "
\n");
fprintf(cgiOut, "");
- fprintf(cgiOut, "",
+ fprintf(cgiOut, "", HTTP_TYPE,
cgiServerName, CERTEXPORTURL, certnamestr, format);
- fprintf(cgiOut, "http://%s%s/%s.%s",
+ fprintf(cgiOut, "%s://%s%s/%s.%s", HTTP_TYPE,
cgiServerName, CERTEXPORTURL, certnamestr, format);
fprintf(cgiOut, " | ");
fprintf(cgiOut, "
\n");
@@ -213,9 +213,15 @@ int cgiMain() {
* These function calls are essential to make many PEM + other openssl *
* functions work. *
* -------------------------------------------------------------------------- */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
ERR_load_BIO_strings();
+#endif
/* -------------------------------------------------------------------------- *
* read the certstore certificate and define a BIO output stream *
@@ -337,9 +343,9 @@ int cgiMain() {
fprintf(cgiOut, "
\n");
fprintf(cgiOut, "");
- fprintf(cgiOut, "\n",
+ fprintf(cgiOut, "\n", HTTP_TYPE,
cgiServerName, CERTEXPORTURL, certnamestr, format);
- fprintf(cgiOut, "http://%s%s/%s.%s\n",
+ fprintf(cgiOut, "%s://%s%s/%s.%s\n", HTTP_TYPE,
cgiServerName, CERTEXPORTURL, certnamestr, format);
fprintf(cgiOut, " | \n");
fprintf(cgiOut, "
\n");
diff --git a/src/certrenew.c b/src/certrenew.c
index f29cf4c..f10abba 100644
--- a/src/certrenew.c
+++ b/src/certrenew.c
@@ -23,9 +23,16 @@ int cgiMain() {
/* ---------------------------------------------------------- *
* These function calls initialize openssl for correct work. *
* ---------------------------------------------------------- */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
OpenSSL_add_all_algorithms();
- ERR_load_BIO_strings();
ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+#endif
+
add_missing_ev_oids();
outbio = BIO_new(BIO_s_file());
@@ -153,7 +160,14 @@ int cgiMain() {
* ---------------------------------------------------------- */
char cmp_res1_str[40]; // contains the string for match, missmatch, etc
int cmp_res1;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 changed EVP_PKEY_cmp() to EVP_PKEY_eq():
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+ cmp_res1 = EVP_PKEY_eq(priv_key, pub_key);
+#else
cmp_res1 = EVP_PKEY_cmp(priv_key, pub_key);
+#endif
if(cmp_res1 == -2) {
snprintf(error_str, sizeof(error_str), "Error in EVP_PKEY_cmp(): operation is not supported.");
diff --git a/src/certrevoke.c b/src/certrevoke.c
index f97cfd3..519f06a 100644
--- a/src/certrevoke.c
+++ b/src/certrevoke.c
@@ -47,9 +47,15 @@ int cgiMain() {
* These function calls are essential to make many PEM + *
* other openssl functions work. *
* ---------------------------------------------------------- */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
ERR_load_BIO_strings();
+#endif
/* ---------------------------------------------------------- *
* process the CGI calling arguments *
@@ -194,7 +200,14 @@ int cgiMain() {
* ---------------------------------------------------------- */
char cmp_res1_str[40]; // contains the string for match, missmatch, etc
int cmp_res1;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 changed EVP_PKEY_cmp() to EVP_PKEY_eq():
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+ cmp_res1 = EVP_PKEY_eq(priv_key, pub_key);
+#else
cmp_res1 = EVP_PKEY_cmp(priv_key, pub_key);
+#endif
if(cmp_res1 == -2) int_error("Cert key problem in EVP_PKEY_cmp(): operation is not supported");
if(cmp_res1 == -1) snprintf(cmp_res1_str, sizeof(cmp_res1_str), "Cert key type missmatch");
@@ -213,7 +226,13 @@ int cgiMain() {
if (! (revo_key = PEM_read_bio_PUBKEY(revbio, NULL, NULL, NULL)))
int_error("Error loading revocation key content");
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 changed EVP_PKEY_cmp() to EVP_PKEY_eq():
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+ cmp_res1 = EVP_PKEY_eq(priv_key, revo_key);
+#else
cmp_res1 = EVP_PKEY_cmp(priv_key, revo_key);
+#endif
if(cmp_res1 == -2) int_error("Revocation key problem in EVP_PKEY_cmp(): operation is not supported");
if(cmp_res1 == -1) snprintf(cmp_res1_str, sizeof(cmp_res1_str), "Revocation key type missmatch");
diff --git a/src/certsearch.c b/src/certsearch.c
index cc32a0a..82931ba 100644
--- a/src/certsearch.c
+++ b/src/certsearch.c
@@ -548,10 +548,10 @@ int cgiMain() {
int_error("Error retrieving CGI form expiration end time.");
strncat(exp_startstr, exp_startdate, sizeof(exp_startstr)-1);
- strncat(exp_startstr, " ", 1); /* add a space between date and time */
+ strcat(exp_startstr, " "); /* add a space between date and time */
strncat(exp_startstr, exp_starttime, sizeof(exp_startstr)-strlen(exp_startstr)-1);
strncat(exp_endstr, exp_enddate, sizeof(exp_endstr)-1);
- strncat(exp_endstr, " ", 1); /* add a space between date and time */
+ strcat(exp_endstr, " "); /* add a space between date and time */
strncat(exp_endstr, exp_endtime, sizeof(exp_endstr)-strlen(exp_endstr)-1);
snprintf(title, sizeof(title), "Search Certs by Expiration");
snprintf(subtitle, sizeof(subtitle), "Certificates with expiration between %s and %s", exp_startstr, exp_endstr);
@@ -574,10 +574,10 @@ int cgiMain() {
int_error("Error retrieving CGI form enable end time.");
strncat(ena_startstr, ena_startdate, sizeof(ena_startstr)-1);
- strncat(ena_startstr, " ", 1); /* add a space between date and time */
+ strcat(ena_startstr, " "); /* add a space between date and time */
strncat(ena_startstr, ena_starttime, sizeof(ena_startstr)-strlen(ena_startstr)-1);
strncat(ena_endstr, ena_enddate, sizeof(ena_endstr)-1);
- strncat(ena_endstr, " ", 1); /* add a space between date and time */
+ strcat(ena_endstr, " "); /* add a space between date and time */
strncat(ena_endstr, ena_endtime, sizeof(ena_endstr)-strlen(ena_endstr)-1);
snprintf(title, sizeof(title), "Search Certs by Start Date");
snprintf(subtitle, sizeof(subtitle), "Certificates with start date between %s and %s", ena_startstr, ena_endstr);
@@ -600,10 +600,10 @@ int cgiMain() {
int_error("Error retrieving CGI form enable end time.");
strncat(rev_startstr, rev_startdate, sizeof(rev_startstr)-1);
- strncat(rev_startstr, " ", 1); /* add a space between date and time */
+ strcat(rev_startstr, " "); /* add a space between date and time */
strncat(rev_startstr, rev_starttime, sizeof(rev_startstr)-strlen(rev_startstr)-1);
strncat(rev_endstr, rev_enddate, sizeof(rev_endstr)-1);
- strncat(rev_endstr, " ", 1); /* add a space between date and time */
+ strcat(rev_endstr, " "); /* add a space between date and time */
strncat(rev_endstr, rev_endtime, sizeof(rev_endstr)-strlen(rev_endstr)-1);
snprintf(title, sizeof(title), "Search Revoked Certificates");
snprintf(subtitle, sizeof(subtitle), "Certificates revoked between %s and %s", rev_startstr, rev_endstr);
diff --git a/src/certsign.c b/src/certsign.c
index 6688d59..8f68551 100644
--- a/src/certsign.c
+++ b/src/certsign.c
@@ -49,9 +49,15 @@ int cgiMain() {
* These function calls are essential to make many PEM + *
* other openssl functions work. *
* ---------------------------------------------------------- */
- OpenSSL_add_all_algorithms();
- ERR_load_crypto_strings();
- ERR_load_BIO_strings();
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+#endif
/* ---------------------------------------------------------- *
* check if a certificate was handed to certsign.cgi or if *
diff --git a/src/certvalidate.c b/src/certvalidate.c
index 29f4e21..0a9fb65 100644
--- a/src/certvalidate.c
+++ b/src/certvalidate.c
@@ -98,9 +98,16 @@ int cgiMain() {
/* ---------------------------------------------------------- *
* These function calls initialize openssl for correct work. *
* ---------------------------------------------------------- */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
OpenSSL_add_all_algorithms();
- ERR_load_BIO_strings();
ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+#endif
+
add_missing_ev_oids();
time(&now);
diff --git a/src/genrequest.c b/src/genrequest.c
index 1b8d50c..827c538 100644
--- a/src/genrequest.c
+++ b/src/genrequest.c
@@ -41,9 +41,6 @@ int cgiMain() {
X509_REQ *webrequest = NULL;
EVP_PKEY *pkey = NULL;
X509_NAME *reqname = NULL;
- DSA *mydsa = NULL;
- RSA *myrsa = NULL;
- EC_KEY *myecc = NULL;
EVP_MD const *digest = NULL;
BIO *outbio = NULL;
@@ -117,13 +114,22 @@ int cgiMain() {
if(strlen(cname) == 0)
int_error("No CN has been provided. The CN field is mandatory.");
+ if(cgiFormString("sigalg", sigalgstr, sizeof(sigalgstr)) != cgiFormSuccess)
+ int_error("Error getting the signature algorithm from buildrequest.cgi form");
+
/* ------------------------------------------------------------------------ *
* These function calls are essential to make many PEM + other openssl *
* functions work. *
* ------------------------------------------------------------------------ */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
OpenSSL_add_all_algorithms();
ERR_load_crypto_strings();
ERR_load_BIO_strings();
+#endif
/* ------------------------------------------------------------------------- *
* Generate the key pair based on the selected keytype *
@@ -131,30 +137,64 @@ int cgiMain() {
if ((pkey=EVP_PKEY_new()) == NULL)
int_error("Error creating EVP_PKEY structure.");
- BIGNUM *exp = BN_new();
- BN_set_word(exp, RSA_F4);
-
if(strcmp(keytype, "rsa") == 0) {
- myrsa = RSA_new();
+ BIGNUM *exp = BN_new();
+ BN_set_word(exp, RSA_F4); // this sets e=65537 (0x10001L)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_PKEY_CTX *ctx = NULL;
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
+ EVP_PKEY_keygen_init(ctx);
+ EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, rsastrength);
+ EVP_PKEY_CTX_set_rsa_keygen_primes(ctx, 2);
+// EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx, RSA_F4);
+ EVP_PKEY_generate(ctx, &pkey);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ RSA *myrsa = RSA_new();
if (! (RSA_generate_key_ex(myrsa, rsastrength, exp, NULL)))
int_error("Error generating the RSA key.");
if (!EVP_PKEY_assign_RSA(pkey,myrsa))
int_error("Error assigning RSA key to EVP_PKEY structure.");
+#endif
}
else if(strcmp(keytype, "dsa") == 0) {
- mydsa = DSA_new();
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *paramkey = NULL;
+ pctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL);
+ if(!EVP_PKEY_paramgen_init(pctx))
+ int_error("Error initializing DSA key parameters.");
+ if(!EVP_PKEY_CTX_set_dsa_paramgen_bits(pctx, dsastrength))
+ int_error("Error setting DSA key strength.");
+ if (!EVP_PKEY_paramgen(pctx, ¶mkey))
+ int_error("Error setting DSA parameter list object.");
+ EVP_PKEY_CTX_free(pctx);
+
+ EVP_PKEY_CTX *ctx = NULL;
+ ctx = EVP_PKEY_CTX_new(paramkey, NULL);
+ if(!EVP_PKEY_keygen_init(ctx))
+ int_error("Error initializing DSA key.");
+ if(!EVP_PKEY_keygen(ctx, &pkey))
+ int_error("Error generating the DSA key.");
+ EVP_PKEY_CTX_free(ctx);
+#else
+ DSA *mydsa = DSA_new();
DSA_generate_parameters_ex(mydsa, dsastrength, NULL, 0, NULL, NULL, NULL);
if (! (DSA_generate_key(mydsa)))
int_error("Error generating the DSA key.");
if (!EVP_PKEY_assign_DSA(pkey,mydsa))
int_error("Error assigning DSA key to EVP_PKEY structure.");
+#endif
}
else if(strcmp(keytype, "ecc") == 0) {
- myecc = EC_KEY_new();
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ pkey = EVP_EC_gen(eccstrength);
+#else
+ EC_KEY *myecc = EC_KEY_new();
int eccgrp = OBJ_txt2nid(eccstrength);
myecc = EC_KEY_new_by_curve_name(eccgrp);
/* Important to set the OPENSSL_EC_NAMED_CURVE flag, *
@@ -165,13 +205,11 @@ int cgiMain() {
if (!EVP_PKEY_assign_EC_KEY(pkey,myecc))
int_error("Error assigning ECC key to EVP_PKEY structure.");
+#endif
}
else
int_error("Error: Wrong keytype - choose either RSA, DSA or ECC.");
- if(cgiFormString("sigalg", sigalgstr, sizeof(sigalgstr)) != cgiFormSuccess)
- int_error("Error getting the signature algorithm from buildrequest.cgi form");
-
/* ------------------------------------------------------------------------- *
* Generate the certificate request from scratch *
* ------------------------------------------------------------------------- */
diff --git a/src/keycompare.c b/src/keycompare.c
index 2e367de..a89ba61 100644
--- a/src/keycompare.c
+++ b/src/keycompare.c
@@ -18,11 +18,16 @@
#include
#include "webcert.h"
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include
+#include
+#endif
+
int key_enc_check(EVP_PKEY *, EVP_PKEY *);
-int rsa_cmp_mod(RSA *, RSA *);
-int rsa_enc_check(RSA *, RSA *);
-int dsa_enc_check(DSA *, DSA *);
+int rsa_cmp_mod(EVP_PKEY *, EVP_PKEY *);
+int rsa_enc_check(EVP_PKEY *, EVP_PKEY *);
+int dsa_enc_check(EVP_PKEY *, EVP_PKEY *);
int ec_enc_check(EC_KEY *, EC_KEY *);
int cgiMain() {
@@ -30,9 +35,15 @@ int cgiMain() {
* These function calls are essential to make many PEM + other*
* OpenSSL functions work. *
* ---------------------------------------------------------- */
- OpenSSL_add_all_algorithms();
- ERR_load_crypto_strings();
- ERR_load_BIO_strings();
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+#endif
static char title[] = "Key Checker";
@@ -196,7 +207,14 @@ int cgiMain() {
* ---------------------------------------------------------- */
char cmp_res1_str[40]; // contains the string for match, missmatch, etc
int cmp_res1;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 changed EVP_PKEY_cmp() to EVP_PKEY_eq():
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+ cmp_res1 = EVP_PKEY_eq(priv_key, pub_key);
+#else
cmp_res1 = EVP_PKEY_cmp(priv_key, pub_key);
+#endif
if(cmp_res1 == -2) {
snprintf(error_str, sizeof(error_str), "Error in EVP_PKEY_cmp(): operation is not supported.");
@@ -263,15 +281,7 @@ int key_data_check(EVP_PKEY *priv, EVP_PKEY *pub) {
switch (EVP_PKEY_base_id(priv)) {
case EVP_PKEY_RSA:
if(EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) {
- RSA *privrsa, *pubrsa;
-
- if((privrsa = EVP_PKEY_get1_RSA(priv)) == NULL)
- int_error("Error getting RSA private key data.");
-
- if((pubrsa = EVP_PKEY_get1_RSA(pub)) == NULL)
- int_error("Error getting RSA public key data.");
-
- ret = rsa_cmp_mod(privrsa, pubrsa);
+ ret = rsa_cmp_mod(priv, pub);
return ret;
}
else return -1;
@@ -305,73 +315,78 @@ int key_data_check(EVP_PKEY *priv, EVP_PKEY *pub) {
* matches by comparing the RSA modulus. Returns 1 for OK, 0 for *
* missmatch. *
* ------------------------------------------------------------- */
-int rsa_cmp_mod(RSA *priv, RSA *pub) {
+int rsa_cmp_mod(EVP_PKEY *priv, EVP_PKEY *pub) {
int match;
- const BIGNUM **priv_mod = NULL;
- const BIGNUM **pub_mod = NULL;
- RSA_get0_key(priv, priv_mod, NULL, NULL);
- char *priv_mod_hex = BN_bn2hex(*priv_mod);
-
- RSA_get0_key(pub, pub_mod, NULL, NULL);
- char *pub_mod_hex = BN_bn2hex(*pub_mod);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ /* -------------------------------------------------------- *
+ * Old code for OpenSSL versions before version 3.0 *
+ * ---------------------------------------------------------*/
+ const BIGNUM *priv_mod;
+ const BIGNUM *pub_mod;
+ RSA *privrsa = NULL;
+ if((privrsa = EVP_PKEY_get1_RSA(priv)) == NULL)
+ int_error("Error getting RSA private key data.");
+ RSA_get0_key(privrsa, &priv_mod, NULL, NULL);
+
+ RSA *pubrsa = NULL;
+ if((pubrsa = EVP_PKEY_get1_RSA(pub)) == NULL)
+ int_error("Error getting RSA public key data.");
+ RSA_get0_key(pubrsa, &pub_mod, NULL, NULL);
+#else
+ /* -------------------------------------------------------- *
+ * New code for latest OpenSSL version 3.0 *
+ * ---------------------------------------------------------*/
+ BIGNUM *priv_mod = NULL;
+ BIGNUM *pub_mod = NULL;
+ EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_N, &priv_mod);
+ EVP_PKEY_get_bn_param(pub, OSSL_PKEY_PARAM_RSA_N, &pub_mod);
+#endif
+
+ char *priv_mod_hex = BN_bn2hex(priv_mod);
+ char *pub_mod_hex = BN_bn2hex(pub_mod);
//printf("priv: %s\n", priv_mod_hex);
- //printf("pub: %s\n", pub_mod_hex);
+ //printf(" pub: %s\n", pub_mod_hex);
if(strcmp(priv_mod_hex, pub_mod_hex) == 0)
match = 1; // the keys modulus is matching
else
match = 0; // the keys modulus don't match
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
OPENSSL_free(priv_mod_hex);
OPENSSL_free(pub_mod_hex);
+#endif
return match;
}
/* ------------------------------------------------------------- *
* Function key_encr_check() checks if a public and private key *
* matches by doing EVP_PKEY_sign/EVP_PKEY_verify. Returns 1 for *
- * OK, 0 for key missmatch, -1 for type missmatch. *
+ * OK the keys match, 0 for key missmatch, -1 for type missmatch *
* ------------------------------------------------------------- */
int key_enc_check(EVP_PKEY *priv, EVP_PKEY *pub) {
int ret = -1;
- switch (EVP_PKEY_base_id(priv)) {
+ switch (EVP_PKEY_id(priv)) {
case EVP_PKEY_RSA:
- if(EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) {
- RSA *privrsa, *pubrsa;
-
- if((privrsa = EVP_PKEY_get1_RSA(priv)) == NULL)
- int_error("Error getting RSA private key data.");
-
- if((pubrsa = EVP_PKEY_get1_RSA(pub)) == NULL)
- int_error("Error getting RSA public key data.");
-
- ret = rsa_enc_check(privrsa, pubrsa);
- RSA_free(privrsa);
- RSA_free(pubrsa);
- return ret;
- }
- else return -1;
+ /* ------------------------------------------------------- *
+ ` * Type match RSA: If privkey is RSA, is pubkey also RSA? *
+ * If not return -1, if yes do encrypt/decrypt test next *
+ * ------------------------------------------------------- */
+ if(EVP_PKEY_id(pub) != EVP_PKEY_RSA) return -1;
+ ret = rsa_enc_check(priv, pub);
+ return ret;
break;
case EVP_PKEY_DSA:
- if(EVP_PKEY_base_id(pub) == EVP_PKEY_DSA) {
- DSA *privdsa, *pubdsa;
-
- if((privdsa = EVP_PKEY_get1_DSA(priv)) == NULL)
- int_error("Error getting DSA private key data.");
-
- if((pubdsa = EVP_PKEY_get1_DSA(pub)) == NULL)
- int_error("Error getting DSA public key data.");
-
- ret = dsa_enc_check(privdsa, pubdsa);
- DSA_free(privdsa);
- DSA_free(pubdsa);
- return ret;
- }
- else
- int_error("Error public key type does not match private DSA key");
+ /* ------------------------------------------------------- *
+ ` * Type match DSA: If privkey is DSA, is pubkey also DSA? *
+ * If not return -1, if yes do encrypt/decrypt test next *
+ * ------------------------------------------------------- */
+ if(EVP_PKEY_id(pub) != EVP_PKEY_DSA) return -1;
+ ret = dsa_enc_check(priv, pub);
+ return ret;
break;
case EVP_PKEY_EC:
@@ -389,53 +404,135 @@ int key_enc_check(EVP_PKEY *priv, EVP_PKEY *pub) {
return ret;
}
-int rsa_enc_check(RSA *priv, RSA *pub) {
+/* ------------------------------------------------------------ *
+ * rsa_enc_check takes a private and public key to encrypt and *
+ * decrypt a test string, then compares the decrypted string. *
+ * ------------------------------------------------------------ */
+int rsa_enc_check(EVP_PKEY *priv, EVP_PKEY *pub) {
int match = -1;
/* ---------------------------------------------------------- *
- * Create a random 512 byte md string for signing *
+ * Create a random 24 byte test string to encrypt with priv *
* ---------------------------------------------------------- */
- const char md[] = "This is a secret string";
+ const unsigned char md[] = "This is a secret string";
size_t md_len = sizeof(md);
/* ---------------------------------------------------------- *
- * Define the encrypted buffer, assign memory *
+ * Define the RSA padding method for both encrypt/decrypt ops *
* ---------------------------------------------------------- */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ const char pad = RSA_PKCS1_PADDING;
+#else
+ OSSL_PARAM params[2];
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE,
+ OSSL_PKEY_RSA_PAD_MODE_PKCSV15, 0);
+ params[1] = OSSL_PARAM_construct_end();
+ EVP_PKEY_CTX *ctx = NULL;
+#endif
+
+ /* ---------------------------------------------------------- *
+ * Define the encrypted buffer, assign and zero out memory *
+ * buffer size is hardcoded as 1024 bytes (1K) to simplify. *
+ * watch buffer overrun if we get large key sizes or large *
+ * md test strings. *
+ * ---------------------------------------------------------- */
+ size_t enc_len;
unsigned char *enc_str;
- enc_str = OPENSSL_malloc(RSA_size(priv));
+ enc_str = OPENSSL_zalloc(1024);
if (!enc_str)
int_error("Error allocating memory for encryption result.");
/* ---------------------------------------------------------- *
* Encrypt string with private RSA key *
* ---------------------------------------------------------- */
- size_t enc_len;
- const unsigned char pad = RSA_PKCS1_PADDING;
-
- enc_len = RSA_public_encrypt(md_len, (unsigned char*) md, enc_str, priv, pad);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ RSA *privrsa;
+ if((privrsa = EVP_PKEY_get1_RSA(priv)) == NULL)
+ int_error("Error getting RSA private key data.");
+ enc_len = RSA_public_encrypt(md_len, (unsigned char*) md, enc_str, privrsa, pad);
+ RSA_free(privrsa);
+# else
+ ctx = NULL;
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, priv, NULL);
+ EVP_PKEY_encrypt_init_ex(ctx, params);
+ EVP_PKEY_encrypt(ctx, enc_str, (size_t *) &enc_len, md, md_len);
+ EVP_PKEY_CTX_free(ctx);
+#endif
if(enc_len <= 0)
int_error("Error encrypting digest with private RSA key.");
/* ---------------------------------------------------------- *
* Successfully encrypted, now decrypt it with public RSA key *
+ * buffer size is hardcoded as 1024 bytes (1K) to simplify. *
+ * watch buffer overrun if we set larger md test strings. *
* ---------------------------------------------------------- */
- char *clr_str;
- clr_str = OPENSSL_malloc(RSA_size(pub));
- if (!clr_str)
- int_error("Error allocating memory for decryption result.");
-
size_t clr_len;
- clr_len = RSA_private_decrypt(enc_len, enc_str, (unsigned char*) clr_str, pub, pad);
+ unsigned char clr_str[1024] = "";
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ RSA *pubrsa;
+ if((pubrsa = EVP_PKEY_get1_RSA(pub)) == NULL)
+ int_error("Error getting RSA public key data.");
+ clr_len = RSA_private_decrypt(enc_len, enc_str, (unsigned char*) clr_str, pubrsa, pad);
+ RSA_free(pubrsa);
+#else
+ ctx = NULL;
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pub, NULL);
+ EVP_PKEY_decrypt_init_ex(ctx, params);
+ EVP_PKEY_decrypt(ctx, clr_str, (size_t *) &clr_len, enc_str, enc_len);
+ EVP_PKEY_CTX_free(ctx);
+#endif
if(clr_len <= 0)
int_error("Error decrypting digest with public RSA key.");
-
- if(strcmp(md, clr_str) == 0) match = 1; // The keys match
+ /* ---------------------------------------------------------- *
+ * Successfully decrypted, compare decrypt string with orig. *
+ * ---------------------------------------------------------- */
+ if(strcmp((char *)md, (char *)clr_str) == 0) match = 1; // The keys match
else match = 0; // The keys don't match
-
return match;
}
-int dsa_enc_check(DSA *priv, DSA *pub) {
+/* ------------------------------------------------------------ *
+ * dsa_enc_check takes a private and public key to encrypt and *
+ * decrypt a test string, then compares the decrypted string. *
+ * THIS FUNCTION IS IN DEV - not implemented yet!!!!!!!!!!!!! *
+ * ------------------------------------------------------------ */
+int dsa_enc_check(EVP_PKEY *priv, EVP_PKEY *pub) {
+ //int match = -1;
+ /* ---------------------------------------------------------- *
+ * Create a random 24 byte test string to encrypt with priv *
+ * ---------------------------------------------------------- */
+ //const unsigned char md[] = "This is a secret string";
+ //size_t md_len = sizeof(md);
+
+ /* ---------------------------------------------------------- *
+ * Define the encrypted buffer, assign and zero out memory *
+ * buffer size is hardcoded as 1024 bytes (1K) to simplify. *
+ * watch buffer overrun if we get large key sizes or large *
+ * md test strings. *
+ * ---------------------------------------------------------- */
+ //size_t enc_len;
+ unsigned char *enc_str;
+ enc_str = OPENSSL_zalloc(1024);
+ if (!enc_str)
+ int_error("Error allocating memory for encryption result.");
+
+ /* ---------------------------------------------------------- *
+ * Encrypt string with private DSA key *
+ * ---------------------------------------------------------- */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+ DSA *privdsa;
+ if((privdsa = EVP_PKEY_get1_DSA(priv)) == NULL)
+ int_error("Error getting DSA private key data.");
+ // encryption function goes here
+ DSA_free(privdsa);
+
+ DSA *pubdsa;
+ if((pubdsa = EVP_PKEY_get1_DSA(pub)) == NULL)
+ int_error("Error getting DSA public key data.");
+ // decryption function goes here
+ DSA_free(pubdsa);
+#endif
return 1;
}
int ec_enc_check(EC_KEY *priv, EC_KEY *pub) {
diff --git a/src/p12convert.c b/src/p12convert.c
index ca87c9f..c94097d 100644
--- a/src/p12convert.c
+++ b/src/p12convert.c
@@ -38,9 +38,15 @@ int cgiMain() {
* These function calls are essential to make many PEM + other*
* OpenSSL functions work. *
* ---------------------------------------------------------- */
- OpenSSL_add_all_algorithms();
- ERR_load_crypto_strings();
- ERR_load_BIO_strings();
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ // OpenSSL v3.0 now loads error strings automatically:
+ // https://www.openssl.org/docs/manmaster/man7/migration_guide.html
+#else
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+ ERR_load_BIO_strings();
+#endif
/* ---------------------------------------------------------- *
* If called w/o arguments, display the data gathering form. *
@@ -409,9 +415,9 @@ int cgiMain() {
fprintf(cgiOut, "");
fprintf(cgiOut, "PKCS12 URL: | ");
fprintf(cgiOut, "");
- fprintf(cgiOut, "",
+ fprintf(cgiOut, "", HTTP_TYPE,
cgiServerName, CERTEXPORTURL, p12name);
- fprintf(cgiOut, "http://%s%s/tmp/%s\n",
+ fprintf(cgiOut, "%s://%s%s/tmp/%s\n", HTTP_TYPE,
cgiServerName, CERTEXPORTURL, p12name);
fprintf(cgiOut, " | \n");
fprintf(cgiOut, "\n");
diff --git a/src/pagefoot.c b/src/pagefoot.c
index 8d7d65b..751207b 100644
--- a/src/pagefoot.c
+++ b/src/pagefoot.c
@@ -32,7 +32,7 @@ void pagefoot() {
fprintf(cgiOut, "