发布于 2015-08-30 08:02:11 | 188 次阅读 | 评论: 0 | 来源: 网络整理

问题

You want to implement a network service involving sockets where servers and clients authenticate themselves and encrypt the transmitted data using SSL.


解决方案

The ssl module provides support for adding SSL to low-level socket connections. In particular, the ssl.wrap_socket() function takes an existing socket and wraps an SSL layer around it. For example, here’s an example of a simple echo server that presents a server certificate to connecting clients:

from socket import socket, AF_INET, SOCK_STREAM import ssl

KEYFILE = ‘server_key.pem’ # Private key of the server CERTFILE = ‘server_cert.pem’ # Server certificate (given to client)

def echo_client(s):
while True:

data = s.recv(8192) if data == b’‘:

break

s.send(data)

s.close() print(‘Connection closed’)

def echo_server(address):

s = socket(AF_INET, SOCK_STREAM) s.bind(address) s.listen(1)

# Wrap with an SSL layer requiring client certs s_ssl = ssl.wrap_socket(s,

keyfile=KEYFILE, certfile=CERTFILE, server_side=True )

# Wait for connections while True:

try:
c,a = s_ssl.accept() print(‘Got connection’, c, a) echo_client(c)
except Exception as e:
print(‘{}: {}’.format(e.__class__.__name__, e))

echo_server((‘’, 20000))

Here’s an interactive session that shows how to connect to the server as a client. The client requires the server to present its certificate and verifies it:

>>> from socket import socket, AF_INET, SOCK_STREAM
>>> import ssl
>>> s = socket(AF_INET, SOCK_STREAM)
>>> s_ssl = ssl.wrap_socket(s,
...                         cert_reqs=ssl.CERT_REQUIRED,
...                         ca_certs = 'server_cert.pem')
>>> s_ssl.connect(('localhost', 20000))
>>> s_ssl.send(b'Hello World?')
12
>>> s_ssl.recv(8192)
b'Hello World?'
>>>

The problem with all of this low-level socket hacking is that it doesn’t play well with existing network services already implemented in the standard library. For example, most server code (HTTP, XML-RPC, etc.) is actually based on the socketserver library. Client code is also implemented at a higher level. It is possible to add SSL to existing services, but a slightly different approach is needed. First, for servers, SSL can be added through the use of a mixin class like this:

import ssl

class SSLMixin:

‘’’ Mixin class that adds support for SSL to existing servers based on the socketserver module. ‘’’ def __init__(self, *args,

keyfile=None, certfile=None, ca_certs=None, cert_reqs=ssl.NONE, **kwargs):

self._keyfile = keyfile self._certfile = certfile self._ca_certs = ca_certs self._cert_reqs = cert_reqs super().__init__(*args, **kwargs)

def get_request(self):

client, addr = super().get_request() client_ssl = ssl.wrap_socket(client,

keyfile = self._keyfile, certfile = self._certfile, ca_certs = self._ca_certs, cert_reqs = self._cert_reqs, server_side = True)

return client_ssl, addr

To use this mixin class, you can mix it with other server classes. For example, here’s an example of defining an XML-RPC server that operates over SSL:

# XML-RPC server with SSL

from xmlrpc.server import SimpleXMLRPCServer

class SSLSimpleXMLRPCServer(SSLMixin, SimpleXMLRPCServer):
pass

Here’s the XML-RPC server from Recipe 11.6 modified only slightly to use SSL:

import ssl from xmlrpc.server import SimpleXMLRPCServer from sslmixin import SSLMixin

class SSLSimpleXMLRPCServer(SSLMixin, SimpleXMLRPCServer):
pass
class KeyValueServer:

_rpc_methods_ = [‘get’, ‘set’, ‘delete’, ‘exists’, ‘keys’] def __init__(self, *args, **kwargs):

self._data = {} self._serv = SSLSimpleXMLRPCServer(*args, allow_none=True, **kwargs) for name in self._rpc_methods_:

self._serv.register_function(getattr(self, name))
def get(self, name):
return self._data[name]
def set(self, name, value):
self._data[name] = value
def delete(self, name):
del self._data[name]
def exists(self, name):
return name in self._data
def keys(self):
return list(self._data)
def serve_forever(self):
self._serv.serve_forever()
if __name__ == ‘__main__’:

KEYFILE=’server_key.pem’ # Private key of the server CERTFILE=’server_cert.pem’ # Server certificate kvserv = KeyValueServer((‘’, 15000),

keyfile=KEYFILE, certfile=CERTFILE),

kvserv.serve_forever()

To use this server, you can connect using the normal xmlrpc.client module. Just spec‐ ify a https: in the URL. For example:

>>> from xmlrpc.client import ServerProxy
>>> s = ServerProxy('https://localhost:15000', allow_none=True)
>>> s.set('foo','bar')
>>> s.set('spam', [1, 2, 3])
>>> s.keys()
['spam', 'foo']
>>> s.get('foo')
'bar'
>>> s.get('spam')
[1, 2, 3]
>>> s.delete('spam')
>>> s.exists('spam')
False
>>>

One complicated issue with SSL clients is performing extra steps to verify the server certificate or to present a server with client credentials (such as a client certificate). Unfortunately, there seems to be no standardized way to accomplish this, so research is often required. However, here is an example of how to set up a secure XML-RPC con‐ nection that verifies the server’s certificate:

from xmlrpc.client import SafeTransport, ServerProxy import ssl

class VerifyCertSafeTransport(SafeTransport):
def __init__(self, cafile, certfile=None, keyfile=None):

SafeTransport.__init__(self) self._ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) self._ssl_context.load_verify_locations(cafile) if cert:

self._ssl_context.load_cert_chain(certfile, keyfile)

self._ssl_context.verify_mode = ssl.CERT_REQUIRED

def make_connection(self, host):

# Items in the passed dictionary are passed as keyword # arguments to the http.client.HTTPSConnection() constructor. # The context argument allows an ssl.SSLContext instance to # be passed with information about the SSL configuration s = super().make_connection((host, {‘context’: self._ssl_context}))

return s

# Create the client proxy s = ServerProxy(‘https://localhost:15000‘,

transport=VerifyCertSafeTransport(‘server_cert.pem’), allow_none=True)

As shown, the server presents a certificate to the client and the client verifies it. This verification can go both directions. If the server wants to verify the client, change the server startup to the following: if __name__ == ‘__main__’:

KEYFILE=’server_key.pem’ # Private key of the server CERTFILE=’server_cert.pem’ # Server certificate CA_CERTS=’client_cert.pem’ # Certificates of accepted clients

kvserv = KeyValueServer((‘’, 15000),
keyfile=KEYFILE, certfile=CERTFILE, ca_certs=CA_CERTS, cert_reqs=ssl.CERT_REQUIRED, )

kvserv.serve_forever()

To make the XML-RPC client present its certificates, change the ServerProxy initiali‐ zation to this:

# Create the client proxy s = ServerProxy(‘https://localhost:15000‘,

transport=VerifyCertSafeTransport(‘server_cert.pem’,
‘client_cert.pem’, ‘client_key.pem’),

allow_none=True)


讨论

Getting this recipe to work will test your system configuration skills and understanding of SSL. Perhaps the biggest challenge is simply getting the initial configuration of keys, certificates, and other matters in order. To clarify what’s required, each endpoint of an SSL connection typically has a private key and a signed certificate file. The certificate file contains the public key and is pre‐ sented to the remote peer on each connection. For public servers, certificates are nor‐ mally signed by a certificate authority such as Verisign, Equifax, or similar organization (something that costs money). To verify server certificates, clients maintain a file con‐ taining the certificates of trusted certificate authorities. For example, web browsers maintain certificates corresponding to the major certificate authorities and use them to verify the integrity of certificates presented by web servers during HTTPS connections. For the purposes of this recipe, you can create what’s known as a self-signed certificate. Here’s how you do it:

bash % openssl req -new -x509 -days 365 -nodes -out server_cert.pem
-keyout server_key.pem

Generating a 1024 bit RSA private key ..........................................++++++ ...++++++

writing new private key to ‘server_key.pem’

You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter ‘.’, the field will be left blank.

Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:Illinois Locality Name (eg, city) []:Chicago Organization Name (eg, company) [Internet Widgits Pty Ltd]:Dabeaz, LLC Organizational Unit Name (eg, section) []: Common Name (eg, YOUR name) []:localhost Email Address []: bash %

When creating the certificate, the values for the various fields are often arbitrary. How‐ ever, the “Common Name” field often contains the DNS hostname of servers. If you’re just testing things out on your own machine, use “localhost.” Otherwise, use the domain name of the machine that’s going to run the server. As a result of this configuration, you will have a server_key.pem file that contains the private key. It looks like this:

