Making changes to csr and https certificate

This commit will support creation of https certificate.

Change-Id: I58989351b24f7cc2520caff5351ab8a6e553554c
This commit is contained in:
ankit 2021-08-25 10:36:45 +00:00 committed by Nisha Agarwal
parent ed00e9ee2e
commit 084b0acb8b
6 changed files with 186 additions and 26 deletions

View File

@ -1,3 +1,4 @@
# Copyright 2022 Hewlett Packard Enterprise Development LP
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -232,3 +233,14 @@ class MissingAttributeError(RedfishError):
class InvalidParameterValueError(RedfishError):
message = ('The parameter "%(parameter)s" value "%(value)s" is invalid. '
'Valid values are: %(valid_values)s')
class CertificateCreationError(ProliantUtilsException):
message = ("Failed to create HTTPS certificate"
"reason: %(reason)s")
def __init__(self, message=None, **kwargs):
if not message:
message = self.message % kwargs
super(CertificateCreationError, self).__init__(message)

View File

@ -1,4 +1,4 @@
# Copyright 2018 Hewlett-Packard Development Company, L.P.
# Copyright 2018-2022 Hewlett Packard Enterprise Development LP
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -134,7 +134,8 @@ SUPPORTED_REDFISH_METHODS = [
'update_authentication_failure_logging',
'update_secure_boot',
'create_csr',
'add_https_certificate'
'add_https_certificate',
'add_ssl_certificate'
]
LOG = log.get_logger(__name__)
@ -1033,6 +1034,7 @@ class IloClient(operations.IloOperations):
"""
return self._call_method('update_secure_boot', enable, ignore)
# This method is deprecated, and will be removed in future release.
def create_csr(self, path, csr_params):
"""Creates the Certificate Signing Request.
@ -1043,6 +1045,7 @@ class IloClient(operations.IloOperations):
"""
return self._call_method('create_csr', path, csr_params)
# This method is deprecated, and will be removed in future release.
def add_https_certificate(self, cert_file):
"""Adds the signed https certificate to the iLO.
@ -1050,3 +1053,21 @@ class IloClient(operations.IloOperations):
:raises: IloError, on an error from iLO.
"""
return self._call_method('add_https_certificate', cert_file)
def add_ssl_certificate(self, csr_params, signed_cert,
private_key, pass_phrase):
"""Creates CSR and adds the signed SSL certificate to the iLO.
:param csr_params: A dictionary containing all the necessary
information required to create CSR.
:param signed_cert: Signed certificate which will be used
to sign the created CSR.
:param private_key: private key.
:param pass_phrase: Pass phrase for the private key.
:raises: IloError, on an error from iLO.
"""
return self._call_method('add_ssl_certificate',
csr_params=csr_params,
signed_cert=signed_cert,
private_key=private_key,
pass_phrase=pass_phrase)

View File

@ -1,3 +1,4 @@
# Copyright 2022 Hewlett Packard Enterprise Development LP
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -568,3 +569,39 @@ class IloOperations(object):
not supported on the server.
"""
raise exception.IloCommandNotSupportedError(ERRMSG)
# This method is deprecated, and will be removed in future release.
def create_csr(self, path, csr_params):
"""Creates the Certificate Signing Request.
:param path: directory to store csr file.
:param csr_params: A dictionary containing all the necessary
information required to create CSR.
:raises: IloError, on an error from iLO.
"""
raise exception.IloCommandNotSupportedError(ERRMSG)
# This method is deprecated, and will be removed in future release.
def add_https_certificate(self, cert_file):
"""Adds the signed https certificate to the iLO.
:param cert_file: Signed HTTPS certificate file.
:raises: IloError, on an error from iLO.
"""
raise exception.IloCommandNotSupportedError(ERRMSG)
def add_ssl_certificate(self, csr_params, signed_cert,
private_key, pass_phrase):
"""Creates CSR and adds the signed SSL certificate to the iLO.
:param csr_params: A dictionary containing all the necessary
information required to create CSR.
:param signed_cert: Signed certificate which will be used
to sign the created CSR.
:param private_key: private key.
:param pass_phrase: Pass phrase for the private key.
:raises: IloError, on an error from iLO.
:raises: IloCommandNotSupportedError, if the command is
not supported on the server.
"""
raise exception.IloCommandNotSupportedError(ERRMSG)

