/[pdpsoft]/nl.nikhef.pdp.tcs/nl.nikhef.pdp.tcs.tcsg4-tools/trunk/sgtcs-cli.py
ViewVC logotype

Contents of /nl.nikhef.pdp.tcs/nl.nikhef.pdp.tcs.tcsg4-tools/trunk/sgtcs-cli.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3291 - (show annotations) (download) (as text)
Fri Jun 19 14:23:31 2020 UTC (18 months, 4 weeks ago) by davidg
File MIME type: application/x-python
File size: 8213 byte(s)
Added TCS G4 API based request script

1 #!/usr/bin/env python3
2 """sgtcs-cli - Sectigo Terena Certificate Service command line interface
3
4 Usage:
5 sgtcs-cli new [options] <hostname> [<althostname>...]
6 sgtcs-cli retrieve [options] <hostname>
7 sgtcs-cli list-types [options]
8
9 Options:
10 -h --help Show this screen
11 --product=<product> Select alternative certificate type
12 --customer=<customer> Sectigo customer name
13 --username=<username> Username
14 --password=<password> Password
15 --type=( unified-comm | ov-multi-domain | ev-ssl
16 | ev-multi-domain | igtf-multi-domain
17 | wildcard-ssl | ov-ssl )
18 --term=<term> Validity period (days)
19
20 """
21
22 # @(#)$Id$
23 # written by Dennis van Dok, Nikhef
24
25 import os
26 from requests import Request, Session
27 from docopt import docopt
28
29 from cryptography.hazmat.backends import default_backend
30 from cryptography.hazmat.primitives import serialization
31 from cryptography.hazmat.primitives.asymmetric import rsa
32 from cryptography import x509
33 from cryptography.x509.oid import NameOID
34 from cryptography.hazmat.primitives import hashes
35
36
37 def generate_key():
38 key = rsa.generate_private_key(
39 public_exponent=65537,
40 key_size=2048,
41 backend=default_backend()
42 )
43 return key
44
45 def write_key_to_file(key, path):
46 with open(path, "wb") as f:
47 f.write(key.private_bytes(
48 encoding=serialization.Encoding.PEM,
49 format=serialization.PrivateFormat.TraditionalOpenSSL,
50 encryption_algorithm=serialization.NoEncryption(),
51 ))
52
53 def generate_csr(key, hostname, altnames):
54 csr = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([
55 # Provide various details about who we are.
56 x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u"org"),
57 x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u"terena"),
58 x509.NameAttribute(NameOID.DOMAIN_COMPONENT, u"tcs"),
59 x509.NameAttribute(NameOID.COUNTRY_NAME, u"NL"),
60 x509.NameAttribute(NameOID.LOCALITY_NAME, u"Amsterdam"),
61 x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"Nikhef"),
62 x509.NameAttribute(NameOID.COMMON_NAME, hostname),
63 #x509.NameAttribute(NameOID.EMAIL_ADDRESS, u"grid.sysadmin@nikhef.nl"),
64 ])).add_extension(
65 x509.SubjectAlternativeName(
66 # Describe what sites we want this certificate for.
67 [ x509.DNSName(x) for x in [hostname] + altnames ]),
68 critical=False,
69 ).add_extension(
70 x509.BasicConstraints(ca=False, path_length=None), critical=False,
71 ).add_extension(
72 x509.KeyUsage(digital_signature=True, key_encipherment=True,content_commitment=True,
73 data_encipherment=False, key_agreement=False, key_cert_sign=False,
74 crl_sign=False, encipher_only=False, decipher_only=False), critical=False
75 # Sign the CSR with our private key.
76 ).sign(key, hashes.SHA256(), default_backend())
77 return csr
78
79 def write_csr_to_file(csr, path):
80 csrdata = csr.public_bytes(serialization.Encoding.PEM)
81 with open(path, "wb") as f:
82 f.write(csrdata)
83 return csrdata.decode('ascii')
84
85 def get_types():
86 typesdata = { 'organizationId': 11358 }
87 url = baseurl + 'ssl/v1/types'
88 r = s.get(url, json=typesdata)
89 return r
90
91
92 def enroll(csrdata, certtype, term, hostname, altnames):
93 newreqdata = {'orgId': 11358,
94 'csr': csrdata.rstrip(),
95 'subjAltNames': ','.join([hostname] + altnames),
96 'certType': certtype,
97 'numberServers': 1,
98 'serverType': -1, # no idea
99 'term': term,
100 'optionalFields': [{'name':'localityName','value':'Amsterdam'},
101 {'name':'postalCode','value':''},
102 {'name':'stateOrProvinceName','value':''},
103 ],
104 'comments': 'Requested via ' + __name__,
105 'externalRequester': '',
106 }
107 #data = {
108 # "orgId": org_id, "csr": csr.rstrip(), "subjAltNames": subject_alt_names, "certType": type_id,
109 # "numberServers": 1, "serverType": -1, "term": term, "comments": "Enrolled by %s" % self._client.user_agent,
110 # "externalRequester": ""
111 #}
112
113 url = baseurl + 'ssl/v1/enroll'
114 r = s.post(url, json=newreqdata)
115 return r
116
117 def retrieve(hostname, sslid):
118 url = baseurl + 'ssl/v1/collect/{}/x509CO'.format(sslid)
119 print(url)
120 r = s.get(url)
121 return r
122
123
124 def enroll_keygen(certtype, hostname):
125 newreqdata = {'orgId': 11358,
126 'commonName': hostname,
127 'subjAltNames': hostname,
128 'certType': certtype,
129 'serverType': -1, # no idea
130 'term': 365,
131 'comments': 'Testing the API',
132 'algorithm': 'RSA',
133 'keySize': 2048,
134 'passPhrase': 'FooBar12',
135 }
136 url = baseurl + 'ssl/v1/enroll-keygen'
137 r = s.post(url, json=newreqdata)
138 return r
139
140 def dump_response(r):
141 """Print the result of an API call response"""
142 print(r.request.headers)
143 print('body:')
144 print(r.request.body)
145 print(r.headers)
146 print(r.status_code)
147 #print(r.json())
148
149 # This is the list of types we can use. Let's make a static
150 # structure from it.
151 # which needs to be updated for SCM 20.5+
152 # [{'id': 425, 'name': 'GÉANT Unified Communications Certificate',
153 # 'terms': [365, 730]}, {'id': 426, 'name': 'GÉANT OV Multi-Domain',
154 # 'terms': [365, 730]}, {'id': 427, 'name': 'GÉANT EV SSL', 'terms':
155 # [365, 730]}, {'id': 428, 'name': 'GÉANT EV Multi-Domain', 'terms':
156 # [365, 730]}, {'id': 429, 'name': 'GÉANT IGTF Multi Domain', 'terms':
157 # [365, 395]}, {'id': 424, 'name': 'GÉANT Wildcard SSL', 'terms':
158 # [365, 730]}, {'id': 423, 'name': 'GÉANT OV SSL', 'terms': [365,
159 # 730]}]
160 certtypes = { 'unified-comm': 425,
161 'ov-multi-domain': 426,
162 'ev-ssl': 427,
163 'ev-multi-domain': 428,
164 'igtf-multi-domain': 429,
165 'wildcard-ssl': 424,
166 'ov-ssl': 423,
167 }
168
169
170 if __name__ == '__main__':
171 arguments = docopt(__doc__, version='sectman 0.1')
172 print(arguments)
173
174 # If no password is passed in, this environment variable should have the password
175 pw = arguments['--password']
176 if pw is None:
177 pw = os.getenv('TCSAPIKEY')
178
179 username = arguments['--username']
180 if username is None:
181 username = os.getenv('TCSAPIUSER')
182
183 # This is the base of Sectigo's REST API service
184 baseurl = 'https://cert-manager.com/api/'
185
186 s = Session()
187 s.headers.update({'customerUri': 'surfnet', 'login': username, 'password': pw})
188
189 if arguments['list-types']:
190 r = get_types()
191 dump_response(r)
192 elif arguments['new']:
193 if arguments['--type'] is not None:
194 certtype = certtypes[arguments['--type']]
195 else:
196 certtype = certtypes['igtf-multi-domain']
197 if arguments['--term'] is not None:
198 term = arguments['--term']
199 else:
200 term = 365
201 print('certtype: %s' % certtype)
202 # enroll a new certificate
203 hostname = arguments['<hostname>']
204 os.mkdir(hostname, mode=0o755)
205 keypath = hostname + '/hostkey.pem'
206 csrpath = hostname + '/hostreq.pem'
207 # create a new key
208 key = generate_key()
209 write_key_to_file(key, keypath)
210 # generate a csr
211 althostnames = arguments['<althostname>']
212 csr = generate_csr(key, hostname, althostnames)
213 csrdata = write_csr_to_file(csr, csrpath)
214 # submit the csr
215 r = enroll(csrdata, certtype, term, hostname, althostnames)
216 dump_response(r)
217 print(r.json())
218 j = r.json()
219 #r = enroll_keygen(certtype, hostname)
220 # if successful, write the request id out to a file for retrieval later
221 #{"renewId":"RANDOM_TOKEN_FROM_SECTIGO","sslId":6666666}
222 with open(hostname + '/requestid.txt', 'w') as f:
223 f.write(str(j['sslId']))
224
225 elif arguments['retrieve']:
226 hostname = arguments['<hostname>']
227 with open(hostname + '/requestid.txt', 'r') as f:
228 sslid = f.read().rstrip()
229 r = retrieve(hostname, sslid)
230 dump_response(r)
231 with open(hostname + '/hostcert.pem', 'w') as f:
232 f.write(r.content.decode(r.encoding))
233

Properties

Name Value
svn:executable *

grid.support@nikhef.nl
ViewVC Help
Powered by ViewVC 1.1.28