—–BEGIN RSA PRIVATE KEY—– MIICXQIBAAKBgQCZrCNLoEyAKF+f9UNcFaz5Osa6jf7qkbUl8si5xQrY3ZYC7juu nL1dZLn/VbEFIITaUOgvBtPv1qUWTJGwga62VSG1oFE0ODIx3g2Nh4sRf+rySsx2 L4442nx0z4O5vJQ7k6eRNHAZUUnCL50+YvjyLyt7ryLSjSuKhCcJsbZgPwIDAQAB AoGAB5evrr7eyL4160tM5rHTeATlaLY3UBOe5Z8XN8Z6gLiB/ucSX9AysviVD/6F 3oD6z2aL8jbeJc1vHqjt0dC2dwwm32vVl8mRdyoAsQpWmiqXrkvP4Bsl04VpBeHw Qt8xNSW9SFhceL3LEvw9M8i9MV39viih1ILyH8OuHdvJyFECQQDLEjl2d2ppxND9 PoLqVFAirDfX2JnLTdWbc+M11a9Jdn3hKF8TcxfEnFVs5Gav1MusicY5KB0ylYPb YbTvqKc7AkEAwbnRBO2VYEZsJZp2X0IZqP9ovWokkpYx+PE4+c6MySDgaMcigL7v WDIHJG1CHudD09GbqENasDzyb2HAIW4CzQJBAKDdkv+xoW6gJx42Auc2WzTcUHCA eXR/+BLpPrhKykzbvOQ8YvS5W764SUO1u1LWs3G+wnRMvrRvlMCZKgggBjkCQQCG Jewto2+a+WkOKQXrNNScCDE5aPTmZQc5waCYq4UmCZQcOjkUOiN3ST1U5iuxRqfb V/yX6fw0qh+fLWtkOs/JAkA+okMSxZwqRtfgOFGBfwQ8/iKrnizeanTQ3L6scFXI CHZXdJ3XQ6qUmNxNn7iJ7S/LDawo1QfWkCfD9FYoxBlg —–END RSA PRIVATE KEY—–

The server certificate in server_cert.pem looks similar:

—–BEGIN CERTIFICATE—– MIIC+DCCAmGgAwIBAgIJAPMd+vi45js3MA0GCSqGSIb3DQEBBQUAMFwxCzAJBgNV BAYTAlVTMREwDwYDVQQIEwhJbGxpbm9pczEQMA4GA1UEBxMHQ2hpY2FnbzEUMBIG A1UEChMLRGFiZWF6LCBMTEMxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMzAxMTEx ODQyMjdaFw0xNDAxMTExODQyMjdaMFwxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhJ bGxpbm9pczEQMA4GA1UEBxMHQ2hpY2FnbzEUMBIGA1UEChMLRGFiZWF6LCBMTEMx EjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA mawjS6BMgChfn/VDXBWs+TrGuo3+6pG1JfLIucUK2N2WAu47rpy9XWS5/1WxBSCE 2lDoLwbT79alFkyRsIGutlUhtaBRNDgyMd4NjYeLEX/q8krMdi+OONp8dM+DubyU

O5OnkTRwGVFJwi+dPmL48i8re68i0o0rioQnCbG2YD8CAwEAAaOBwTCBvjAdBgNV HQ4EFgQUrtoLHHgXiDZTr26NMmgKJLJLFtIwgY4GA1UdIwSBhjCBg4AUrtoLHHgX iDZTr26NMmgKJLJLFtKhYKReMFwxCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhJbGxp bm9pczEQMA4GA1UEBxMHQ2hpY2FnbzEUMBIGA1UEChMLRGFiZWF6LCBMTEMxEjAQ BgNVBAMTCWxvY2FsaG9zdIIJAPMd+vi45js3MAwGA1UdEwQFMAMBAf8wDQYJKoZI hvcNAQEFBQADgYEAFci+dqvMG4xF8UTnbGVvZJPIzJDRee6Nbt6AHQo9pOdAIMAu WsGCplSOaDNdKKzl+b2UT2Zp3AIW4Qd51bouSNnR4M/gnr9ZD1ZctFd3jS+C5XRp D3vvcW5lAnCCC80P6rXy7d7hTeFu5EYKtRGXNvVNd/06NALGDflrrOwxF3Y= —–END CERTIFICATE—–

In server-related code, both the private key and certificate file will be presented to the various SSL-related wrapping functions. The certificate is what gets presented to clients. The private key should be protected and remains on the server. In client-related code, a special file of valid certificate authorities needs to be maintained to verify the server’s certificate. If you have no such file, then at the very least, you can put a copy of the server’s certificate on the client machine and use that as a means for verification. During connection, the server will present its certificate, and then you’ll use the stored certificate you already have to verify that it’s correct. Servers can also elect to verify the identity of clients. To do that, clients need to have their own private key and certificate key. The server would also need to maintain a file of trusted certificate authorities for verifying the client certificates. If you intend to add SSL support to a network service for real, this recipe really only gives a small taste of how to set it up. You will definitely want to consult the documen‐ tation for more of the finer points. Be prepared to spend a significant amount of time experimenting with it to get things to work.

最新网友评论  共有(0)条评论 发布评论 返回顶部

Copyright © 2007-2017 PHPERZ.COM All Rights Reserved   冀ICP备14009818号  版权声明  广告服务