Merge branch 'master' into lemur_vault_plugin
This commit is contained in:
commit
a801112cf6
|
@ -85,7 +85,9 @@ def parse_cert_chain(pem_chain):
|
||||||
:param pem_chain: string
|
:param pem_chain: string
|
||||||
:return: List of parsed certificates
|
:return: List of parsed certificates
|
||||||
"""
|
"""
|
||||||
return [parse_certificate(cert) for cert in split_pem(pem_chain) if pem_chain]
|
if pem_chain is None:
|
||||||
|
return []
|
||||||
|
return [parse_certificate(cert) for cert in split_pem(pem_chain) if cert]
|
||||||
|
|
||||||
|
|
||||||
def parse_csr(csr):
|
def parse_csr(csr):
|
||||||
|
|
|
@ -1,246 +0,0 @@
|
||||||
"""
|
|
||||||
.. module: lemur.plugins.lemur_java.plugin
|
|
||||||
:platform: Unix
|
|
||||||
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
|
|
||||||
:license: Apache, see LICENSE for more details.
|
|
||||||
|
|
||||||
.. moduleauthor:: Kevin Glisson <kglisson@netflix.com>
|
|
||||||
"""
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
from cryptography.fernet import Fernet
|
|
||||||
|
|
||||||
from lemur.utils import mktempfile, mktemppath
|
|
||||||
from lemur.plugins.bases import ExportPlugin
|
|
||||||
from lemur.plugins import lemur_java as java
|
|
||||||
from lemur.common.utils import parse_certificate
|
|
||||||
from lemur.common.defaults import common_name
|
|
||||||
|
|
||||||
|
|
||||||
def run_process(command):
|
|
||||||
"""
|
|
||||||
Runs a given command with pOpen and wraps some
|
|
||||||
error handling around it.
|
|
||||||
:param command:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
stdout, stderr = p.communicate()
|
|
||||||
|
|
||||||
if p.returncode != 0:
|
|
||||||
current_app.logger.debug(" ".join(command))
|
|
||||||
current_app.logger.error(stderr)
|
|
||||||
current_app.logger.error(stdout)
|
|
||||||
raise Exception(stderr)
|
|
||||||
|
|
||||||
|
|
||||||
def split_chain(chain):
|
|
||||||
"""
|
|
||||||
Split the chain into individual certificates for import into keystore
|
|
||||||
|
|
||||||
:param chain:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
certs = []
|
|
||||||
|
|
||||||
if not chain:
|
|
||||||
return certs
|
|
||||||
|
|
||||||
lines = chain.split('\n')
|
|
||||||
|
|
||||||
cert = []
|
|
||||||
for line in lines:
|
|
||||||
cert.append(line + '\n')
|
|
||||||
if line == '-----END CERTIFICATE-----':
|
|
||||||
certs.append("".join(cert))
|
|
||||||
cert = []
|
|
||||||
|
|
||||||
return certs
|
|
||||||
|
|
||||||
|
|
||||||
def create_truststore(cert, chain, jks_tmp, alias, passphrase):
|
|
||||||
assert isinstance(cert, str)
|
|
||||||
assert isinstance(chain, str)
|
|
||||||
|
|
||||||
with mktempfile() as cert_tmp:
|
|
||||||
with open(cert_tmp, 'w') as f:
|
|
||||||
f.write(cert)
|
|
||||||
|
|
||||||
run_process([
|
|
||||||
"keytool",
|
|
||||||
"-importcert",
|
|
||||||
"-file", cert_tmp,
|
|
||||||
"-keystore", jks_tmp,
|
|
||||||
"-alias", "{0}_cert".format(alias),
|
|
||||||
"-storepass", passphrase,
|
|
||||||
"-noprompt"
|
|
||||||
])
|
|
||||||
|
|
||||||
# Import the entire chain
|
|
||||||
for idx, cert in enumerate(split_chain(chain)):
|
|
||||||
with mktempfile() as c_tmp:
|
|
||||||
with open(c_tmp, 'w') as f:
|
|
||||||
f.write(cert)
|
|
||||||
|
|
||||||
# Import signed cert in to JKS keystore
|
|
||||||
run_process([
|
|
||||||
"keytool",
|
|
||||||
"-importcert",
|
|
||||||
"-file", c_tmp,
|
|
||||||
"-keystore", jks_tmp,
|
|
||||||
"-alias", "{0}_cert_{1}".format(alias, idx),
|
|
||||||
"-storepass", passphrase,
|
|
||||||
"-noprompt"
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
def create_keystore(cert, chain, jks_tmp, key, alias, passphrase):
|
|
||||||
assert isinstance(cert, str)
|
|
||||||
assert isinstance(chain, str)
|
|
||||||
assert isinstance(key, str)
|
|
||||||
|
|
||||||
# Create PKCS12 keystore from private key and public certificate
|
|
||||||
with mktempfile() as cert_tmp:
|
|
||||||
with open(cert_tmp, 'w') as f:
|
|
||||||
if chain:
|
|
||||||
f.writelines([key.strip() + "\n", cert.strip() + "\n", chain.strip() + "\n"])
|
|
||||||
else:
|
|
||||||
f.writelines([key.strip() + "\n", cert.strip() + "\n"])
|
|
||||||
|
|
||||||
with mktempfile() as p12_tmp:
|
|
||||||
run_process([
|
|
||||||
"openssl",
|
|
||||||
"pkcs12",
|
|
||||||
"-export",
|
|
||||||
"-nodes",
|
|
||||||
"-name", alias,
|
|
||||||
"-in", cert_tmp,
|
|
||||||
"-out", p12_tmp,
|
|
||||||
"-password", "pass:{}".format(passphrase)
|
|
||||||
])
|
|
||||||
|
|
||||||
# Convert PKCS12 keystore into a JKS keystore
|
|
||||||
run_process([
|
|
||||||
"keytool",
|
|
||||||
"-importkeystore",
|
|
||||||
"-destkeystore", jks_tmp,
|
|
||||||
"-srckeystore", p12_tmp,
|
|
||||||
"-srcstoretype", "pkcs12",
|
|
||||||
"-deststoretype", "JKS",
|
|
||||||
"-alias", alias,
|
|
||||||
"-srcstorepass", passphrase,
|
|
||||||
"-deststorepass", passphrase
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class JavaTruststoreExportPlugin(ExportPlugin):
|
|
||||||
title = 'Java Truststore (JKS)'
|
|
||||||
slug = 'java-truststore-jks'
|
|
||||||
description = 'Attempts to generate a JKS truststore'
|
|
||||||
requires_key = False
|
|
||||||
version = java.VERSION
|
|
||||||
|
|
||||||
author = 'Kevin Glisson'
|
|
||||||
author_url = 'https://github.com/netflix/lemur'
|
|
||||||
|
|
||||||
options = [
|
|
||||||
{
|
|
||||||
'name': 'alias',
|
|
||||||
'type': 'str',
|
|
||||||
'required': False,
|
|
||||||
'helpMessage': 'Enter the alias you wish to use for the truststore.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'passphrase',
|
|
||||||
'type': 'str',
|
|
||||||
'required': False,
|
|
||||||
'helpMessage': 'If no passphrase is given one will be generated for you, we highly recommend this. Minimum length is 8.',
|
|
||||||
'validation': ''
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
def export(self, body, chain, key, options, **kwargs):
|
|
||||||
"""
|
|
||||||
Generates a Java Truststore
|
|
||||||
|
|
||||||
:param key:
|
|
||||||
:param chain:
|
|
||||||
:param body:
|
|
||||||
:param options:
|
|
||||||
:param kwargs:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.get_option('alias', options):
|
|
||||||
alias = self.get_option('alias', options)
|
|
||||||
else:
|
|
||||||
alias = "blah"
|
|
||||||
|
|
||||||
if self.get_option('passphrase', options):
|
|
||||||
passphrase = self.get_option('passphrase', options)
|
|
||||||
else:
|
|
||||||
passphrase = Fernet.generate_key().decode('utf-8')
|
|
||||||
|
|
||||||
with mktemppath() as jks_tmp:
|
|
||||||
create_truststore(body, chain, jks_tmp, alias, passphrase)
|
|
||||||
|
|
||||||
with open(jks_tmp, 'rb') as f:
|
|
||||||
raw = f.read()
|
|
||||||
|
|
||||||
return "jks", passphrase, raw
|
|
||||||
|
|
||||||
|
|
||||||
class JavaKeystoreExportPlugin(ExportPlugin):
|
|
||||||
title = 'Java Keystore (JKS)'
|
|
||||||
slug = 'java-keystore-jks'
|
|
||||||
description = 'Attempts to generate a JKS keystore'
|
|
||||||
version = java.VERSION
|
|
||||||
|
|
||||||
author = 'Kevin Glisson'
|
|
||||||
author_url = 'https://github.com/netflix/lemur'
|
|
||||||
|
|
||||||
options = [
|
|
||||||
{
|
|
||||||
'name': 'passphrase',
|
|
||||||
'type': 'str',
|
|
||||||
'required': False,
|
|
||||||
'helpMessage': 'If no passphrase is given one will be generated for you, we highly recommend this. Minimum length is 8.',
|
|
||||||
'validation': ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'name': 'alias',
|
|
||||||
'type': 'str',
|
|
||||||
'required': False,
|
|
||||||
'helpMessage': 'Enter the alias you wish to use for the keystore.',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
def export(self, body, chain, key, options, **kwargs):
|
|
||||||
"""
|
|
||||||
Generates a Java Keystore
|
|
||||||
|
|
||||||
:param key:
|
|
||||||
:param chain:
|
|
||||||
:param body:
|
|
||||||
:param options:
|
|
||||||
:param kwargs:
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.get_option('passphrase', options):
|
|
||||||
passphrase = self.get_option('passphrase', options)
|
|
||||||
else:
|
|
||||||
passphrase = Fernet.generate_key().decode('utf-8')
|
|
||||||
|
|
||||||
if self.get_option('alias', options):
|
|
||||||
alias = self.get_option('alias', options)
|
|
||||||
else:
|
|
||||||
alias = common_name(parse_certificate(body))
|
|
||||||
|
|
||||||
with mktemppath() as jks_tmp:
|
|
||||||
create_keystore(body, chain, jks_tmp, key, alias, passphrase)
|
|
||||||
|
|
||||||
with open(jks_tmp, 'rb') as f:
|
|
||||||
raw = f.read()
|
|
||||||
|
|
||||||
return "jks", passphrase, raw
|
|
|
@ -1,63 +0,0 @@
|
||||||
import pytest
|
|
||||||
|
|
||||||
from lemur.tests.vectors import INTERNAL_CERTIFICATE_A_STR, INTERNAL_PRIVATE_KEY_A_STR
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="no way of currently testing this")
|
|
||||||
def test_export_truststore(app):
|
|
||||||
from lemur.plugins.base import plugins
|
|
||||||
|
|
||||||
p = plugins.get('java-truststore-jks')
|
|
||||||
options = [{'name': 'passphrase', 'value': 'test1234'}]
|
|
||||||
actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
|
|
||||||
|
|
||||||
assert actual[0] == 'jks'
|
|
||||||
assert actual[1] == 'test1234'
|
|
||||||
assert isinstance(actual[2], bytes)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="no way of currently testing this")
|
|
||||||
def test_export_truststore_default_password(app):
|
|
||||||
from lemur.plugins.base import plugins
|
|
||||||
|
|
||||||
p = plugins.get('java-truststore-jks')
|
|
||||||
options = []
|
|
||||||
actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
|
|
||||||
|
|
||||||
assert actual[0] == 'jks'
|
|
||||||
assert isinstance(actual[1], str)
|
|
||||||
assert isinstance(actual[2], bytes)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="no way of currently testing this")
|
|
||||||
def test_export_keystore(app):
|
|
||||||
from lemur.plugins.base import plugins
|
|
||||||
|
|
||||||
p = plugins.get('java-keystore-jks')
|
|
||||||
options = [{'name': 'passphrase', 'value': 'test1234'}]
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
|
|
||||||
|
|
||||||
actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", INTERNAL_PRIVATE_KEY_A_STR, options)
|
|
||||||
|
|
||||||
assert actual[0] == 'jks'
|
|
||||||
assert actual[1] == 'test1234'
|
|
||||||
assert isinstance(actual[2], bytes)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip(reason="no way of currently testing this")
|
|
||||||
def test_export_keystore_default_password(app):
|
|
||||||
from lemur.plugins.base import plugins
|
|
||||||
|
|
||||||
p = plugins.get('java-keystore-jks')
|
|
||||||
options = []
|
|
||||||
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
p.export(INTERNAL_CERTIFICATE_A_STR, "", "", options)
|
|
||||||
|
|
||||||
actual = p.export(INTERNAL_CERTIFICATE_A_STR, "", INTERNAL_PRIVATE_KEY_A_STR, options)
|
|
||||||
|
|
||||||
assert actual[0] == 'jks'
|
|
||||||
assert isinstance(actual[1], str)
|
|
||||||
assert isinstance(actual[2], bytes)
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.lemur_jks.plugin
|
||||||
|
:platform: Unix
|
||||||
|
:copyright: (c) 2018 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
.. moduleauthor:: Marti Raudsepp <marti@juffo.org>
|
||||||
|
"""
|
||||||
|
|
||||||
|
from cryptography.fernet import Fernet
|
||||||
|
from cryptography.hazmat.primitives import serialization
|
||||||
|
from jks import PrivateKeyEntry, KeyStore, TrustedCertEntry
|
||||||
|
|
||||||
|
from lemur.common.defaults import common_name
|
||||||
|
from lemur.common.utils import parse_certificate, parse_cert_chain, parse_private_key
|
||||||
|
from lemur.plugins import lemur_jks as jks
|
||||||
|
from lemur.plugins.bases import ExportPlugin
|
||||||
|
|
||||||
|
|
||||||
|
def cert_chain_as_der(cert, chain):
|
||||||
|
"""Return a certificate and its chain in a list format, as expected by pyjks."""
|
||||||
|
|
||||||
|
certs = [parse_certificate(cert)]
|
||||||
|
certs.extend(parse_cert_chain(chain))
|
||||||
|
# certs (list) – A list of certificates, as byte strings. The first one should be the one belonging to the private
|
||||||
|
# key, the others the chain (in correct order).
|
||||||
|
return [cert.public_bytes(encoding=serialization.Encoding.DER) for cert in certs]
|
||||||
|
|
||||||
|
|
||||||
|
def create_truststore(cert, chain, alias, passphrase):
|
||||||
|
entries = []
|
||||||
|
for idx, cert_bytes in enumerate(cert_chain_as_der(cert, chain)):
|
||||||
|
# The original cert gets name <ALIAS>_cert, first chain element is <ALIAS>_cert_1, etc.
|
||||||
|
cert_alias = alias + '_cert' + ('_{}'.format(idx) if idx else '')
|
||||||
|
entries.append(TrustedCertEntry.new(cert_alias, cert_bytes))
|
||||||
|
|
||||||
|
return KeyStore.new('jks', entries).saves(passphrase)
|
||||||
|
|
||||||
|
|
||||||
|
def create_keystore(cert, chain, key, alias, passphrase):
|
||||||
|
certs_bytes = cert_chain_as_der(cert, chain)
|
||||||
|
key_bytes = parse_private_key(key).private_bytes(
|
||||||
|
encoding=serialization.Encoding.DER,
|
||||||
|
format=serialization.PrivateFormat.PKCS8,
|
||||||
|
encryption_algorithm=serialization.NoEncryption()
|
||||||
|
)
|
||||||
|
entry = PrivateKeyEntry.new(alias, certs_bytes, key_bytes)
|
||||||
|
|
||||||
|
return KeyStore.new('jks', [entry]).saves(passphrase)
|
||||||
|
|
||||||
|
|
||||||
|
class JavaTruststoreExportPlugin(ExportPlugin):
|
||||||
|
title = 'Java Truststore (JKS)'
|
||||||
|
slug = 'java-truststore-jks'
|
||||||
|
description = 'Generates a JKS truststore'
|
||||||
|
requires_key = False
|
||||||
|
version = jks.VERSION
|
||||||
|
|
||||||
|
author = 'Marti Raudsepp'
|
||||||
|
author_url = 'https://github.com/intgr'
|
||||||
|
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
'name': 'alias',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'Enter the alias you wish to use for the truststore.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'passphrase',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'If no passphrase is given one will be generated for you, we highly recommend this.',
|
||||||
|
'validation': ''
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
def export(self, body, chain, key, options, **kwargs):
|
||||||
|
"""
|
||||||
|
Generates a Java Truststore
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.get_option('alias', options):
|
||||||
|
alias = self.get_option('alias', options)
|
||||||
|
else:
|
||||||
|
alias = common_name(parse_certificate(body))
|
||||||
|
|
||||||
|
if self.get_option('passphrase', options):
|
||||||
|
passphrase = self.get_option('passphrase', options)
|
||||||
|
else:
|
||||||
|
passphrase = Fernet.generate_key().decode('utf-8')
|
||||||
|
|
||||||
|
raw = create_truststore(body, chain, alias, passphrase)
|
||||||
|
|
||||||
|
return 'jks', passphrase, raw
|
||||||
|
|
||||||
|
|
||||||
|
class JavaKeystoreExportPlugin(ExportPlugin):
|
||||||
|
title = 'Java Keystore (JKS)'
|
||||||
|
slug = 'java-keystore-jks'
|
||||||
|
description = 'Generates a JKS keystore'
|
||||||
|
version = jks.VERSION
|
||||||
|
|
||||||
|
author = 'Marti Raudsepp'
|
||||||
|
author_url = 'https://github.com/intgr'
|
||||||
|
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
'name': 'passphrase',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'If no passphrase is given one will be generated for you, we highly recommend this.',
|
||||||
|
'validation': ''
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'alias',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'Enter the alias you wish to use for the keystore.',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def export(self, body, chain, key, options, **kwargs):
|
||||||
|
"""
|
||||||
|
Generates a Java Keystore
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.get_option('passphrase', options):
|
||||||
|
passphrase = self.get_option('passphrase', options)
|
||||||
|
else:
|
||||||
|
passphrase = Fernet.generate_key().decode('utf-8')
|
||||||
|
|
||||||
|
if self.get_option('alias', options):
|
||||||
|
alias = self.get_option('alias', options)
|
||||||
|
else:
|
||||||
|
alias = common_name(parse_certificate(body))
|
||||||
|
|
||||||
|
raw = create_keystore(body, chain, key, alias, passphrase)
|
||||||
|
|
||||||
|
return 'jks', passphrase, raw
|
|
@ -0,0 +1,96 @@
|
||||||
|
import pytest
|
||||||
|
from jks import KeyStore, TrustedCertEntry, PrivateKeyEntry
|
||||||
|
|
||||||
|
from lemur.tests.vectors import INTERNAL_CERTIFICATE_A_STR, SAN_CERT_STR, INTERMEDIATE_CERT_STR, ROOTCA_CERT_STR, \
|
||||||
|
SAN_CERT_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_truststore(app):
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
p = plugins.get('java-truststore-jks')
|
||||||
|
options = [
|
||||||
|
{'name': 'passphrase', 'value': 'hunter2'},
|
||||||
|
{'name': 'alias', 'value': 'AzureDiamond'},
|
||||||
|
]
|
||||||
|
chain = INTERMEDIATE_CERT_STR + '\n' + ROOTCA_CERT_STR
|
||||||
|
ext, password, raw = p.export(SAN_CERT_STR, chain, SAN_CERT_KEY, options)
|
||||||
|
|
||||||
|
assert ext == 'jks'
|
||||||
|
assert password == 'hunter2'
|
||||||
|
assert isinstance(raw, bytes)
|
||||||
|
|
||||||
|
ks = KeyStore.loads(raw, 'hunter2')
|
||||||
|
assert ks.store_type == 'jks'
|
||||||
|
# JKS lower-cases alias strings
|
||||||
|
assert ks.entries.keys() == {'azurediamond_cert', 'azurediamond_cert_1', 'azurediamond_cert_2'}
|
||||||
|
assert isinstance(ks.entries['azurediamond_cert'], TrustedCertEntry)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_truststore_defaults(app):
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
p = plugins.get('java-truststore-jks')
|
||||||
|
options = []
|
||||||
|
ext, password, raw = p.export(INTERNAL_CERTIFICATE_A_STR, '', '', options)
|
||||||
|
|
||||||
|
assert ext == 'jks'
|
||||||
|
assert isinstance(password, str)
|
||||||
|
assert isinstance(raw, bytes)
|
||||||
|
|
||||||
|
ks = KeyStore.loads(raw, password)
|
||||||
|
assert ks.store_type == 'jks'
|
||||||
|
# JKS lower-cases alias strings
|
||||||
|
assert ks.entries.keys() == {'acommonname_cert'}
|
||||||
|
assert isinstance(ks.entries['acommonname_cert'], TrustedCertEntry)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_keystore(app):
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
p = plugins.get('java-keystore-jks')
|
||||||
|
options = [
|
||||||
|
{'name': 'passphrase', 'value': 'hunter2'},
|
||||||
|
{'name': 'alias', 'value': 'AzureDiamond'},
|
||||||
|
]
|
||||||
|
|
||||||
|
chain = INTERMEDIATE_CERT_STR + '\n' + ROOTCA_CERT_STR
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
p.export(INTERNAL_CERTIFICATE_A_STR, chain, '', options)
|
||||||
|
|
||||||
|
ext, password, raw = p.export(SAN_CERT_STR, chain, SAN_CERT_KEY, options)
|
||||||
|
|
||||||
|
assert ext == 'jks'
|
||||||
|
assert password == 'hunter2'
|
||||||
|
assert isinstance(raw, bytes)
|
||||||
|
|
||||||
|
ks = KeyStore.loads(raw, password)
|
||||||
|
assert ks.store_type == 'jks'
|
||||||
|
# JKS lower-cases alias strings
|
||||||
|
assert ks.entries.keys() == {'azurediamond'}
|
||||||
|
entry = ks.entries['azurediamond']
|
||||||
|
assert isinstance(entry, PrivateKeyEntry)
|
||||||
|
assert len(entry.cert_chain) == 3 # Cert and chain were provided
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_keystore_defaults(app):
|
||||||
|
from lemur.plugins.base import plugins
|
||||||
|
|
||||||
|
p = plugins.get('java-keystore-jks')
|
||||||
|
options = []
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
p.export(INTERNAL_CERTIFICATE_A_STR, '', '', options)
|
||||||
|
|
||||||
|
ext, password, raw = p.export(SAN_CERT_STR, '', SAN_CERT_KEY, options)
|
||||||
|
|
||||||
|
assert ext == 'jks'
|
||||||
|
assert isinstance(password, str)
|
||||||
|
assert isinstance(raw, bytes)
|
||||||
|
|
||||||
|
ks = KeyStore.loads(raw, password)
|
||||||
|
assert ks.store_type == 'jks'
|
||||||
|
assert ks.entries.keys() == {'san.example.org'}
|
||||||
|
entry = ks.entries['san.example.org']
|
||||||
|
assert isinstance(entry, PrivateKeyEntry)
|
||||||
|
assert len(entry.cert_chain) == 1 # Only cert itself, no chain was provided
|
|
@ -18,7 +18,7 @@ invoke==1.2.0
|
||||||
mccabe==0.6.1 # via flake8
|
mccabe==0.6.1 # via flake8
|
||||||
nodeenv==1.3.3
|
nodeenv==1.3.3
|
||||||
pkginfo==1.5.0.1 # via twine
|
pkginfo==1.5.0.1 # via twine
|
||||||
pre-commit==1.15.1
|
pre-commit==1.15.2
|
||||||
pycodestyle==2.3.1 # via flake8
|
pycodestyle==2.3.1 # via flake8
|
||||||
pyflakes==1.6.0 # via flake8
|
pyflakes==1.6.0 # via flake8
|
||||||
pygments==2.3.1 # via readme-renderer
|
pygments==2.3.1 # via readme-renderer
|
||||||
|
@ -30,7 +30,7 @@ six==1.12.0 # via bleach, cfgv, pre-commit, readme-renderer
|
||||||
toml==0.10.0 # via pre-commit
|
toml==0.10.0 # via pre-commit
|
||||||
tqdm==4.31.1 # via twine
|
tqdm==4.31.1 # via twine
|
||||||
twine==1.13.0
|
twine==1.13.0
|
||||||
urllib3==1.24.1 # via requests
|
urllib3==1.24.2 # via requests
|
||||||
virtualenv==16.4.3 # via pre-commit
|
virtualenv==16.4.3 # via pre-commit
|
||||||
webencodings==0.5.1 # via bleach
|
webencodings==0.5.1 # via bleach
|
||||||
zipp==0.3.3 # via importlib-metadata
|
zipp==0.3.3 # via importlib-metadata
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#
|
#
|
||||||
# pip-compile --output-file requirements-docs.txt requirements-docs.in -U --no-index
|
# pip-compile --output-file requirements-docs.txt requirements-docs.in -U --no-index
|
||||||
#
|
#
|
||||||
|
-e git+git://github.com/kurtbrose/pyjks.git@e742f80b0bbd06c5a6cf0535985af6416ba014a4#egg=pyjks
|
||||||
acme==0.33.1
|
acme==0.33.1
|
||||||
alabaster==0.7.12 # via sphinx
|
alabaster==0.7.12 # via sphinx
|
||||||
alembic-autogenerate-enums==0.0.2
|
alembic-autogenerate-enums==0.0.2
|
||||||
|
@ -17,12 +18,12 @@ babel==2.6.0 # via sphinx
|
||||||
bcrypt==3.1.6
|
bcrypt==3.1.6
|
||||||
billiard==3.6.0.0
|
billiard==3.6.0.0
|
||||||
blinker==1.4
|
blinker==1.4
|
||||||
boto3==1.9.130
|
boto3==1.9.134
|
||||||
botocore==1.12.130
|
botocore==1.12.134
|
||||||
celery[redis]==4.3.0
|
celery[redis]==4.3.0
|
||||||
certifi==2019.3.9
|
certifi==2019.3.9
|
||||||
certsrv==2.1.1
|
certsrv==2.1.1
|
||||||
cffi==1.12.2
|
cffi==1.12.3
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
click==7.0
|
click==7.0
|
||||||
cloudflare==2.1.0
|
cloudflare==2.1.0
|
||||||
|
@ -47,6 +48,7 @@ idna==2.8
|
||||||
imagesize==1.1.0 # via sphinx
|
imagesize==1.1.0 # via sphinx
|
||||||
inflection==0.3.1
|
inflection==0.3.1
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
|
javaobj-py3==0.2.4
|
||||||
jinja2==2.10.1
|
jinja2==2.10.1
|
||||||
jmespath==0.9.4
|
jmespath==0.9.4
|
||||||
josepy==1.1.0
|
josepy==1.1.0
|
||||||
|
@ -67,6 +69,7 @@ psycopg2==2.8.2
|
||||||
pyasn1-modules==0.2.4
|
pyasn1-modules==0.2.4
|
||||||
pyasn1==0.4.5
|
pyasn1==0.4.5
|
||||||
pycparser==2.19
|
pycparser==2.19
|
||||||
|
pycryptodomex==3.8.1
|
||||||
pygments==2.3.1 # via sphinx
|
pygments==2.3.1 # via sphinx
|
||||||
pyjwt==1.7.1
|
pyjwt==1.7.1
|
||||||
pynacl==1.3.0
|
pynacl==1.3.0
|
||||||
|
@ -97,7 +100,8 @@ sphinxcontrib-serializinghtml==1.1.3 # via sphinx
|
||||||
sqlalchemy-utils==0.33.11
|
sqlalchemy-utils==0.33.11
|
||||||
sqlalchemy==1.3.3
|
sqlalchemy==1.3.3
|
||||||
tabulate==0.8.3
|
tabulate==0.8.3
|
||||||
urllib3==1.24.1
|
twofish==0.3.0
|
||||||
|
urllib3==1.24.2
|
||||||
vine==1.3.0
|
vine==1.3.0
|
||||||
werkzeug==0.15.2
|
werkzeug==0.15.2
|
||||||
xmltodict==0.12.0
|
xmltodict==0.12.0
|
||||||
|
|
|
@ -7,12 +7,14 @@
|
||||||
asn1crypto==0.24.0 # via cryptography
|
asn1crypto==0.24.0 # via cryptography
|
||||||
atomicwrites==1.3.0 # via pytest
|
atomicwrites==1.3.0 # via pytest
|
||||||
attrs==19.1.0 # via pytest
|
attrs==19.1.0 # via pytest
|
||||||
aws-xray-sdk==0.95 # via moto
|
aws-sam-translator==1.10.0 # via cfn-lint
|
||||||
boto3==1.9.130 # via moto
|
aws-xray-sdk==2.4.2 # via moto
|
||||||
|
boto3==1.9.134 # via aws-sam-translator, moto
|
||||||
boto==2.49.0 # via moto
|
boto==2.49.0 # via moto
|
||||||
botocore==1.12.130 # via boto3, moto, s3transfer
|
botocore==1.12.134 # via aws-xray-sdk, boto3, moto, s3transfer
|
||||||
certifi==2019.3.9 # via requests
|
certifi==2019.3.9 # via requests
|
||||||
cffi==1.12.2 # via cryptography
|
cffi==1.12.3 # via cryptography
|
||||||
|
cfn-lint==0.19.1 # via moto
|
||||||
chardet==3.0.4 # via requests
|
chardet==3.0.4 # via requests
|
||||||
click==7.0 # via flask
|
click==7.0 # via flask
|
||||||
coverage==4.5.3
|
coverage==4.5.3
|
||||||
|
@ -20,44 +22,47 @@ cryptography==2.6.1 # via moto
|
||||||
docker-pycreds==0.4.0 # via docker
|
docker-pycreds==0.4.0 # via docker
|
||||||
docker==3.7.2 # via moto
|
docker==3.7.2 # via moto
|
||||||
docutils==0.14 # via botocore
|
docutils==0.14 # via botocore
|
||||||
ecdsa==0.13 # via python-jose
|
ecdsa==0.13.2 # via python-jose
|
||||||
factory-boy==2.11.1
|
factory-boy==2.11.1
|
||||||
faker==1.0.5
|
faker==1.0.5
|
||||||
flask==1.0.2 # via pytest-flask
|
flask==1.0.2 # via pytest-flask
|
||||||
freezegun==0.3.11
|
freezegun==0.3.11
|
||||||
future==0.17.1 # via python-jose
|
future==0.17.1 # via aws-xray-sdk, python-jose
|
||||||
idna==2.8 # via requests
|
idna==2.8 # via moto, requests
|
||||||
itsdangerous==1.1.0 # via flask
|
itsdangerous==1.1.0 # via flask
|
||||||
jinja2==2.10.1 # via flask, moto
|
jinja2==2.10.1 # via flask, moto
|
||||||
jmespath==0.9.4 # via boto3, botocore
|
jmespath==0.9.4 # via boto3, botocore
|
||||||
jsondiff==1.1.1 # via moto
|
jsondiff==1.1.2 # via moto
|
||||||
|
jsonpatch==1.23 # via cfn-lint
|
||||||
jsonpickle==1.1 # via aws-xray-sdk
|
jsonpickle==1.1 # via aws-xray-sdk
|
||||||
|
jsonpointer==2.0 # via jsonpatch
|
||||||
|
jsonschema==2.6.0 # via aws-sam-translator, cfn-lint
|
||||||
markupsafe==1.1.1 # via jinja2
|
markupsafe==1.1.1 # via jinja2
|
||||||
mock==2.0.0 # via moto
|
mock==2.0.0 # via moto
|
||||||
more-itertools==7.0.0 # via pytest
|
more-itertools==7.0.0 # via pytest
|
||||||
moto==1.3.7
|
moto==1.3.8
|
||||||
nose==1.3.7
|
nose==1.3.7
|
||||||
pbr==5.1.3 # via mock
|
pbr==5.1.3 # via mock
|
||||||
pluggy==0.9.0 # via pytest
|
pluggy==0.9.0 # via pytest
|
||||||
py==1.8.0 # via pytest
|
py==1.8.0 # via pytest
|
||||||
pyaml==18.11.0 # via moto
|
pyasn1==0.4.5 # via rsa
|
||||||
pycparser==2.19 # via cffi
|
pycparser==2.19 # via cffi
|
||||||
pycryptodome==3.8.1 # via python-jose
|
|
||||||
pyflakes==2.1.1
|
pyflakes==2.1.1
|
||||||
pytest-flask==0.14.0
|
pytest-flask==0.14.0
|
||||||
pytest-mock==1.10.3
|
pytest-mock==1.10.4
|
||||||
pytest==4.4.1
|
pytest==4.4.1
|
||||||
python-dateutil==2.8.0 # via botocore, faker, freezegun, moto
|
python-dateutil==2.8.0 # via botocore, faker, freezegun, moto
|
||||||
python-jose==2.0.2 # via moto
|
python-jose==3.0.1 # via moto
|
||||||
pytz==2019.1 # via moto
|
pytz==2019.1 # via moto
|
||||||
pyyaml==5.1
|
pyyaml==5.1
|
||||||
requests-mock==1.5.2
|
requests-mock==1.5.2
|
||||||
requests==2.21.0 # via aws-xray-sdk, docker, moto, requests-mock, responses
|
requests==2.21.0 # via cfn-lint, docker, moto, requests-mock, responses
|
||||||
responses==0.10.6 # via moto
|
responses==0.10.6 # via moto
|
||||||
|
rsa==4.0 # via python-jose
|
||||||
s3transfer==0.2.0 # via boto3
|
s3transfer==0.2.0 # via boto3
|
||||||
six==1.12.0 # via cryptography, docker, docker-pycreds, faker, freezegun, mock, moto, pytest, python-dateutil, python-jose, requests-mock, responses, websocket-client
|
six==1.12.0 # via aws-sam-translator, cfn-lint, cryptography, docker, docker-pycreds, faker, freezegun, mock, moto, pytest, python-dateutil, python-jose, requests-mock, responses, websocket-client
|
||||||
text-unidecode==1.2 # via faker
|
text-unidecode==1.2 # via faker
|
||||||
urllib3==1.24.1 # via botocore, requests
|
urllib3==1.24.2 # via botocore, requests
|
||||||
websocket-client==0.56.0 # via docker
|
websocket-client==0.56.0 # via docker
|
||||||
werkzeug==0.15.2 # via flask, moto, pytest-flask
|
werkzeug==0.15.2 # via flask, moto, pytest-flask
|
||||||
wrapt==1.11.1 # via aws-xray-sdk
|
wrapt==1.11.1 # via aws-xray-sdk
|
||||||
|
|
|
@ -47,3 +47,4 @@ SQLAlchemy-Utils
|
||||||
tabulate
|
tabulate
|
||||||
xmltodict
|
xmltodict
|
||||||
pyyaml>=4.2b1 #high severity alert
|
pyyaml>=4.2b1 #high severity alert
|
||||||
|
-e git://github.com/kurtbrose/pyjks.git@e742f80b0bbd06c5a6cf0535985af6416ba014a4#egg=pyjks # Needed until pyjks publishes a release with the contents of this PR
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#
|
#
|
||||||
# pip-compile --output-file requirements.txt requirements.in -U --no-index
|
# pip-compile --output-file requirements.txt requirements.in -U --no-index
|
||||||
#
|
#
|
||||||
|
-e git+git://github.com/kurtbrose/pyjks.git@e742f80b0bbd06c5a6cf0535985af6416ba014a4#egg=pyjks
|
||||||
acme==0.33.1
|
acme==0.33.1
|
||||||
alembic-autogenerate-enums==0.0.2
|
alembic-autogenerate-enums==0.0.2
|
||||||
alembic==1.0.9 # via flask-migrate
|
alembic==1.0.9 # via flask-migrate
|
||||||
|
@ -15,12 +16,12 @@ asyncpool==1.0
|
||||||
bcrypt==3.1.6 # via flask-bcrypt, paramiko
|
bcrypt==3.1.6 # via flask-bcrypt, paramiko
|
||||||
billiard==3.6.0.0 # via celery
|
billiard==3.6.0.0 # via celery
|
||||||
blinker==1.4 # via flask-mail, flask-principal, raven
|
blinker==1.4 # via flask-mail, flask-principal, raven
|
||||||
boto3==1.9.130
|
boto3==1.9.134
|
||||||
botocore==1.12.130
|
botocore==1.12.134
|
||||||
celery[redis]==4.3.0
|
celery[redis]==4.3.0
|
||||||
certifi==2019.3.9
|
certifi==2019.3.9
|
||||||
certsrv==2.1.1
|
certsrv==2.1.1
|
||||||
cffi==1.12.2 # via bcrypt, cryptography, pynacl
|
cffi==1.12.3 # via bcrypt, cryptography, pynacl
|
||||||
chardet==3.0.4 # via requests
|
chardet==3.0.4 # via requests
|
||||||
click==7.0 # via flask
|
click==7.0 # via flask
|
||||||
cloudflare==2.1.0
|
cloudflare==2.1.0
|
||||||
|
@ -44,6 +45,7 @@ hvac==0.8.2
|
||||||
idna==2.8 # via requests
|
idna==2.8 # via requests
|
||||||
inflection==0.3.1
|
inflection==0.3.1
|
||||||
itsdangerous==1.1.0 # via flask
|
itsdangerous==1.1.0 # via flask
|
||||||
|
javaobj-py3==0.2.4
|
||||||
jinja2==2.10.1
|
jinja2==2.10.1
|
||||||
jmespath==0.9.4 # via boto3, botocore
|
jmespath==0.9.4 # via boto3, botocore
|
||||||
josepy==1.1.0 # via acme
|
josepy==1.1.0 # via acme
|
||||||
|
@ -63,6 +65,7 @@ psycopg2==2.8.2
|
||||||
pyasn1-modules==0.2.4 # via python-ldap
|
pyasn1-modules==0.2.4 # via python-ldap
|
||||||
pyasn1==0.4.5 # via ndg-httpsclient, paramiko, pyasn1-modules, python-ldap
|
pyasn1==0.4.5 # via ndg-httpsclient, paramiko, pyasn1-modules, python-ldap
|
||||||
pycparser==2.19 # via cffi
|
pycparser==2.19 # via cffi
|
||||||
|
pycryptodomex==3.8.1
|
||||||
pyjwt==1.7.1
|
pyjwt==1.7.1
|
||||||
pynacl==1.3.0 # via paramiko
|
pynacl==1.3.0 # via paramiko
|
||||||
pyopenssl==19.0.0
|
pyopenssl==19.0.0
|
||||||
|
@ -82,7 +85,8 @@ six==1.12.0
|
||||||
sqlalchemy-utils==0.33.11
|
sqlalchemy-utils==0.33.11
|
||||||
sqlalchemy==1.3.3 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils
|
sqlalchemy==1.3.3 # via alembic, flask-sqlalchemy, marshmallow-sqlalchemy, sqlalchemy-utils
|
||||||
tabulate==0.8.3
|
tabulate==0.8.3
|
||||||
urllib3==1.24.1 # via botocore, requests
|
twofish==0.3.0
|
||||||
|
urllib3==1.24.2 # via botocore, requests
|
||||||
vine==1.3.0 # via amqp, celery
|
vine==1.3.0 # via amqp, celery
|
||||||
werkzeug==0.15.2 # via flask
|
werkzeug==0.15.2 # via flask
|
||||||
xmltodict==0.12.0
|
xmltodict==0.12.0
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -143,8 +143,8 @@ setup(
|
||||||
'aws_s3 = lemur.plugins.lemur_aws.plugin:S3DestinationPlugin',
|
'aws_s3 = lemur.plugins.lemur_aws.plugin:S3DestinationPlugin',
|
||||||
'email_notification = lemur.plugins.lemur_email.plugin:EmailNotificationPlugin',
|
'email_notification = lemur.plugins.lemur_email.plugin:EmailNotificationPlugin',
|
||||||
'slack_notification = lemur.plugins.lemur_slack.plugin:SlackNotificationPlugin',
|
'slack_notification = lemur.plugins.lemur_slack.plugin:SlackNotificationPlugin',
|
||||||
'java_truststore_export = lemur.plugins.lemur_java.plugin:JavaTruststoreExportPlugin',
|
'java_truststore_export = lemur.plugins.lemur_jks.plugin:JavaTruststoreExportPlugin',
|
||||||
'java_keystore_export = lemur.plugins.lemur_java.plugin:JavaKeystoreExportPlugin',
|
'java_keystore_export = lemur.plugins.lemur_jks.plugin:JavaKeystoreExportPlugin',
|
||||||
'openssl_export = lemur.plugins.lemur_openssl.plugin:OpenSSLExportPlugin',
|
'openssl_export = lemur.plugins.lemur_openssl.plugin:OpenSSLExportPlugin',
|
||||||
'atlas_metric = lemur.plugins.lemur_atlas.plugin:AtlasMetricPlugin',
|
'atlas_metric = lemur.plugins.lemur_atlas.plugin:AtlasMetricPlugin',
|
||||||
'kubernetes_destination = lemur.plugins.lemur_kubernetes.plugin:KubernetesDestinationPlugin',
|
'kubernetes_destination = lemur.plugins.lemur_kubernetes.plugin:KubernetesDestinationPlugin',
|
||||||
|
|
Loading…
Reference in New Issue