Python with SSL Library(OpenSSL, ssl)

最後更新: 2020-02-10

 


證書 - OpenSSL Class

 

#!/bin/usr/env python

import ssl
import M2Crypto
import OpenSSL

# Get SSL
# The call will attempt to validate the server certificate against that set of root certificates, 
# and will fail if the validation attempt fails.
cert = ssl.get_server_certificate(('www.google.com', 443))

# M2Crypto
x509 = M2Crypto.X509.load_cert_string(cert)
print x509.get_subject().as_text()

# Output
# 'C=US, ST=California, L=Mountain View, O=Google Inc, CN=www.google.com'

# OpenSSL
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
print x509.get_subject().get_components()

# Output
#[('C', 'US'),
# ('ST', 'California'),
# ('L', 'Mountain View'),
# ('O', 'Google Inc'),
# ('CN', 'www.google.com')]

P.S.

以上 Example 有機會出現以下 Error, 原因係 TLS 用戶端不支援 SNI

OU=No SNI provided; please fix your client., CN=invalid2.invalid

 


設定 SNI

 

獲得證書

行用 class ssl.SSLContext(protocol) 去實現

hostname = "www.google.com"

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

conn = ssl.create_connection((hostname, 443))
sock = context.wrap_socket(conn, server_hostname=hostname)
cert = ssl.DER_cert_to_PEM_cert(sock.getpeercert(True))
x509 = M2Crypto.X509.load_cert_string(cert)
print x509.get_not_after().get_datetime()

PROTOCOL_* constants defined in this module.

ssl.PROTOCOL_TLS

Selects the highest protocol version that both the client and server support.
Despite the name, this option can select “TLS”

SSL HTTP Connection

mySSL_v1.py

#!/usr/bin/env python
# vim: sts=4:ts=4:sw=4:paste:et

import socket, ssl, pprint

ServerName = "datahunter.org"

#context = ssl.create_default_context()
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.verify_mode = ssl.CERT_NONE

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssl_sock = context.wrap_socket(s, server_hostname=ServerName)

ssl_sock.connect((ServerName, 443))

#ssl_sock.sendall("POST / HTTP/1.1\r\n")
ssl_sock.sendall("POST /tim.php HTTP/1.1\r\n")
ssl_sock.sendall("Host: " + ServerName + "\r\n")
ssl_sock.sendall("Content-Type: text/plain" + "\r\n")
#ssl_sock.sendall("Content-Length: 8" + "\r\n")
#ssl_sock.sendall("mytest: 1" + "\r\n")
ssl_sock.sendall("\r\n")
ssl_sock.sendall("testtest" + "\r\n")
ssl_sock.sendall("\r\n")

pprint.pprint(ssl_sock.recv(4096).split("\r\n"))

ssl_sock.close()

只能收到 Header, 沒有收到 Body !!

P.S.

1) 設定 SSL 連線

context = ssl.SSLContext(ssl.PROTOCOL_TLS)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True
context.load_default_certs()

SSLContext.verify_mode

Whether to try to verify other peers’ certificates and how to behave if verification fails.
This attribute must be one of ssl.CERT_NONE, ssl.CERT_OPTIONAL or ssl.CERT_REQUIRED.

2) sendall()

Unlike send(), sendall() method continues to send data from string until
either all data has been sent or an error occurs.
None is returned on success. On error, an exception is raised

close()

releases the resource associated with a connection but does not necessarily close the connection immediately.

If you want to close the connection in a timely fashion, call shutdown() before close().

3) socket.recv(bufsize[, flags])

The maximum amount of data to be received at once is specified by bufsize.(Default: 0)

只收到 Header, 但沒有 Body 的情況

Even if you read only a few bytes from a SSL socket

it needs to read the full SSL record which contains the encrypted data,

decrypt the full record and then it can return the few bytes you've requested.

The ssl_socket.revc(RECV_BUFFER) will at least read as much data from the underlying TCP connection as it needs to have a full SSL record.

Then it will decrypt the SSL record and return at most RECV_BUFFER bytes of decrypted data.

Fix: mySSL_v2.py

...
pprint.pprint(ssl_sock.recv(4096).split("\r\n"))

ssl_socket.pending()

It will tell you if there is still unread decrypt data in the SSL socket.

It will not check if there are data available at the underlying TCP socket.

If there are data still in the SSL socket the next ssl_socket.recv(...) will return from these data

but will not try to read more data from the underlying TCP socket.

Only if there are no more decrypted but unread data are available in the SSL socket

a recv will read more from the underlying TCP socket

(in this case pending will return false so you will never try to read more data.)

 * SSL need to be treated like a data stream and not like a message protocol (same for TCP).

This means you cannot assume that the message gets read in full and

that it will be returned in full or that it is least already in full in the SSL object.

Instead you either need to know the size of the response up-front (like prefixing the response with a length) or

need to have some clear marker that the response has ended and read until this marker.

 


UNSUPPORTED_PROTOCOL

 

ssl.SSLError: [SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:727)
  • ssl.PROTOCOL_TLS
  • ssl.PROTOCOL_TLSv1
  • ssl.PROTOCOL_TLSv1_1
  • ssl.PROTOCOL_TLSv1_2

由 "ssl.PROTOCOL_TLS" 改成 "ssl.PROTOCOL_TLSv1"

context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

 


M2Crypto

 

m2crypto - Python wrapper for the OpenSSL library

pip

pip install m2crypto

# Debain

apt-get install python-m2crypto

x509

x509.get_subject().as_text()
x509.get_not_after().get_datetime()