diff options
author | Paul Kehrer <paul.l.kehrer@gmail.com> | 2016-03-13 09:11:16 -0400 |
---|---|---|
committer | Paul Kehrer <paul.l.kehrer@gmail.com> | 2016-03-13 09:11:16 -0400 |
commit | 84c194aa64048880fba30152910947f8f4054902 (patch) | |
tree | 2fbf7eee1f84abc40e02526a727cab3d07049683 | |
parent | 9c756eacc062c843d96256eb6dec6022a8a4c684 (diff) | |
parent | 8b7e455b8603248dccb115ee4cd381ebc6434f20 (diff) | |
download | pyopenssl-84c194aa64048880fba30152910947f8f4054902.tar.gz |
Merge pull request #440 from hynek/examples
Polish up examples (somewhat)
-rw-r--r-- | doc/index.rst | 2 | ||||
-rw-r--r-- | examples/README | 48 | ||||
-rw-r--r-- | examples/README.rst | 56 | ||||
-rw-r--r-- | examples/SecureXMLRPCServer.py | 43 | ||||
-rw-r--r-- | examples/certgen.py | 7 | ||||
-rw-r--r-- | examples/mk_simple_certs.py | 48 | ||||
-rw-r--r-- | examples/proxy.py | 11 | ||||
-rw-r--r-- | examples/simple/client.py | 13 | ||||
-rw-r--r-- | examples/simple/server.py | 34 | ||||
-rw-r--r-- | examples/sni/client.py | 9 | ||||
-rw-r--r-- | examples/sni/server.py | 11 | ||||
-rw-r--r-- | tox.ini | 2 |
12 files changed, 182 insertions, 102 deletions
diff --git a/doc/index.rst b/doc/index.rst index 56d84ea..d4c5e2e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -20,6 +20,8 @@ Contents: api internals +There are also `examples in the pyOpenSSL repository <https://github.com/pyca/pyopenssl/tree/master/examples>`_ that may help you getting started. + Meta ---- diff --git a/examples/README b/examples/README deleted file mode 100644 index ecf95e5..0000000 --- a/examples/README +++ /dev/null @@ -1,48 +0,0 @@ -I've finally gotten around to writing some examples :-) - -They aren't many, but at least it's something. If you write any, feel free to -send them to me and I will add themn. - - -certgen.py - Certificate generation module -========================================== - -Example module with three functions: - createKeyPair - Create a public/private key pair - createCertRequest - Create a certificate request - createCertificate - Create a certificate given a cert request -In fact, I created the certificates and keys in the 'simple' directory with -the script mk_simple_certs.py - - -simple - Simple client/server example -===================================== - -Start the server with - python server.py PORT -and start clients with - python client.py HOST PORT - -The server is a simple echo server, anything a client sends, it sends back. - - -proxy.py - Example of an SSL-enabled proxy -========================================== - -The proxy example demonstrate how to use set_connect_state to start -talking SSL over an already connected socket. - -Usage: python proxy.py server[:port] proxy[:port] - -Contributed by Mihai Ibanescu - - -SecureXMLRPCServer.py - SSL-enabled version of SimpleXMLRPCServer -================================================================= - -This acts exactly like SimpleXMLRPCServer from the standard python library, -but uses secure connections. The technique and classes should work for any -SocketServer style server. However, the code has not been extensively tested. - -Contributed by Michal Wallace - diff --git a/examples/README.rst b/examples/README.rst new file mode 100644 index 0000000..1e9116b --- /dev/null +++ b/examples/README.rst @@ -0,0 +1,56 @@ +======== +Examples +======== + + +certgen.py -- Certificate generation module +=========================================== + +Example module with three functions: + +createKeyPair + Create a public/private key pair. + +createCertRequest + Create a certificate request. + +createCertificate + Create a certificate given a cert request. + +In fact, I created the certificates and keys in the 'simple' directory with the script ``mk_simple_certs.py``. + + +simple -- Simple client/server example +====================================== + +Start the server with:: + + python server.py PORT + +and start clients with:: + + python client.py HOST PORT + +The server is a simple echo server, anything a client sends, it sends back. + + +proxy.py -- Example of an SSL-enabled proxy +=========================================== + +The proxy example demonstrate how to use set_connect_state to start talking SSL over an already connected socket. + +Usage:: + + python proxy.py server[:port] proxy[:port] + +Contributed by Mihai Ibanescu + + +SecureXMLRPCServer.py -- SSL-enabled version of SimpleXMLRPCServer +================================================================== + +Acts exactly like `SimpleXMLRPCServer <https://docs.python.org/3/library/xmlrpc.server.html>`_ from the Python standard library, but uses secure connections. +The technique and classes should work for any SocketServer style server. +However, the code has not been extensively tested. + +Contributed by Michal Wallace diff --git a/examples/SecureXMLRPCServer.py b/examples/SecureXMLRPCServer.py index 757b49c..56bfaea 100644 --- a/examples/SecureXMLRPCServer.py +++ b/examples/SecureXMLRPCServer.py @@ -14,11 +14,15 @@ been extensively tested. This code is in the public domain. It is provided AS-IS WITH NO WARRANTY WHATSOEVER. """ -import SocketServer -import os, socket + import SimpleXMLRPCServer +import SocketServer +import os +import socket + from OpenSSL import SSL + class SSLWrapper: """ This whole class exists just to filter out a parameter @@ -30,10 +34,13 @@ class SSLWrapper: so I'm making a proxy instead of subclassing. """ self.__dict__["conn"] = conn - def __getattr__(self,name): + + def __getattr__(self, name): return getattr(self.__dict__["conn"], name) - def __setattr__(self,name, value): + + def __setattr__(self, name, value): setattr(self.__dict__["conn"], name, value) + def shutdown(self, how=1): """ SimpleXMLRpcServer.doPOST calls shutdown(1), @@ -41,6 +48,7 @@ class SSLWrapper: an argument. So we just discard the argument. """ self.__dict__["conn"].shutdown() + def accept(self): """ This is the other part of the shutdown() workaround. @@ -51,42 +59,48 @@ class SSLWrapper: return (SSLWrapper(c), a) - class SecureTCPServer(SocketServer.TCPServer): """ Just like TCPServer, but use a socket. This really ought to let you specify the key and certificate files. """ def __init__(self, server_address, RequestHandlerClass): - SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass) + SocketServer.BaseServer.__init__( + self, server_address, RequestHandlerClass + ) - ## Same as normal, but make it secure: + # Same as normal, but make it secure: ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.set_options(SSL.OP_NO_SSLv2) dir = os.curdir - ctx.use_privatekey_file (os.path.join(dir, 'server.pkey')) + ctx.use_privatekey_file(os.path.join(dir, 'server.pkey')) ctx.use_certificate_file(os.path.join(dir, 'server.cert')) - self.socket = SSLWrapper(SSL.Connection(ctx, socket.socket(self.address_family, - self.socket_type))) + self.socket = SSLWrapper( + SSL.Connection( + ctx, socket.socket(self.address_family, self.socket_type) + ) + ) self.server_bind() self.server_activate() -class SecureXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): +class SecureXMLRPCRequestHandler( + SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): def setup(self): """ We need to use socket._fileobject Because SSL.Connection doesn't have a 'dup'. Not exactly sure WHY this is, but this is backed up by comments in socket.py and SSL/connection.c """ - self.connection = self.request # for doPOST + self.connection = self.request # for doPOST self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) - -class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, SecureTCPServer): + +class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, + SecureTCPServer): def __init__(self, addr, requestHandler=SecureXMLRPCRequestHandler, logRequests=1): @@ -99,4 +113,3 @@ class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, SecureTCPServer) self.logRequests = logRequests self.instance = None SecureTCPServer.__init__(self, addr, requestHandler) - diff --git a/examples/certgen.py b/examples/certgen.py index da0624f..7b70e98 100644 --- a/examples/certgen.py +++ b/examples/certgen.py @@ -13,6 +13,7 @@ from OpenSSL import crypto TYPE_RSA = crypto.TYPE_RSA TYPE_DSA = crypto.TYPE_DSA + def createKeyPair(type, bits): """ Create a public/private key pair. @@ -25,6 +26,7 @@ def createKeyPair(type, bits): pkey.generate_key(type, bits) return pkey + def createCertRequest(pkey, digest="sha256", **name): """ Create a certificate request. @@ -52,7 +54,9 @@ def createCertRequest(pkey, digest="sha256", **name): req.sign(pkey, digest) return req -def createCertificate(req, issuerCertKey, serial, validityPeriod, digest="sha256"): + +def createCertificate(req, issuerCertKey, serial, validityPeriod, + digest="sha256"): """ Generate a certificate given a certificate request. @@ -78,4 +82,3 @@ def createCertificate(req, issuerCertKey, serial, validityPeriod, digest="sha256 cert.set_pubkey(req.get_pubkey()) cert.sign(issuerKey, digest) return cert - diff --git a/examples/mk_simple_certs.py b/examples/mk_simple_certs.py index 7129f13..f0416cd 100644 --- a/examples/mk_simple_certs.py +++ b/examples/mk_simple_certs.py @@ -2,27 +2,49 @@ Create certificates and private keys for the 'simple' example. """ +from __future__ import print_function + from OpenSSL import crypto -from certgen import * # yes yes, I know, I'm lazy -cakey = createKeyPair(TYPE_RSA, 2048) +from certgen import ( + createKeyPair, + createCertRequest, + createCertificate, +) + +cakey = createKeyPair(crypto.TYPE_RSA, 2048) careq = createCertRequest(cakey, CN='Certificate Authority') -cacert = createCertificate(careq, (careq, cakey), 0, (0, 60*60*24*365*5)) # five years +# CA certificate is valid for five years. +cacert = createCertificate(careq, (careq, cakey), 0, (0, 60*60*24*365*5)) print('Creating Certificate Authority private key in "simple/CA.pkey"') with open('simple/CA.pkey', 'w') as capkey: - capkey.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, cakey).decode('utf-8')) + capkey.write( + crypto.dump_privatekey(crypto.FILETYPE_PEM, cakey).decode('utf-8') + ) + print('Creating Certificate Authority certificate in "simple/CA.cert"') with open('simple/CA.cert', 'w') as ca: - ca.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cacert).decode('utf-8')) + ca.write( + crypto.dump_certificate(crypto.FILETYPE_PEM, cacert).decode('utf-8') + ) -for (fname, cname) in [('client', 'Simple Client'), ('server', 'Simple Server')]: - pkey = createKeyPair(TYPE_RSA, 2048) +for (fname, cname) in [('client', 'Simple Client'), + ('server', 'Simple Server')]: + pkey = createKeyPair(crypto.TYPE_RSA, 2048) req = createCertRequest(pkey, CN=cname) - cert = createCertificate(req, (cacert, cakey), 1, (0, 60*60*24*365*5)) # five years - print('Creating Certificate %s private key in "simple/%s.pkey"' % (fname, fname)) + # Certificates are valid for five years. + cert = createCertificate(req, (cacert, cakey), 1, (0, 60*60*24*365*5)) + + print('Creating Certificate %s private key in "simple/%s.pkey"' + % (fname, fname)) with open('simple/%s.pkey' % (fname,), 'w') as leafpkey: - leafpkey.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey).decode('utf-8')) - print('Creating Certificate %s certificate in "simple/%s.cert"' % (fname, fname)) - with open('simple/%s.cert' % (fname,), 'w') as leafcert: - leafcert.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode('utf-8')) + leafpkey.write( + crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey).decode('utf-8') + ) + print('Creating Certificate %s certificate in "simple/%s.cert"' + % (fname, fname)) + with open('simple/%s.cert' % (fname,), 'w') as leafcert: + leafcert.write( + crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode('utf-8') + ) diff --git a/examples/proxy.py b/examples/proxy.py index b1c4253..3be26f9 100644 --- a/examples/proxy.py +++ b/examples/proxy.py @@ -8,15 +8,20 @@ # # $Id: proxy.py,v 1.2 2004/07/22 12:01:25 martin Exp $ -import sys, socket, string +import sys +import socket +import string + from OpenSSL import SSL + def usage(exit_code=0): print "Usage: %s server[:port] proxy[:port]" % sys.argv[0] print " Connects SSL to the specified server (port 443 by default)" print " using the specified proxy (port 8080 by default)" sys.exit(exit_code) + def main(): # Command-line processing if len(sys.argv) != 3: @@ -26,12 +31,13 @@ def main(): run(split_host(server, 443), split_host(proxy, 8080)) + def split_host(hostname, default_port=80): a = string.split(hostname, ':', 1) if len(a) == 1: a.append(default_port) return a[0], int(a[1]) - + # Connects to the server, through the proxy def run(server, proxy): @@ -66,5 +72,6 @@ def run(server, proxy): print buff, + if __name__ == '__main__': main() diff --git a/examples/simple/client.py b/examples/simple/client.py index dff2d03..5662122 100644 --- a/examples/simple/client.py +++ b/examples/simple/client.py @@ -8,8 +8,12 @@ Simple SSL client, using blocking I/O """ +import os +import socket +import sys + from OpenSSL import SSL, crypto -import sys, os, select, socket + def verify_cb(conn, cert, errnum, depth, ok): certsubject = crypto.X509Name(cert.get_subject()) @@ -17,20 +21,23 @@ def verify_cb(conn, cert, errnum, depth, ok): print('Got certificate: ' + commonname) return ok + if len(sys.argv) < 3: print('Usage: python client.py HOST PORT') sys.exit(1) + dir = os.path.dirname(sys.argv[0]) if dir == '': dir = os.curdir + # Initialize context ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.set_options(SSL.OP_NO_SSLv2) ctx.set_options(SSL.OP_NO_SSLv3) -ctx.set_verify(SSL.VERIFY_PEER, verify_cb) # Demand a certificate -ctx.use_privatekey_file (os.path.join(dir, 'client.pkey')) +ctx.set_verify(SSL.VERIFY_PEER, verify_cb) # Demand a certificate +ctx.use_privatekey_file(os.path.join(dir, 'client.pkey')) ctx.use_certificate_file(os.path.join(dir, 'client.cert')) ctx.load_verify_locations(os.path.join(dir, 'CA.cert')) diff --git a/examples/simple/server.py b/examples/simple/server.py index bc001ed..19f6d21 100644 --- a/examples/simple/server.py +++ b/examples/simple/server.py @@ -8,16 +8,23 @@ Simple echo server, using nonblocking I/O """ +from __future__ import print_function + +import os +import select +import socket +import sys + from OpenSSL import SSL, crypto -import sys, os, select, socket def verify_cb(conn, cert, errnum, depth, ok): certsubject = crypto.X509Name(cert.get_subject()) commonname = certsubject.commonName - print(('Got certificate: ' + commonname)) + print('Got certificate: ' + commonname) return ok + if len(sys.argv) < 2: print('Usage: python server.py PORT') sys.exit(1) @@ -30,20 +37,23 @@ if dir == '': ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.set_options(SSL.OP_NO_SSLv2) ctx.set_options(SSL.OP_NO_SSLv3) -ctx.set_verify(SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb) # Demand a certificate -ctx.use_privatekey_file (os.path.join(dir, 'server.pkey')) +ctx.set_verify( + SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb +) # Demand a certificate +ctx.use_privatekey_file(os.path.join(dir, 'server.pkey')) ctx.use_certificate_file(os.path.join(dir, 'server.cert')) ctx.load_verify_locations(os.path.join(dir, 'CA.cert')) # Set up server server = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM)) server.bind(('', int(sys.argv[1]))) -server.listen(3) +server.listen(3) server.setblocking(0) clients = {} writers = {} + def dropClient(cli, errors=None): if errors: print('Client %s left unexpectedly:' % (clients[cli],)) @@ -59,20 +69,24 @@ def dropClient(cli, errors=None): while 1: try: - r, w, _ = select.select([server] + list(clients.keys()), list(writers.keys()), []) + r, w, _ = select.select( + [server] + list(clients.keys()), list(writers.keys()), [] + ) except: break for cli in r: if cli == server: - cli,addr = server.accept() + cli, addr = server.accept() print('Connection from %s' % (addr,)) clients[cli] = addr else: try: ret = cli.recv(1024).decode('utf-8') - except (SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError): + except (SSL.WantReadError, + SSL.WantWriteError, + SSL.WantX509LookupError): pass except SSL.ZeroReturnError: dropClient(cli) @@ -86,7 +100,9 @@ while 1: for cli in w: try: ret = cli.send(writers[cli]) - except (SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError): + except (SSL.WantReadError, + SSL.WantWriteError, + SSL.WantX509LookupError): pass except SSL.ZeroReturnError: dropClient(cli) diff --git a/examples/sni/client.py b/examples/sni/client.py index 5b93671..428525b 100644 --- a/examples/sni/client.py +++ b/examples/sni/client.py @@ -1,15 +1,12 @@ # Copyright (C) Jean-Paul Calderone # See LICENSE for details. -if __name__ == '__main__': - import client - raise SystemExit(client.main()) - from sys import argv, stdout from socket import socket from OpenSSL.SSL import TLSv1_METHOD, Context, Connection + def main(): """ Connect to an SNI-enabled server and request a specific hostname, specified @@ -33,3 +30,7 @@ def main(): print 'Server subject is', client_ssl.get_peer_certificate().get_subject() client_ssl.close() + +if __name__ == '__main__': + import client + raise SystemExit(client.main()) diff --git a/examples/sni/server.py b/examples/sni/server.py index 8738416..e0c159a 100644 --- a/examples/sni/server.py +++ b/examples/sni/server.py @@ -1,16 +1,13 @@ # Copyright (C) Jean-Paul Calderone # See LICENSE for details. -if __name__ == '__main__': - import server - raise SystemExit(server.main()) - from sys import stdout from socket import SOL_SOCKET, SO_REUSEADDR, socket from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, load_certificate from OpenSSL.SSL import TLSv1_METHOD, Context, Connection + def load(domain): crt = open(domain + ".crt") key = open(domain + ".key") @@ -49,7 +46,7 @@ def main(): certificates = { "example.invalid": load("example.invalid"), "another.invalid": load("another.invalid"), - } +} def pick_certificate(connection): @@ -62,3 +59,7 @@ def pick_certificate(connection): new_context.use_privatekey(key) new_context.use_certificate(cert) connection.set_context(new_context) + + +if __name__ == '__main__': + raise SystemExit(main()) @@ -38,7 +38,7 @@ deps = flake8 skip_install = true commands = - flake8 src tests setup.py + flake8 src tests examples setup.py [testenv:pypi-readme] deps = |