Skip to content

Commit

Permalink
feature: certtool improvements
Browse files Browse the repository at this point in the history
- default ca cn to address corresponding to main public key

- add `package` option to copy required server files into a separate directory for easy copying
  • Loading branch information
Jaguar0625 committed Nov 24, 2021
1 parent eac0ca1 commit 7ae99ff
Showing 1 changed file with 102 additions and 39 deletions.
141 changes: 102 additions & 39 deletions certtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
from pathlib import Path
from subprocess import PIPE, STDOUT, Popen

from symbolchain.core.CryptoTypes import PrivateKey
from symbolchain.core.Network import NetworkLocator
from symbolchain.core.PrivateKeyStorage import PrivateKeyStorage
from symbolchain.core.symbol.KeyPair import KeyPair
from symbolchain.core.symbol.Network import Network
from zenlog import log


Expand Down Expand Up @@ -87,42 +88,25 @@ def prepare_node_config(node_cn):
''')


def main():
parser = argparse.ArgumentParser(description='Cert generation tool', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--output', help='certificates working directory (all files will be created inside)', default='certificates')
parser.add_argument('--ca', help='path to key PEM file that will be used as a CA key', default='ca.key.pem')
parser.add_argument('--name-ca', help='use provided name as CA CN (common name) - suggested: account name')
parser.add_argument('--name-node', help='use provided name as node CN (common name) - suggested: node name, host or ip')
parser.add_argument('--force', help='overwrite output directory if it already exists', action='store_true')
args = parser.parse_args()

filepath = Path(args.output)
if filepath.exists():
if not args.force:
raise FileExistsError(f'output directory ({filepath}) already exists, use --force to overwrite')
shutil.rmtree(filepath)

check_openssl_version()

# obtain full path prior to switching directory
ca_path = Path(args.ca).absolute()

os.makedirs(filepath)
os.chdir(filepath)

def openssl_prepare_keys(ca_path):
log.info('creating ca.pubkey.pem')
run_openssl(['pkey', '-in', ca_path, '-pubout', '-out', 'ca.pubkey.pem'])
run_openssl([
'pkey',
'-in', ca_path,
'-out', 'ca.pubkey.pem',
'-pubout'
])

log.info('creating random node.key.pem')
node_key_pair = KeyPair(PrivateKey.random())
PrivateKeyStorage('.', None).save('node.key', node_key_pair.private_key)
run_openssl([
'genpkey',
'-out', 'node.key.pem',
'-outform', 'PEM',
'-algorithm', 'ed25519'
])

log.info('preparing configuration files')
ca_cn = get_common_name(args.name_ca, 'CA common name')
node_cn = get_common_name(args.name_node, 'node common name')
prepare_ca_config(ca_path, ca_cn)
prepare_node_config(node_cn)

def openssl_prepare_certs(ca_path):
log.info('creating CA certificate')
run_openssl([
'req',
Expand All @@ -131,29 +115,108 @@ def main():
'-key', ca_path,
'-new', '-x509',
'-days', '7300',
'-out', 'ca.crt.pem'])
'-out', 'ca.crt.pem'
])

# prepare node CSR
log.info('preparing node CSR')
run_openssl([
'req',
'-config', 'node.cnf',
'-key', 'node.key.pem',
'-new',
'-out', 'node.csr.pem'])
'-out', 'node.csr.pem'
])

log.info('signing node certificate')
run_openssl(['rand', '-out', './serial.dat', '-hex', '19'])

run_openssl([
'rand',
'-out', './serial.dat',
'-hex',
'19'
])
run_openssl([
'ca',
'-config', 'ca.cnf',
'-days', '375',
'-notext',
'-batch',
'-in', 'node.csr.pem',
'-out', 'node.crt.pem'])
'-out', 'node.crt.pem'
])


def prepare_directory(directory_path, force):
filepath = Path(directory_path)
if filepath.exists():
if not force:
raise FileExistsError(f'output directory ({filepath}) already exists, use --force to overwrite')

shutil.rmtree(filepath)

os.makedirs(filepath)
return filepath


def main():
parser = argparse.ArgumentParser(description='Cert generation tool', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--working', help='certificates working directory (all files will be created inside)', default='cert')
parser.add_argument('--package', help='certificates package directory (all required server files will be copied inside)')
parser.add_argument('--package-mode', help='contents of the package', choices=('all', 'node'), default='all')
parser.add_argument('--ca', help='path to key PEM file that will be used as a CA key', default='ca.key.pem')
parser.add_argument('--name-ca', help='use provided name as CA CN (common name) - suggested: account name')
parser.add_argument('--name-node', help='use provided name as node CN (common name) - suggested: node name, host or ip')
parser.add_argument('--network', help='network to use when using autogenerated ca names', default='mainnet')
parser.add_argument('--force', help='overwrite output directory if it already exists', action='store_true')
args = parser.parse_args()

check_openssl_version()

# obtain full paths prior to switching directory
ca_path = Path(args.ca).absolute()

if args.package:
package_path = Path(args.package).absolute()
prepare_directory(args.package, args.force)

os.chdir(prepare_directory(args.working, args.force))

log.info('preparing configuration files')
if args.name_ca:
ca_cn = args.name_ca
elif '.pem' == ca_path.suffix:
main_private_key = PrivateKeyStorage(ca_path.parent).load(ca_path.stem)
main_public_key = KeyPair(main_private_key).public_key

network = NetworkLocator.find_by_name(Network.NETWORKS, args.network)
main_address = network.public_key_to_address(main_public_key)
ca_cn = str(main_address)
else:
ca_cn = get_common_name(args.name_ca, 'CA common name')

node_cn = get_common_name(args.name_node, 'node common name')

log.info(f' * CA common name: {ca_cn}')
log.info(f' * Node common name: {node_cn}')

prepare_ca_config(ca_path, ca_cn)
prepare_node_config(node_cn)

openssl_prepare_keys(ca_path)
openssl_prepare_certs(ca_path)

log.info(f'certificates generated in {args.working} directory')

if args.package:
package_filenames = ['node.crt.pem', 'node.key.pem']
if 'all' == args.package_mode:
package_filenames += ['ca.pubkey.pem', 'ca.crt.pem']

for filename in package_filenames:
destination_path = package_path / filename
shutil.copyfile(filename, destination_path)
os.chmod(destination_path, 0o400)

log.info(f'certificates generated in {args.output} directory')
log.info(f'certificates packaged in {args.package} directory')


if __name__ == '__main__':
Expand Down

0 comments on commit 7ae99ff

Please sign in to comment.