1010import json
1111import logging
1212import os
13+ import socket
1314import sys
1415import threading
1516import time
1617import urllib2
1718
19+ from cryptography import x509
1820from cryptography .hazmat .backends import default_backend
1921from cryptography .hazmat .primitives .asymmetric import rsa
22+ from cryptography .hazmat .primitives .serialization import load_pem_private_key
2023
2124import OpenSSL
25+ from OpenSSL import SSL
2226import josepy
2327
2428from acme import challenges
@@ -144,6 +148,12 @@ def http_01_answer(client, chall_body):
144148 chall = chall_body .chall , response = response ,
145149 validation = validation )
146150
151+ def tls_alpn_01_cert (client , chall_body , domain ):
152+ """Return x509 certificate for tls-alpn-01 challenge"""
153+ response = chall_body .response (client .key )
154+ cert , key = response .gen_cert (domain )
155+ return key , cert
156+
147157def do_dns_challenges (client , authzs ):
148158 cleanup_hosts = []
149159 for a in authzs :
@@ -190,6 +200,53 @@ def cleanup():
190200 thread .join ()
191201 return cleanup
192202
203+ def do_tlsalpn_challenges (client , authzs ):
204+ port = 5001
205+ example_key , example_cert = load_example_cert ()
206+ server_certs = {'localhost' : (example_key , example_cert )}
207+ challs = {a .body .identifier .value : get_chall (a , challenges .TLSALPN01 )
208+ for a in authzs }
209+ chall_certs = {domain : tls_alpn_01_cert (client , c , domain )
210+ for domain , c in challs .items ()}
211+ # TODO: this won't be needed once acme standalone tls-alpn server serves
212+ # certs correctly, not only challenge certs.
213+ chall_certs ['localhost' ] = (example_key , example_cert )
214+ server = standalone .TLSALPN01Server (("" , port ), server_certs , chall_certs )
215+ thread = threading .Thread (target = server .serve_forever )
216+ thread .start ()
217+
218+ # Loop until the TLSALPN01Server is ready.
219+ while True :
220+ try :
221+ s = socket .socket ()
222+ s .connect (("localhost" , port ))
223+ client_ssl = SSL .Connection (SSL .Context (SSL .TLSv1_METHOD ), s )
224+ client_ssl .set_connect_state ()
225+ client_ssl .set_tlsext_host_name ("localhost" )
226+ client_ssl .set_alpn_protos ([b'acme-tls/1' ])
227+ client_ssl .do_handshake ()
228+ break
229+ except (socket .error , SSL .Error ):
230+ time .sleep (0.1 )
231+ finally :
232+ s .close ()
233+
234+ for chall_body in challs .values ():
235+ client .answer_challenge (chall_body , chall_body .response (client .key ))
236+
237+ def cleanup ():
238+ server .shutdown ()
239+ server .server_close ()
240+ thread .join ()
241+ return cleanup
242+
243+ def load_example_cert ():
244+ keypem = open ('test/test-example.key' , 'rb' ).read ()
245+ key = OpenSSL .crypto .load_privatekey (OpenSSL .crypto .FILETYPE_PEM , keypem )
246+ crtpem = open ('test/test-example.pem' , 'rb' ).read ()
247+ cert = OpenSSL .crypto .load_certificate (OpenSSL .crypto .FILETYPE_PEM , crtpem )
248+ return (key , cert )
249+
193250def auth_and_issue (domains , chall_type = "dns-01" , email = None , cert_output = None , client = None ):
194251 """Make authzs for each of the given domains, set up a server to answer the
195252 challenges in those authzs, tell the ACME server to validate the challenges,
@@ -202,6 +259,8 @@ def auth_and_issue(domains, chall_type="dns-01", email=None, cert_output=None, c
202259 cleanup = do_http_challenges (client , authzs )
203260 elif chall_type == "dns-01" :
204261 cleanup = do_dns_challenges (client , authzs )
262+ elif chall_type == "tls-alpn-01" :
263+ cleanup = do_tlsalpn_challenges (client , authzs )
205264 else :
206265 raise Exception ("invalid challenge type %s" % chall_type )
207266
0 commit comments