mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
add passphrase to execution module
This commit is contained in:
parent
a4d6598f1e
commit
e47a93d496
1 changed files with 94 additions and 50 deletions
|
@ -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)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue