Add hostname listener for generating self-signed HTTPS certificate
- Add a hostname listener that will create a self-signed HTTPS
certificate with the appropriate subject when the BMC gets its
hostname assigned via IPMI. The "insecure-disable-ssl" must be
disabled for this feature to take effect.
Note:
- New self-signed certificate subject: C=US, O=OpenBMC, CN=${hostname}
- If the same hostname is assigned, it will not be triggered
- Only the self-signed certificate with Netscape Comment of
"Generated from OpenBMC service" will be replaced
Details about certificate key usage:
- NID_basic_constraints
The CA boolean indicates whether the certified public key may be
used to verify certificate signatures.
Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.9
- NID_subject_alt_name
Although the use of the Common Name is existing practice, it is
deprecated and Certification Authorities are encouraged to use the
dNSName instead.
Refer to: https://tools.ietf.org/html/rfc2818#section-3.1
- NID_subject_key_identifier
The subject key identifier extension provides a means of
identifying certificates that contain a particular public key.
Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.2
- NID_authority_key_identifier
The authority key identifier extension provides a means of
identifying the public key corresponding to the private key used
to sign a certificate.
Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.1
- NID_key_usage
- NID_ext_key_usage
id-kp-serverAuth
-- TLS WWW server authentication
-- Key usage bits that may be consistent: digitalSignature,
-- keyEncipherment or keyAgreement
Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.3
Refer to: https://tools.ietf.org/html/rfc5280#section-4.2.1.12
Tested:
- To test and verify the service is functionally working correctly,
we can use `openssl` and `ipmitool` to execute the following
commands:
- Assign BMC hostname
ipmitool -H $IP -I lanplus -U root -P 0penBmc -C 17 dcmi
set_mc_id_string $hostname
- Get BMC server certificate infomation
echo quit | openssl s_client -showcerts -servername $IP -connect
$IP:443
Signed-off-by: Alan Kuo <Alan_Kuo@quantatw.com>
Change-Id: I24aeb4d2fb46ff5f0cc1c6aa65984f46b0e1d3e2
diff --git a/include/ssl_key_handler.hpp b/include/ssl_key_handler.hpp
index 19d3ec7..93fe5d0 100644
--- a/include/ssl_key_handler.hpp
+++ b/include/ssl_key_handler.hpp
@@ -18,6 +18,7 @@
namespace ensuressl
{
constexpr char const* trustStorePath = "/etc/ssl/certs/authority";
+constexpr char const* x509Comment = "Generated from OpenBMC service";
static void initOpenssl();
static EVP_PKEY* createEcKey();
@@ -170,7 +171,56 @@
return certValid;
}
-inline void generateSslCertificate(const std::string& filepath)
+inline X509* loadCert(const std::string& filePath)
+{
+ BIO* certFileBio = BIO_new_file(filePath.c_str(), "rb");
+ if (!certFileBio)
+ {
+ BMCWEB_LOG_ERROR << "Error occured during BIO_new_file call, "
+ << "FILE= " << filePath;
+ return nullptr;
+ }
+
+ X509* cert = X509_new();
+ if (!cert)
+ {
+ BMCWEB_LOG_ERROR << "Error occured during X509_new call, "
+ << ERR_get_error();
+ BIO_free(certFileBio);
+ return nullptr;
+ }
+
+ if (!PEM_read_bio_X509(certFileBio, &cert, nullptr, nullptr))
+ {
+ BMCWEB_LOG_ERROR << "Error occured during PEM_read_bio_X509 call, "
+ << "FILE= " << filePath;
+
+ BIO_free(certFileBio);
+ X509_free(cert);
+ return nullptr;
+ }
+ return cert;
+}
+
+inline int addExt(X509* cert, int nid, const char* value)
+{
+ X509_EXTENSION* ex = nullptr;
+ X509V3_CTX ctx;
+ X509V3_set_ctx_nodb(&ctx);
+ X509V3_set_ctx(&ctx, cert, cert, nullptr, nullptr, 0);
+ ex = X509V3_EXT_conf_nid(nullptr, &ctx, nid, const_cast<char*>(value));
+ if (!ex)
+ {
+ BMCWEB_LOG_ERROR << "Error: In X509V3_EXT_conf_nidn: " << value;
+ return -1;
+ }
+ X509_add_ext(cert, ex, -1);
+ X509_EXTENSION_free(ex);
+ return 0;
+}
+
+inline void generateSslCertificate(const std::string& filepath,
+ const std::string& cn)
{
FILE* pFile = nullptr;
std::cout << "Generating new keys\n";
@@ -189,8 +239,10 @@
// get a random number from the RNG for the certificate serial
// number If this is not random, regenerating certs throws broswer
// errors
- std::random_device rd;
- int serial = static_cast<int>(rd());
+ bmcweb::OpenSSLGenerator gen;
+ std::uniform_int_distribution<int> dis(
+ 1, std::numeric_limits<int>::max());
+ int serial = dis(gen);
ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
@@ -215,10 +267,19 @@
reinterpret_cast<const unsigned char*>("OpenBMC"), -1, -1, 0);
X509_NAME_add_entry_by_txt(
name, "CN", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("testhost"), -1, -1, 0);
+ reinterpret_cast<const unsigned char*>(cn.c_str()), -1, -1, 0);
// set the CSR options
X509_set_issuer_name(x509, name);
+ X509_set_version(x509, 2);
+ addExt(x509, NID_basic_constraints, ("critical,CA:TRUE"));
+ addExt(x509, NID_subject_alt_name, ("DNS:" + cn).c_str());
+ addExt(x509, NID_subject_key_identifier, ("hash"));
+ addExt(x509, NID_authority_key_identifier, ("keyid"));
+ addExt(x509, NID_key_usage, ("digitalSignature, keyEncipherment"));
+ addExt(x509, NID_ext_key_usage, ("serverAuth"));
+ addExt(x509, NID_netscape_comment, (x509Comment));
+
// Sign the certificate with our private key
X509_sign(x509, pPrivKey, EVP_sha256());
@@ -289,7 +350,7 @@
if (!pemFileValid)
{
std::cerr << "Error in verifying signature, regenerating\n";
- generateSslCertificate(filepath);
+ generateSslCertificate(filepath, "testhost");
}
}