View File

@ -16,9 +16,8 @@ __author__ = 'HPE'
from base64 import b64decode
import json
import os
import re
import shutil
import subprocess
import tempfile
from OpenSSL.crypto import FILETYPE_ASN1
@ -891,10 +890,9 @@ class RedfishOperations(operations.IloOperations):
LOG.debug(msg)
raise exception.IloError(msg)
def create_csr(self, path, csr_params):
def create_csr(self, csr_params):
"""Creates the Certificate Signing Request.
:param path: directory to store csr file.
:param csr_params: A dictionary containing all the necessary
information required to create CSR.
:raises: IloError, on an error from iLO.
@ -905,17 +903,12 @@ class RedfishOperations(operations.IloOperations):
sushy_man.securityservice.https_certificate_uri.generate_csr(
csr_params))
dir = os.path.join(path, 'cert')
if not os.path.exists(dir):
os.makedirs(dir, 0o755)
(fd, temp_file) = tempfile.mkstemp(suffix='.csr')
with open(temp_file, 'w') as f:
f.write(cert_request)
shutil.copy(temp_file, dir)
return temp_file
except sushy.exceptions.SushyError as e:
msg = (self._('The Redfish controller failed to create the '
'certificate signing request. '
@ -923,6 +916,71 @@ class RedfishOperations(operations.IloOperations):
LOG.debug(msg)
raise exception.IloError(msg)
def add_ssl_certificate(self, csr_params, signed_cert,
private_key, pass_phrase):
"""Creates CSR and adds the signed SSL certificate to the iLO.
:param csr_params: A dictionary containing all the necessary
information required to create CSR.
:param signed_cert: signed certificate which will be used
to sign the created CSR.
:param private_key: private key.
:param pass_phrase: Pass phrase for the private key.
:raises: IloError, on an error from iLO.
"""
csr_file = self.create_csr(csr_params)
(fd, temp_file) = tempfile.mkstemp(suffix='.ext')
(fd, https_cert_file) = tempfile.mkstemp(suffix='.crt')
(fd, ss_cert_file) = tempfile.mkstemp(suffix='.crt')
with open(signed_cert, 'r') as f:
data = json.dumps(f.read())
p = re.sub(r"\"", "", data)
q = re.sub(r"\\n", "\r\n", p).rstrip()
c_list = re.findall(_CERTIFICATE_PATTERN, q, re.DOTALL)
if len(c_list) == 0:
msg = (self._("No valid certificate in %(cert_file)s.") %
{"cert_file": signed_cert})
LOG.debug(msg)
raise exception.InvalidParameterValueError(msg)
ss_cert = c_list[0]
with open(ss_cert_file, 'w') as f:
f.write(ss_cert)
content = [
"authorityKeyIdentifier = keyid,issuer\n",
"basicConstraints = CA:true,pathlen:1\n",
"keyUsage = digitalSignature,keyEncipherment,keyCertSign,cRLSign",
"\nextendedKeyUsage = clientAuth,serverAuth\n",
"subjectKeyIdentifier = hash"]
with open(temp_file, 'w') as f:
f.writelines(content)
cert_cmd = (
"openssl x509 -req -days 365 -in %(csr_file)s -extfile"
" %(tempfile)s -CA %(cert)s -CAkey %(p_key)s -passin "
" pass:%(pphrase)s -CAcreateserial -out %(cert_file)s"
% {'csr_file': csr_file, 'tempfile': temp_file,
'cert': ss_cert_file, 'p_key': private_key,
'pphrase': pass_phrase, 'cert_file': https_cert_file})
try:
process = subprocess.Popen(cert_cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
out, err = process.communicate()
except Exception as e:
msg = (self._("Failed to create HTTPS certificate. "
"error: %(err)s") % {"err": e})
LOG.debug(msg)
raise exception.CertificateCreationError(msg)
self.add_https_certificate(https_cert_file)
def add_https_certificate(self, cert_file):
"""Adds the signed https certificate to the iLO.

View File

