Skip to content

Instantly share code, notes, and snippets.

@exvito
Created November 19, 2017 13:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save exvito/b8298f25196d41daf67414f702518f6b to your computer and use it in GitHub Desktop.
Save exvito/b8298f25196d41daf67414f702518f6b to your computer and use it in GitHub Desktop.
Twisted double stopProducer call on HTTPS POST failing certificate validation
# shortened version adapted from:
# https://gist.github.com/jlitzingerdev/2b010b15d4772a41601e400c17c5386e
import datetime
import io
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, asymmetric
from cryptography.hazmat.primitives.serialization import (Encoding,
PrivateFormat,
NoEncryption)
from cryptography import x509
from twisted.web import client
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.resource import Resource
from twisted.internet.ssl import Certificate, KeyPair, PrivateCertificate
def _selfSignedCert(issuerName):
key = asymmetric.rsa.generate_private_key(65537, 2048, default_backend())
privateKeyBytes = key.private_bytes(
Encoding.DER,
PrivateFormat.TraditionalOpenSSL,
NoEncryption()
)
certBuilder = x509.CertificateBuilder().subject_name(
x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, issuerName)])
).issuer_name(
x509.Name([x509.NameAttribute(x509.NameOID.COMMON_NAME, issuerName)])
).public_key(
key.public_key()
).serial_number(
x509.random_serial_number()
).not_valid_before(
datetime.datetime.utcnow()
).not_valid_after(
datetime.datetime.utcnow() + datetime.timedelta(days=1)
)
cert = certBuilder.sign(key, hashes.SHA256(), default_backend())
return PrivateCertificate.fromCertificateAndKeyPair(
Certificate.loadPEM(cert.public_bytes(Encoding.PEM)),
KeyPair.load(privateKeyBytes)
)
if __name__ == '__main__':
# minimal HTTP server responding to POST
class TestResource(Resource):
isLeaf = True
def render_POST(self, request):
return b'POST response body'
# using self-signed cert for TLS
cf = _selfSignedCert(u'name').options()
reactor.listenSSL(8080, Site(TestResource()), cf, interface='127.0.0.1')
# Agent POST request will fail TLS certificate validation and,
# in turn, lead to two stopProducing() calls on FileBodyProduer which
# fails the second time around with a twisted.internet.task.TaskStopped
# exception.
agent = client.Agent(reactor)
d = agent.request(
b'POST',
b'https://localhost:8080/',
bodyProducer=client.FileBodyProducer(io.BytesIO(b'post data'))
)
d.addCallback(lambda r: print('ok: {!r}'.format(r)))
d.addErrback(lambda f: print('ups: {!r}'.format(f)))
d.addCallback(lambda _: reactor.stop())
reactor.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment