Replace PyOpenSSL by cryptography
Using The crypto module of PyOpenSSL is discouraged now in favor of the cryptography library[1]. [1] https://www.pyopenssl.org/en/latest/api/crypto.html pyca/cryptography is likely a better choice than using this module. It contains a complete set of cryptographic primitives as well as a significantly better and more powerful X509 API. Change-Id: Ic9c2bf44b6996ccf9645e0f1e52c1bfcbae33b19
This commit is contained in:
parent
8ebec483df
commit
1158d5e1f7
@ -11,11 +11,16 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import yaml
|
||||
|
||||
from OpenSSL import crypto
|
||||
from time import time
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography import x509
|
||||
from cryptography.x509 import oid
|
||||
|
||||
from packstack.installer import basedefs
|
||||
from packstack.installer import utils
|
||||
from packstack.installer.setup_controller import Controller
|
||||
@ -93,64 +98,97 @@ def generateHieraDataFile():
|
||||
|
||||
def generate_ssl_cert(config, host, service, ssl_key_file, ssl_cert_file):
|
||||
"""
|
||||
Wrapper on top of openssl
|
||||
Generate SSL certificate
|
||||
"""
|
||||
# We have to check whether the certificate already exists
|
||||
cert_dir = os.path.join(config['CONFIG_SSL_CERT_DIR'], 'certs')
|
||||
local_cert_name = host + os.path.basename(ssl_cert_file)
|
||||
local_cert_path = os.path.join(cert_dir, local_cert_name)
|
||||
if not os.path.exists(local_cert_path):
|
||||
ca_file = open(config['CONFIG_SSL_CACERT_FILE'], 'rt').read()
|
||||
ca_key_file = open(config['CONFIG_SSL_CACERT_KEY_FILE'], 'rt').read()
|
||||
ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, ca_key_file)
|
||||
ca = crypto.load_certificate(crypto.FILETYPE_PEM, ca_file)
|
||||
if os.path.exists(local_cert_path):
|
||||
return
|
||||
|
||||
k = crypto.PKey()
|
||||
k.generate_key(crypto.TYPE_RSA, 4096)
|
||||
mail = config['CONFIG_SSL_CERT_SUBJECT_MAIL']
|
||||
hostinfo = config['HOST_DETAILS'][host]
|
||||
fqdn = hostinfo['fqdn']
|
||||
cert = crypto.X509()
|
||||
subject = cert.get_subject()
|
||||
subject.C = config['CONFIG_SSL_CERT_SUBJECT_C']
|
||||
subject.ST = config['CONFIG_SSL_CERT_SUBJECT_ST']
|
||||
subject.L = config['CONFIG_SSL_CERT_SUBJECT_L']
|
||||
subject.O = config['CONFIG_SSL_CERT_SUBJECT_O'] # noqa: E741
|
||||
subject.OU = config['CONFIG_SSL_CERT_SUBJECT_OU']
|
||||
cn = "%s/%s" % (service, fqdn)
|
||||
# if subject.CN is more than 64 chars long, cert creation will fail
|
||||
if len(cn) > 64:
|
||||
cn = cn[0:63]
|
||||
subject.CN = cn
|
||||
subject.emailAddress = mail
|
||||
with open(config['CONFIG_SSL_CACERT_FILE'], 'rb') as cacert_file:
|
||||
ca_cert = x509.load_pem_x509_certificate(cacert_file.read())
|
||||
|
||||
cert.add_extensions([
|
||||
crypto.X509Extension(
|
||||
"keyUsage".encode('ascii'),
|
||||
False,
|
||||
"nonRepudiation,digitalSignature,keyEncipherment".encode('ascii')),
|
||||
crypto.X509Extension(
|
||||
"extendedKeyUsage".encode('ascii'),
|
||||
False,
|
||||
"clientAuth,serverAuth".encode('ascii')),
|
||||
])
|
||||
with open(config['CONFIG_SSL_CACERT_KEY_FILE'], 'rb') as ca_key_file:
|
||||
ca_key = serialization.load_pem_private_key(ca_key_file.read(),
|
||||
password=None)
|
||||
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(315360000)
|
||||
cert.set_issuer(ca.get_subject())
|
||||
cert.set_pubkey(k)
|
||||
serial = int(time())
|
||||
cert.set_serial_number(serial)
|
||||
cert.sign(ca_key, 'sha256')
|
||||
# create a key pair
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=4096,
|
||||
)
|
||||
|
||||
final_cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
|
||||
final_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k)
|
||||
deliver_ssl_file(ca_file, config['CONFIG_SSL_CACERT'], host)
|
||||
deliver_ssl_file(final_cert.decode(), ssl_cert_file, host)
|
||||
deliver_ssl_file(final_key.decode(), ssl_key_file, host)
|
||||
hostinfo = config['HOST_DETAILS'][host]
|
||||
fqdn = hostinfo['fqdn']
|
||||
cn = "%s/%s" % (service, fqdn)
|
||||
# if subject.CN is more than 64 chars long, cert creation will fail
|
||||
if len(cn) > 64:
|
||||
cn = cn[0:63]
|
||||
|
||||
with open(local_cert_path, 'w') as f:
|
||||
f.write(final_cert.decode())
|
||||
subject = x509.Name([
|
||||
x509.NameAttribute(oid.NameOID.COUNTRY_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_C']),
|
||||
x509.NameAttribute(oid.NameOID.STATE_OR_PROVINCE_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_ST']),
|
||||
x509.NameAttribute(oid.NameOID.LOCALITY_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_L']),
|
||||
x509.NameAttribute(oid.NameOID. ORGANIZATION_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_O']),
|
||||
x509.NameAttribute(oid.NameOID. ORGANIZATIONAL_UNIT_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_OU']),
|
||||
x509.NameAttribute(oid.NameOID.COMMON_NAME, cn),
|
||||
x509.NameAttribute(oid.NameOID.EMAIL_ADDRESS,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_MAIL']),
|
||||
])
|
||||
cert = x509.CertificateBuilder().subject_name(
|
||||
subject
|
||||
).issuer_name(
|
||||
ca_cert.subject
|
||||
).public_key(
|
||||
key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime.datetime.now(datetime.timezone.utc)
|
||||
).not_valid_after(
|
||||
datetime.datetime.now(datetime.timezone.utc) +
|
||||
datetime.timedelta(days=3650)
|
||||
).add_extension(
|
||||
x509.KeyUsage(
|
||||
digital_signature=True,
|
||||
content_commitment=True,
|
||||
key_encipherment=True,
|
||||
data_encipherment=False,
|
||||
key_agreement=False,
|
||||
key_cert_sign=False,
|
||||
crl_sign=False,
|
||||
encipher_only=False,
|
||||
decipher_only=False,
|
||||
),
|
||||
critical=False,
|
||||
).add_extension(
|
||||
x509.ExtendedKeyUsage([
|
||||
x509.ExtendedKeyUsageOID.CLIENT_AUTH,
|
||||
x509.ExtendedKeyUsageOID.SERVER_AUTH,
|
||||
]),
|
||||
critical=False,
|
||||
).sign(ca_key, hashes.SHA256())
|
||||
|
||||
final_cacert = ca_cert.public_bytes(serialization.Encoding.PEM)
|
||||
final_cert = cert.public_bytes(serialization.Encoding.PEM)
|
||||
final_key = key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
)
|
||||
deliver_ssl_file(final_cacert.decode(), config['CONFIG_SSL_CACERT'], host)
|
||||
deliver_ssl_file(final_cert.decode(), ssl_cert_file, host)
|
||||
deliver_ssl_file(final_key.decode(), ssl_key_file, host)
|
||||
|
||||
with open(local_cert_path, 'wb') as f:
|
||||
f.write(final_cert)
|
||||
|
||||
|
||||
def deliver_ssl_file(content, path, host):
|
||||
|
@ -164,9 +164,9 @@ def create_manifest(config, messages):
|
||||
raise exceptions.ParamValidationError(
|
||||
"The file %s doesn't exist" % ssl_chain_file)
|
||||
|
||||
final_cacert = open(ssl_chain_file, 'rt').read()
|
||||
final_cert = open(ssl_cert_file, 'rt').read()
|
||||
final_key = open(ssl_key_file, 'rt').read()
|
||||
final_cacert = open(ssl_chain_file, 'rt').read()
|
||||
host = config['CONFIG_CONTROLLER_HOST']
|
||||
deliver_ssl_file(final_cacert, ssl_chain_file, host)
|
||||
deliver_ssl_file(final_cert, ssl_cert_file, host)
|
||||
|
@ -15,11 +15,16 @@
|
||||
"""
|
||||
Plugin responsible for managing SSL options
|
||||
"""
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from OpenSSL import crypto
|
||||
from socket import gethostname
|
||||
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography import x509
|
||||
from cryptography.x509 import oid
|
||||
|
||||
from packstack.installer import basedefs
|
||||
from packstack.installer import utils
|
||||
from packstack.installer import validators
|
||||
@ -207,7 +212,7 @@ def initSequences(controller):
|
||||
|
||||
def create_self_signed_cert(config, messages):
|
||||
"""
|
||||
OpenSSL wrapper to create selfsigned CA.
|
||||
Generate selfsigned CA.
|
||||
"""
|
||||
|
||||
# for now hardcoded place for landing CACert file on servers
|
||||
@ -239,55 +244,82 @@ def create_self_signed_cert(config, messages):
|
||||
KEY_FILE = config['CONFIG_SSL_CACERT_KEY_FILE'] = (
|
||||
'%s/keys/selfcert.crt' % config['CONFIG_SSL_CERT_DIR']
|
||||
)
|
||||
if not os.path.exists(CERT_FILE) or not os.path.exists(KEY_FILE):
|
||||
# create a key pair
|
||||
k = crypto.PKey()
|
||||
k.generate_key(crypto.TYPE_RSA, 4096)
|
||||
|
||||
# create a self-signed cert
|
||||
mail = config['CONFIG_SSL_CERT_SUBJECT_MAIL']
|
||||
cert = crypto.X509()
|
||||
subject = cert.get_subject()
|
||||
subject.C = config['CONFIG_SSL_CERT_SUBJECT_C']
|
||||
subject.ST = config['CONFIG_SSL_CERT_SUBJECT_ST']
|
||||
subject.L = config['CONFIG_SSL_CERT_SUBJECT_L']
|
||||
subject.O = config['CONFIG_SSL_CERT_SUBJECT_O'] # noqa: E741
|
||||
subject.OU = config['CONFIG_SSL_CERT_SUBJECT_OU']
|
||||
subject.CN = config['CONFIG_SSL_CERT_SUBJECT_CN']
|
||||
subject.emailAddress = mail
|
||||
cert.set_serial_number(1000)
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
|
||||
cert.set_issuer(cert.get_subject())
|
||||
cert.set_pubkey(k)
|
||||
if os.path.exists(CERT_FILE) and os.path.exists(KEY_FILE):
|
||||
return
|
||||
|
||||
# CA extensions
|
||||
cert.add_extensions([
|
||||
crypto.X509Extension("basicConstraints".encode('ascii'), False,
|
||||
"CA:TRUE".encode('ascii')),
|
||||
crypto.X509Extension("keyUsage".encode('ascii'), False,
|
||||
"keyCertSign, cRLSign".encode('ascii')),
|
||||
crypto.X509Extension("subjectKeyIdentifier".encode('ascii'), False,
|
||||
"hash".encode('ascii'),
|
||||
subject=cert),
|
||||
])
|
||||
# create a key pair
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=4096,
|
||||
)
|
||||
|
||||
cert.add_extensions([
|
||||
crypto.X509Extension(
|
||||
"authorityKeyIdentifier".encode('ascii'), False,
|
||||
"keyid:always".encode('ascii'), issuer=cert)
|
||||
])
|
||||
subject = issuer = x509.Name([
|
||||
x509.NameAttribute(oid.NameOID.COUNTRY_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_C']),
|
||||
x509.NameAttribute(oid.NameOID.STATE_OR_PROVINCE_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_ST']),
|
||||
x509.NameAttribute(oid.NameOID.LOCALITY_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_L']),
|
||||
x509.NameAttribute(oid.NameOID. ORGANIZATION_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_O']),
|
||||
x509.NameAttribute(oid.NameOID. ORGANIZATIONAL_UNIT_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_OU']),
|
||||
x509.NameAttribute(oid.NameOID.COMMON_NAME,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_CN']),
|
||||
x509.NameAttribute(oid.NameOID.EMAIL_ADDRESS,
|
||||
config['CONFIG_SSL_CERT_SUBJECT_MAIL']),
|
||||
])
|
||||
cert = x509.CertificateBuilder().subject_name(
|
||||
subject
|
||||
).issuer_name(
|
||||
issuer
|
||||
).public_key(
|
||||
key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime.datetime.now(datetime.timezone.utc)
|
||||
).not_valid_after(
|
||||
datetime.datetime.now(datetime.timezone.utc) +
|
||||
datetime.timedelta(days=3650)
|
||||
).add_extension(
|
||||
x509.BasicConstraints(ca=True, path_length=None),
|
||||
critical=False,
|
||||
).add_extension(
|
||||
x509.KeyUsage(
|
||||
digital_signature=False,
|
||||
content_commitment=False,
|
||||
key_encipherment=False,
|
||||
data_encipherment=False,
|
||||
key_agreement=False,
|
||||
key_cert_sign=True,
|
||||
crl_sign=True,
|
||||
encipher_only=False,
|
||||
decipher_only=False,
|
||||
),
|
||||
critical=False,
|
||||
).add_extension(
|
||||
x509.SubjectKeyIdentifier.from_public_key(key.public_key()),
|
||||
critical=False,
|
||||
).add_extension(
|
||||
x509.AuthorityKeyIdentifier.from_issuer_public_key(key.public_key()),
|
||||
critical=False,
|
||||
).sign(key, hashes.SHA256())
|
||||
|
||||
cert.sign(k, 'sha256')
|
||||
with open(CERT_FILE, 'wb') as certfile:
|
||||
certfile.write(cert.public_bytes(serialization.Encoding.PEM))
|
||||
|
||||
open((CERT_FILE), "w").write(
|
||||
crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode())
|
||||
open((KEY_FILE), "w").write(
|
||||
crypto.dump_privatekey(crypto.FILETYPE_PEM, k).decode())
|
||||
with open(KEY_FILE, 'wb') as keyfile:
|
||||
keyfile.write(key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
))
|
||||
|
||||
messages.append(
|
||||
"%sNOTE%s : A selfsigned CA certificate was generated to be used "
|
||||
"for ssl, you should still change it do subordinate CA cert. In "
|
||||
"any case please save the contents of %s."
|
||||
% (utils.COLORS['red'], utils.COLORS['nocolor'],
|
||||
config['CONFIG_SSL_CERT_DIR']))
|
||||
messages.append(
|
||||
"%sNOTE%s : A selfsigned CA certificate was generated to be used "
|
||||
"for ssl, you should still change it do subordinate CA cert. In "
|
||||
"any case please save the contents of %s."
|
||||
% (utils.COLORS['red'], utils.COLORS['nocolor'],
|
||||
config['CONFIG_SSL_CERT_DIR']))
|
||||
|
@ -2,6 +2,6 @@ pbr>=1.6 # Apache-2.0
|
||||
netaddr>=0.7.6
|
||||
PyYAML>=3.10
|
||||
docutils>=0.11
|
||||
pyOpenSSL>=16.2.0
|
||||
netifaces
|
||||
distro
|
||||
cryptography>=2.1 # BSD/Apache-2.0
|
||||
|
Loading…
Reference in New Issue
Block a user