@ -1,3 +1,4 @@
# Copyright 2022 Hewlett Packard Enterprise Development LP
# Copyright 2015 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
@ -1453,6 +1454,6 @@ class IloRedfishClientTestCase(testtools.TestCase):
self.assertEqual('set_iscsi_info',
even_more_missed_operations[0])
else:
self.assertEqual(2, len(even_more_missed_operations))
self.assertEqual(len(client.SUPPORTED_REDFISH_METHODS) - 2,
self.assertEqual(3, len(even_more_missed_operations))
self.assertEqual(len(client.SUPPORTED_REDFISH_METHODS) - 3,
validate_method_calls.no_test_cases)

View File

@ -18,7 +18,6 @@ import builtins
import collections
import io
import json
import os
from unittest import mock
import ddt
@ -2525,9 +2524,7 @@ class RedfishOperationsTestCase(testtools.TestCase):
self.rf_client.update_authentication_failure_logging)
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_manager')
@mock.patch.object(os, 'makedirs', autospec=True)
@mock.patch.object(redfish, 'shutil', autospec=True)
def test_create_csr(self, shutil_mock, makedirs_mock, manager_mock):
def test__create_csr(self, manager_mock):
data = {
"CommonName": '1.1.1.1',
"Country": 'IN',
@ -2538,13 +2535,12 @@ class RedfishOperationsTestCase(testtools.TestCase):
}
(manager_mock.return_value.securityservice.https_certificate_uri.
generate_csr.return_value) = 'certificate'
self.rf_client.create_csr('/httproot', data)
self.rf_client.create_csr(data)
(manager_mock.return_value.securityservice.https_certificate_uri.
generate_csr.assert_called_once_with(data))
makedirs_mock.assert_called_once_with('/httproot/cert', 0o755)
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_manager')
def test_create_csr_fail(self, manager_mock):
def test__create_csr_fail(self, manager_mock):
data = {
"CommonName": '1.1.1.1',
"Country": 'IN',
@ -2559,12 +2555,47 @@ class RedfishOperationsTestCase(testtools.TestCase):
msg = ("The Redfish controller failed to create the "
"certificate signing request. ")
self.assertRaisesRegex(
exception.IloError, msg, self.rf_client.create_csr,
'/httproot', data)
exception.IloError, msg, self.rf_client.create_csr, data)
@mock.patch.object(redfish.RedfishOperations, 'create_csr')
@mock.patch.object(redfish.RedfishOperations, 'add_https_certificate')
@mock.patch.object(builtins, 'open')
@mock.patch('subprocess.Popen')
def test_add_ssl_certificate(self, subprocess_mock, open_mock,
https_cert_mock, create_mock):
csr_params = {
"CommonName": '1.1.1.1',
"Country": 'IN',
"State": 'KA',
"City": 'blr',
"OrgName": 'HPE',
"OrgUnit": None
}
p_key = '/p_key.key'
create_mock.return_value = '/tmp/csr_file'
data = (
"-----BEGIN CERTIFICATE-----\nMIID7TC\nCF"
"g879\n-----END CERTIFICATE-----\n"
"-----BEGIN CERTIFICATE-----\nKHY8UP\nGH"
"f792\n-----END CERTIFICATE-----\n"
)
fd_mock = mock.MagicMock(spec=io.BytesIO)
open_mock.return_value = fd_mock
fd_mock.__enter__.return_value = fd_mock
fd_mock.read.return_value = data
process_mock = mock.Mock()
attrs = {'communicate.return_value': ('output', 'error')}
process_mock.configure_mock(**attrs)
subprocess_mock.return_value = process_mock
self.rf_client.add_ssl_certificate(csr_params, data, p_key, '1234')
subprocess_mock.assert_called_once()
https_cert_mock.assert_called_once()
create_mock.assert_called_once()
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_manager')
@mock.patch.object(builtins, 'open')
def test_add_https_certificate(self, open_mock, manager_mock):
def test__add_https_certificate(self, open_mock, manager_mock):
data = (
"-----BEGIN CERTIFICATE-----\nMIID7TC\nCF"
"g879\n-----END CERTIFICATE-----\n"
@ -2591,8 +2622,8 @@ class RedfishOperationsTestCase(testtools.TestCase):
@mock.patch.object(redfish.RedfishOperations, '_get_sushy_manager')
@mock.patch.object(builtins, 'open')
def test_add_https_certificate_no_certificate(self, open_mock,
manager_mock):
def test__add_https_certificate_no_certificate(self, open_mock,
manager_mock):
data = (
"-----UNFORMATED CERTIFICATE-----\nMIID7TC\nCF"
"g879\n-----END CERTIFICATE-----\n"