Ticket #2061: use-chain-certs-if-possible-2061.patch

File use-chain-certs-if-possible-2061.patch, 11.1 KB (added by Hynek Schlawack, 8 years ago)

Add support for chained certificates if available.

  • twisted/internet/ssl.py

    # Bazaar merge directive format 2 (Bazaar 0.90)
    # revision_id: hs@ox.cx-20130120223509-hncc428ohhsp0rwv
    # target_branch: file:///Users/hynek/Projects/Twisted/trunk/
    # testament_sha1: 72dd321d00950e898b1654c7c31421084391a82e
    # timestamp: 2013-01-20 23:36:07 +0100
    # base_revision_id: svn-v4:bbbe8e31-12d6-0310-92fd-\
    #   ac37d47ddeeb:trunk:36805
    # 
    # Begin patch
    === modified file 'twisted/internet/ssl.py'
     
    2222from __future__ import division, absolute_import
    2323
    2424# System imports
     25import warnings
     26
    2527from OpenSSL import SSL
    2628supported = True
    2729
     
    5658                 sslmethod=SSL.SSLv23_METHOD, _contextFactory=SSL.Context):
    5759        """
    5860        @param privateKeyFileName: Name of a file containing a private key
    59         @param certificateFileName: Name of a file containing a certificate
     61        @param certificateFileName: Name of a file containing a certificate. It
     62            is possible to supply chain certificates by concatenating them to
     63            the file.
    6064        @param sslmethod: The SSL method to use
    6165        """
    6266        self.privateKeyFileName = privateKeyFileName
     
    7680            # Disallow SSLv2!  It's insecure!  SSLv3 has been around since
    7781            # 1996.  It's time to move on.
    7882            ctx.set_options(SSL.OP_NO_SSLv2)
    79             ctx.use_certificate_file(self.certificateFileName)
     83
     84            if hasattr(self._contextFactory, 'use_certificate_chain_file'):
     85                try:
     86                    ctx.use_certificate_chain_file(self.certificateFileName)
     87                except SSL.Error:
     88                    # The retry is necessary since C{use_certificate_file}
     89                    # allows more input formats than the chain variant which
     90                    # supports only PEM.
     91                    ctx.use_certificate_file(self.certificateFileName)
     92            else:
     93                warnings.warn('Chained certificates are unavailable. Use '
     94                              'PyOpenSSL >= 0.10 if you need this feature.')
     95                ctx.use_certificate_file(self.certificateFileName)
    8096            ctx.use_privatekey_file(self.privateKeyFileName)
    8197            self._context = ctx
    8298
  • twisted/test/test_ssl.py

    === modified file 'twisted/test/test_ssl.py'
     
    623623
    624624
    625625
    626 class FakeContext:
     626class OldFakeContext:
    627627    """
    628628    L{OpenSSL.SSL.Context} double which can more easily be inspected.
     629
     630    This simulates PyOpenSSL version < 0.10 which doesn't have
     631    C{use_certificate_chain_file} yet.
    629632    """
    630633    def __init__(self, method):
    631634        self._method = method
    632635        self._options = 0
     636        self._use_certificate_file_calledWith = []
     637        self._use_privatekey_file_calledWith = []
    633638
    634639
    635640    def set_options(self, options):
     
    637642
    638643
    639644    def use_certificate_file(self, fileName):
    640         pass
     645        self._use_certificate_file_calledWith.append(fileName)
    641646
    642647
    643648    def use_privatekey_file(self, fileName):
    644         pass
     649        self._use_privatekey_file_calledWith.append(fileName)
     650
     651
     652
     653class FakeContext(OldFakeContext):
     654    """
     655    Adds C{use_certificate_chain_file} to L{OldFakeContext} and introspection
     656    thereof.
     657    """
     658    def __init__(self, method):
     659        OldFakeContext.__init__(self, method)
     660        self._use_certificate_chain_file_calledWith = []
     661
     662
     663    def use_certificate_chain_file(self, fileName):
     664        self._use_certificate_chain_file_calledWith.append(fileName)
    645665
    646666
    647667
     
    695715            ssl.DefaultOpenSSLContextFactory, self.mktemp(), certPath)
    696716
    697717
     718    def test_chainLoaderNotAvailable(self):
     719        """
     720        PyOpenSSL < 0.10 is still supported but lacks support for chained
     721        certificates. Ensure we fall back and warn the user.
     722        """
     723        ctx = ssl.DefaultOpenSSLContextFactory(
     724            certPath, certPath,
     725            _contextFactory=OldFakeContext)._context
     726        self.assertEqual([certPath], ctx._use_certificate_file_calledWith)
     727        warns = self.flushWarnings(
     728            offendingFunctions=[ssl.DefaultOpenSSLContextFactory.cacheContext])
     729        self.assertEqual(1, len(warns))
     730        self.assertTrue(warns[0]['message'].startswith('Chained certificates'))
     731
     732
     733    def test_chainLoaderIsAvailable(self):
     734        """
     735        Ensure L{SSL.Context.use_certificate_chain_file} is called if
     736        available.
     737        """
     738        self.assertEqual(
     739            [certPath], self.context._use_certificate_chain_file_calledWith)
     740        self.assertEqual([], self.context._use_certificate_file_calledWith)
     741
     742
     743    def test_certificateIsNotPEMEncoded(self):
     744        """
     745        Nowadays it's preferable to use
     746        L{SSL.Context.use_certificate_chain_file} over
     747        L{SSL.Context.use_certificate_file}.  However, the former accepts only
     748        PEM encoded certificates while the latter is more general.  Therefore
     749        if C{use_certificate_chain_file} fails, it's possible a non-PEM
     750        certificate has been supplied and we have to re-try using
     751        C{use_certificate_file}.
     752        """
     753        class FakeContextRaiseOnChain(FakeContext):
     754            def use_certificate_chain_file(self, fileName):
     755                raise SSL.Error()
     756
     757        factory = ssl.DefaultOpenSSLContextFactory(
     758            certPath, certPath,
     759            _contextFactory=FakeContextRaiseOnChain)
     760        self.assertEqual(
     761            [certPath],
     762            factory._context._use_certificate_file_calledWith)
     763
    698764
    699765class ClientContextFactoryTests(unittest.TestCase):
    700766    """
  • twisted/topfiles/6258.feature

    === added file 'twisted/topfiles/6258.feature'
     
     1twisted.internet.ssl.DefaultOpenSSLContextFactory now supports chained certificate files.