<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On 31 Jan 2017, at 18:26, Steve Dower <<a href="mailto:steve.dower@python.org" class="">steve.dower@python.org</a>> wrote:</div><div class=""><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">In short, I want to allow Python code to set OpenSSL's certificate validation callback. Basically, given a raw certificate, return True/False based on whether it should be trusted. I then have separate code (yet to be published) implementing the callback on Windows by calling into the WinVerifyTrust API with the HTTPS provider, which (allegedly) behaves identically to browsers using the same API (again, allegedly - I have absolutely no evidence to support this other than some manual testing).</span><br style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""></div></blockquote><br class=""></div><div>For context here Steve, this is not quite what Chrome does (and I cannot stress enough that the Chrome approach is the best one I’ve seen, the folks working on it really do know what they’re doing). The reason here is a bit tricky, but essentially the validation callback is called incrementally for each step up the chain. This is not normally what a platform validation API actually wants: generally they want the entire cert chain the remote peer sent at once.</div><div><br class=""></div><div>Chrome, instead, essentially disables the OpenSSL cert validation entirely: they still require the certificate be presented, but override the verification callback to always say “yeah that’s cool, no big deal”. They then take the complete cert chain provided by the remote peer and pass that to the platform validation code in one shot after the handshake is complete, but before they send/receive any data on the connection. This is still safe: so long as you don’t actually expose any data before you have validated the certificates you aren’t at risk.</div><div><br class=""></div><div>I have actually prototyped this approach for Requests/urllib3 in the past. I wrote a small Rust extension to call into the platform-native code, and then wrapped it in a CFFI library that exposed a single callable to validate a cert chain for a specific hostname (library is here: <a href="https://github.com/python-hyper/certitude" class="">https://github.com/python-hyper/certitude</a>). This could then be called from urllib3 code that used PyOpenSSL using this patch here: <a href="https://github.com/shazow/urllib3/pull/802/files" class="">https://github.com/shazow/urllib3/pull/802/files</a></div><div><br class=""></div><div>PLEASE DON’T ACTUALLY USE THIS CODE. I have not validated that certitude does entirely the right things with the platform APIs. This is just an example of a stripped-down version of what Chrome does, as a potential example of how to get something working for your Python use-case.</div><div><br class=""></div><div>Cory</div></body></html>