@@ -76,10 +76,20 @@ def _loopback_for_cert_thread(context, server):
7676 ssl_sock .send (b'0000' )
7777
7878
79- def _loopback_for_cert (certificate , private_key , certificate_chain ):
79+ def _loopback_for_cert (
80+ certificate ,
81+ private_key ,
82+ certificate_chain ,
83+ * ,
84+ private_key_password = None ,
85+ ):
8086 """Create a loopback connection to parse a cert with a private key."""
8187 context = ssl .create_default_context (cafile = certificate_chain )
82- context .load_cert_chain (certificate , private_key )
88+ context .load_cert_chain (
89+ certificate ,
90+ private_key ,
91+ password = private_key_password ,
92+ )
8393 context .check_hostname = False
8494 context .verify_mode = ssl .CERT_NONE
8595
@@ -112,15 +122,26 @@ def _loopback_for_cert(certificate, private_key, certificate_chain):
112122 server .close ()
113123
114124
115- def _parse_cert (certificate , private_key , certificate_chain ):
125+ def _parse_cert (
126+ certificate ,
127+ private_key ,
128+ certificate_chain ,
129+ * ,
130+ private_key_password = None ,
131+ ):
116132 """Parse a certificate."""
117133 # loopback_for_cert uses socket.socketpair which was only
118134 # introduced in Python 3.0 for *nix and 3.5 for Windows
119135 # and requires OS support (AttributeError, OSError)
120136 # it also requires a private key either in its own file
121137 # or combined with the cert (SSLError)
122138 with suppress (AttributeError , ssl .SSLError , OSError ):
123- return _loopback_for_cert (certificate , private_key , certificate_chain )
139+ return _loopback_for_cert (
140+ certificate ,
141+ private_key ,
142+ certificate_chain ,
143+ private_key_password = private_key_password ,
144+ )
124145
125146 # KLUDGE: using an undocumented, private, test method to parse a cert
126147 # unfortunately, it is the only built-in way without a connection
@@ -153,6 +174,9 @@ class BuiltinSSLAdapter(Adapter):
153174 ciphers = None
154175 """The ciphers list of SSL."""
155176
177+ private_key_password = None
178+ """Optional passphrase for password protected private key."""
179+
156180 # from mod_ssl/pkg.sslmod/ssl_engine_vars.c ssl_var_lookup_ssl_cert
157181 CERT_KEY_TO_ENV = {
158182 'version' : 'M_VERSION' ,
@@ -208,6 +232,8 @@ def __init__(
208232 private_key ,
209233 certificate_chain = None ,
210234 ciphers = None ,
235+ * ,
236+ private_key_password = None ,
211237 ):
212238 """Set up context in addition to base class properties if available."""
213239 if ssl is None :
@@ -218,19 +244,29 @@ def __init__(
218244 private_key ,
219245 certificate_chain ,
220246 ciphers ,
247+ private_key_password = private_key_password ,
221248 )
222249
223250 self .context = ssl .create_default_context (
224251 purpose = ssl .Purpose .CLIENT_AUTH ,
225252 cafile = certificate_chain ,
226253 )
227- self .context .load_cert_chain (certificate , private_key )
254+ self .context .load_cert_chain (
255+ certificate ,
256+ private_key ,
257+ password = private_key_password ,
258+ )
228259 if self .ciphers is not None :
229260 self .context .set_ciphers (ciphers )
230261
231262 self ._server_env = self ._make_env_cert_dict (
232263 'SSL_SERVER' ,
233- _parse_cert (certificate , private_key , self .certificate_chain ),
264+ _parse_cert (
265+ certificate ,
266+ private_key ,
267+ self .certificate_chain ,
268+ private_key_password = private_key_password ,
269+ ),
234270 )
235271 if not self ._server_env :
236272 return
0 commit comments