-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreate-client
executable file
·109 lines (83 loc) · 3.94 KB
/
create-client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#!/usr/bin/env python
# Copyright 2017-2019 Fizyr B.V. - https://fizyr.com
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
import sys
from pathlib import Path
from argparse import ArgumentParser
from datetime import datetime, timedelta
from typing import List, Optional, Tuple
from cryptography import x509
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from message import msg, msg2, error
import util
import cert_tools
crypto_backend = default_backend()
def parseArguments():
parser = ArgumentParser(description='Create new client certificate using for a parent CA certificate.')
parser.add_argument('PARENT', help='Parent CA certificate')
parser.add_argument('MACHINE', help='Machine name.')
parser.add_argument('--key', help='Path to store the generated key.')
parser.add_argument('--csr', help='Path to store the generated CSR.')
return parser.parse_args()
def create_csr(key_file: Path, csr_file: Path, dn: x509.Name, dns_name: str) -> int:
files = [key_file, csr_file]
# Make sure the files don't exist yet.
for file in files:
if file.exists():
error('File already exists: {}', file)
return 1
# Generate key with modified umask.
msg('Generating key pair: {}', key_file)
umask = util.or_umask(0o277)
key = cert_tools.generate_rsa_key(key_file)
os.umask(umask)
# Create a CSR for the parent CA to sign.
msg('Making CSR: {}', csr_file)
cert_tools.make_csr(csr_file, key=key, name=dn, extensions=cert_tools.client_extensions(dns_name))
return 0
def main():
now = datetime.now().astimezone()
args = parseArguments()
ca_cert = cert_tools.load_first_certificate(util.read_file(args.PARENT))
ca_dns = cert_tools.get_first_dns_name(cert_tools.get_subject_alt_names(ca_cert))
if not ca_dns:
error('Parent certificate does not have a subject alternative DNS name')
return 1
ca_dns = ca_dns.value.lower()
args.MACHINE = args.MACHINE.lower()
client_name = cert_tools.prefix_name(ca_cert.subject, NameOID.COMMON_NAME, args.MACHINE)
client_dns = '{}.{}'.format(args.MACHINE, ca_dns)
key_file = Path(args.key or '{}-{:%Y-%m-%d-%H-%M-%S}.key.pem'.format(client_dns, now))
csr_file = Path(args.csr or '{}-{:%Y-%m-%d-%H-%M-%S}.csr.pem'.format(client_dns, now))
return create_csr(key_file, csr_file, client_name, client_dns)
if __name__ == '__main__':
sys.exit(main())