add passphrase to execution module

This commit is contained in:
Clint Armstrong 2017-01-10 08:59:16 -05:00
parent a4d6598f1e
commit e47a93d496

View file

@ -74,11 +74,6 @@ CERT_DEFAULTS = {
'serial_bits': 64,
'algorithm': 'sha256'
}
PEM_RE = re.compile(
r"\s*(?P<pem_header>-----(?:.+?)-----)\s+"
r"(?P<pem_body>.+?)\s+(?P<pem_footer>-----(?:.+?)-----)\s*",
re.DOTALL
)
def __virtual__():
@ -353,18 +348,27 @@ def _get_certificate_obj(cert):
return M2Crypto.X509.load_cert_string(text)
def _get_private_key_obj(private_key):
def _get_private_key_obj(private_key, passphrase=None):
'''
Returns a private key object based on PEM text.
'''
private_key = _text_or_file(private_key)
private_key = get_pem_entry(private_key, pem_type='RSA PRIVATE KEY')
rsaprivkey = M2Crypto.RSA.load_key_string(private_key)
rsaprivkey = M2Crypto.RSA.load_key_string(private_key, callback=_passphrase_callback(passphrase))
evpprivkey = M2Crypto.EVP.PKey()
evpprivkey.assign_rsa(rsaprivkey)
return evpprivkey
def _passphrase_callback(passphrase):
'''
Returns a callback function used to supply a passphrase for private keys
'''
def f(*args):
return passphrase
return f
def _get_request_obj(csr):
'''
Returns a CSR object based on PEM text.
@ -397,6 +401,8 @@ def _make_regex(pem_type):
'''
return re.compile(
r"\s*(?P<pem_header>-----BEGIN {0}-----)\s+"
r"(?:(?P<proc_type>Proc-Type: 4,ENCRYPTED)\s*)?"
r"(?:(?P<dek_info>DEK-Info: (?:DES-[3A-Z\-]+,[0-9A-F]{{16}}|[0-9A-Z\-]+,[0-9A-F]{{32}}))\s*)?"
r"(?P<pem_body>.+?)\s+(?P<pem_footer>"
r"-----END {1}-----)\s*".format(pem_type, pem_type),
re.DOTALL
@ -424,6 +430,8 @@ def get_pem_entry(text, pem_type=None):
MIICyzCC Ar8CAQI...-----END CERTIFICATE REQUEST"
'''
text = _text_or_file(text)
# Replace encoded newlines
text = text.replace('\\n', '\n')
_match = None
@ -446,39 +454,34 @@ def get_pem_entry(text, pem_type=None):
pem_temp = pem_temp[pem_temp.index('-'):]
text = "\n".join(pem_fixed)
if not pem_type:
# Find using a regex iterator, pick the first match
for _match in PEM_RE.finditer(text):
if _match:
break
if not _match:
raise salt.exceptions.SaltInvocationError(
'PEM text not valid:\n{0}'.format(text)
)
_match_dict = _match.groupdict()
pem_header = _match_dict['pem_header']
pem_footer = _match_dict['pem_footer']
pem_body = _match_dict['pem_body']
else:
_dregex = _make_regex('[0-9A-Z ]+')
errmsg = 'PEM text not valid:\n{0}'.format(text)
if pem_type:
_dregex = _make_regex(pem_type)
for _match in _dregex.finditer(text):
if _match:
break
if not _match:
raise salt.exceptions.SaltInvocationError(
'PEM does not contain a single entry of type {0}:\n'
'{1}'.format(pem_type, text)
)
_match_dict = _match.groupdict()
pem_header = _match_dict['pem_header']
pem_footer = _match_dict['pem_footer']
pem_body = _match_dict['pem_body']
errmsg = ('PEM does not contain a single entry of type {0}:\n'
'{1}'.format(pem_type, text))
for _match in _dregex.finditer(text):
if _match:
break
if not _match:
raise salt.exceptions.SaltInvocationError(errmsg)
_match_dict = _match.groupdict()
pem_header = _match_dict['pem_header']
proc_type = _match_dict['proc_type']
dek_info = _match_dict['dek_info']
pem_footer = _match_dict['pem_footer']
pem_body = _match_dict['pem_body']
# Remove all whitespace from body
pem_body = ''.join(pem_body.split())
# Generate correctly formatted pem
ret = pem_header + '\n'
if proc_type:
ret += proc_type + '\n'
if dek_info:
ret += dek_info + '\n' + '\n'
for i in range(0, len(pem_body), 64):
ret += pem_body[i:i + 64] + '\n'
ret += pem_footer + '\n'
@ -649,7 +652,7 @@ def read_crl(crl):
return crlparsed
def get_public_key(key, asObj=False):
def get_public_key(key, passphrase=None, asObj=False):
'''
Returns a string containing the public key in PEM format.
@ -687,7 +690,7 @@ def get_public_key(key, asObj=False):
rsa = csr.get_pubkey().get_rsa()
if (text.startswith('-----BEGIN PRIVATE KEY-----') or
text.startswith('-----BEGIN RSA PRIVATE KEY-----')):
rsa = M2Crypto.RSA.load_key_string(text)
rsa = M2Crypto.RSA.load_key_string(text, callback=_passphrase_callback(passphrase))
if asObj:
evppubkey = M2Crypto.EVP.PKey()
@ -698,7 +701,7 @@ def get_public_key(key, asObj=False):
return bio.read_all()
def get_private_key_size(private_key):
def get_private_key_size(private_key, passphrase=None):
'''
Returns the bit length of a private key in PEM format.
@ -711,7 +714,7 @@ def get_private_key_size(private_key):
salt '*' x509.get_private_key_size /etc/pki/mycert.key
'''
return _get_private_key_obj(private_key).size() * 8
return _get_private_key_obj(private_key, passphrase).size() * 8
def write_pem(text, path, overwrite=True, pem_type=None):
@ -768,7 +771,12 @@ def write_pem(text, path, overwrite=True, pem_type=None):
return 'PEM written to {0}'.format(path)
def create_private_key(path=None, text=False, bits=2048, verbose=True):
def create_private_key(path=None,
text=False,
bits=2048,
passphrase=None,
cipher='aes_128_cbc',
verbose=True):
'''
Creates a private key in PEM format.
@ -783,6 +791,12 @@ def create_private_key(path=None, text=False, bits=2048, verbose=True):
bits:
Length of the private key in bits. Default 2048
passphrase:
Passphrase for encryting the private key
cipher:
Cipher for encrypting the private key. Has no effect if passhprase is None.
verbose:
Provide visual feedback on stdout. Default True
@ -810,7 +824,9 @@ def create_private_key(path=None, text=False, bits=2048, verbose=True):
rsa = M2Crypto.RSA.gen_key(bits, M2Crypto.m2.RSA_F4, _callback_func)
# pylint: enable=no-member
bio = M2Crypto.BIO.MemoryBuffer()
rsa.save_key_bio(bio, cipher=None)
if passphrase is None:
cipher=None
rsa.save_key_bio(bio, cipher=cipher, callback=_passphrase_callback(passphrase))
if path:
return write_pem(
@ -1125,6 +1141,9 @@ def create_certificate(
certificate, and the public key matching ``signing_private_key`` will
be used to create the certificate.
signing_private_key_passphrase:
Passphrase used to decrypt the signing_private_key.
signing_cert:
A certificate matching the private key that will be used to sign this
certificate. This is used to populate the issuer values in the
@ -1147,6 +1166,10 @@ def create_certificate(
to the certificate, subject or extension information in the CSR will
be lost.
public_key_passphrase:
If the public key is supplied as a private key, this is the passphrase
used to decrypt it.
csr:
A file or PEM string containing a certificate signing request. This
will be used to supply the subject, extensions and public key of a
@ -1296,6 +1319,8 @@ def create_certificate(
raise salt.exceptions.SaltInvocationError(
'Either path or text must be specified, not both.')
if 'public_key_passphrase' not in kwargs:
kwargs['public_key_passphrase'] = None
if ca_server:
if 'signing_policy' not in kwargs:
raise salt.exceptions.SaltInvocationError(
@ -1309,7 +1334,8 @@ def create_certificate(
if 'public_key' in kwargs:
# Strip newlines to make passing through as cli functions easier
kwargs['public_key'] = get_public_key(
kwargs['public_key']).replace('\n', '')
kwargs['public_key'],
passphrase=kwargs['public_key_passphrase']).replace('\n', '')
# Remove system entries in kwargs
# Including listen_in and preqreuired because they are not included
@ -1380,7 +1406,8 @@ def create_certificate(
cert.set_subject(csr.get_subject())
csrexts = read_csr(kwargs['csr'])['X509v3 Extensions']
cert.set_pubkey(get_public_key(kwargs['public_key'], asObj=True))
cert.set_pubkey(get_public_key(kwargs['public_key'],
passphrase=kwargs['public_key_passphrase'], asObj=True))
subject = cert.get_subject()
@ -1429,13 +1456,18 @@ def create_certificate(
cert.add_ext(ext)
if 'signing_private_key_passphrase' not in kwargs:
kwargs['signing_private_key_passphrase'] = None
if 'testrun' in kwargs and kwargs['testrun'] is True:
cert_props = read_certificate(cert)
cert_props['Issuer Public Key'] = get_public_key(
kwargs['signing_private_key'])
kwargs['signing_private_key'],
passphrase=kwargs['signing_private_key_passphrase'])
return cert_props
if not verify_private_key(kwargs['signing_private_key'], signing_cert):
if not verify_private_key(private_key=kwargs['signing_private_key'],
passphrase=kwargs['signing_private_key_passphrase'],
public_key=signing_cert):
raise salt.exceptions.SaltInvocationError(
'signing_private_key: {0} '
'does no match signing_cert: {1}'.format(
@ -1445,7 +1477,8 @@ def create_certificate(
)
cert.sign(
_get_private_key_obj(kwargs['signing_private_key']),
_get_private_key_obj(kwargs['signing_private_key'],
passphrase=kwargs['signing_private_key_passphrase']),
kwargs['algorithm']
)
@ -1527,7 +1560,10 @@ def create_csr(path=None, text=False, **kwargs):
if 'public_key' not in kwargs:
kwargs['public_key'] = kwargs['private_key']
csr.set_pubkey(get_public_key(kwargs['public_key'], asObj=True))
if 'public_key_passphrase' not in kwargs:
kwargs['public_key_passphrase'] = None
csr.set_pubkey(get_public_key(kwargs['public_key'],
passphrase=kwargs['public_key_passphrase'], asObj=True))
# pylint: disable=unused-variable
for entry, num in six.iteritems(subject.nid):
@ -1565,7 +1601,8 @@ def create_csr(path=None, text=False, **kwargs):
csr.add_extensions(extstack)
csr.sign(_get_private_key_obj(kwargs['private_key']), kwargs['algorithm'])
csr.sign(_get_private_key_obj(kwargs['private_key'],
passphrase=kwargs['public_key_passphrase']), kwargs['algorithm'])
if path:
return write_pem(
@ -1577,7 +1614,7 @@ def create_csr(path=None, text=False, **kwargs):
return csr.as_pem()
def verify_private_key(private_key, public_key):
def verify_private_key(private_key, public_key, passphrase=None):
'''
Verify that 'private_key' matches 'public_key'
@ -1589,6 +1626,9 @@ def verify_private_key(private_key, public_key):
The public key to verify, can be a string or path to a PEM formatted
certificate, csr, or another private key.
passphrase:
Passphrase to decrypt the private key.
CLI Example:
.. code-block:: bash
@ -1596,10 +1636,10 @@ def verify_private_key(private_key, public_key):
salt '*' x509.verify_private_key private_key=/etc/pki/myca.key \\
public_key=/etc/pki/myca.crt
'''
return bool(get_public_key(private_key) == get_public_key(public_key))
return bool(get_public_key(private_key, passphrase) == get_public_key(public_key))
def verify_signature(certificate, signing_pub_key=None):
def verify_signature(certificate, signing_pub_key=None, signing_pub_key_passphrase=None):
'''
Verify that ``certificate`` has been signed by ``signing_pub_key``
@ -1611,6 +1651,9 @@ def verify_signature(certificate, signing_pub_key=None):
The public key to verify, can be a string or path to a PEM formatted
certificate, csr, or private key.
signing_pub_key_passphrase:
Passphrase to the signing_pub_key if it is an encrypted private key.
CLI Example:
.. code-block:: bash
@ -1621,7 +1664,8 @@ def verify_signature(certificate, signing_pub_key=None):
cert = _get_certificate_obj(certificate)
if signing_pub_key:
signing_pub_key = get_public_key(signing_pub_key, asObj=True)
signing_pub_key = get_public_key(signing_pub_key,
passphrase=signing_pub_key_passphrase, asObj=True)
return bool(cert.verify(pkey=signing_pub_key) == 1)