Skip to content

Instantly share code, notes, and snippets.

@oborichkin
Last active April 11, 2024 14:27
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save oborichkin/d8d0c7823fd6db3abeb25f69352a5299 to your computer and use it in GitHub Desktop.
Save oborichkin/d8d0c7823fd6db3abeb25f69352a5299 to your computer and use it in GitHub Desktop.
Simple TLS client and server on python
import socket
import ssl
from tls_server import HOST as SERVER_HOST
from tls_server import PORT as SERVER_PORT
HOST = "127.0.0.1"
PORT = 60002
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
client = ssl.wrap_socket(client, keyfile="path/to/keyfile", certfile="path/to/certfile")
if __name__ == "__main__":
client.bind((HOST, PORT))
client.connect((SERVER_HOST, SERVER_PORT))
while True:
from time import sleep
client.send("Hello World!".encode("utf-8"))
sleep(1)
import socket
import ssl
HOST = "127.0.0.1"
PORT = 60000
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server = ssl.wrap_socket(
server, server_side=True, keyfile="path/to/keyfile", certfile="path/to/certfile"
)
if __name__ == "__main__":
server.bind((HOST, PORT))
server.listen(0)
while True:
connection, client_address = server.accept()
while True:
data = connection.recv(1024)
if not data:
break
print(f"Received: {data.decode('utf-8')}")
@music-cat-bread
Copy link

music-cat-bread commented Aug 6, 2021

What it does?

if not data:
      break

Copy link

ghost commented Aug 10, 2021

What it does?

if not data:
      break

when client socket is open and there is some data, server.recv(1024) will return data it received
when client socket is open and there is no data, server.recv(1024) will blocked
when client socket is closed, server.recv(1024) will return ""

if not data: break means if client socket is closed; return to server.accept() and prepare to handle next connection

@music-cat-bread
Copy link

Thanks for info =)

@mattbillenstein
Copy link

Handy example, but why does the client bind? If I remove that line, still works...

@music-cat-bread
Copy link

If I remove that line, still works...

Yea, but in some cases your console will get in to infinite loop and if you are gonna have print there... RIP your console/logs.

@NguyenKhue09
Copy link

NguyenKhue09 commented Nov 15, 2021

how can i resolve the error "Import "tls_server" could not be resolved"?

@music-cat-bread
Copy link

music-cat-bread commented Nov 15, 2021

Can you show the code? Some part at lest. And do you have it in difrent dirrectiry? @NguyenKhue09

@NguyenKhue09
Copy link

image

@NguyenKhue09
Copy link

This is my code

@music-cat-bread
Copy link

Did you have tls_server.py in same directory as your client?

@NguyenKhue09
Copy link

No i don't have tls_server.py in the same directory

@music-cat-bread
Copy link

music-cat-bread commented Nov 15, 2021

from some_folder.tls_server import foo
Replace some_folder with relative path to your folder (if you have to go directory back use /../) and replace foo with variable or class you need to import.

@NguyenKhue09
Copy link

tls_server is default file or i must create this file?

@music-cat-bread
Copy link

Bro, you are trying to import variable from that file right? So you have to point this import into a file.
Ley's say you have your tls_server.py in lib directoty that is next to your client file.
Then you have to do from lib.tls_server import HOST as SERVER_HOST

@Hunga9k50doker
Copy link

How can I have the same port, for example, port 3000.
If I want to input a string and the server returns that string, what should I do? Hope you help. Thanks!

@NguyenKhue09
Copy link

NguyenKhue09 commented Nov 23, 2021

@Hunga9k50doker You should use the same method "send" in server code to send a message to client.

@Hunga9k50doker
Copy link

port client and server are 3000. Maybe?

@NguyenKhue09
Copy link

not really, you can use whatever port you want.

@skv02
Copy link

skv02 commented Jan 10, 2022

I’m done implementing TLS protocol ut i need to transfer data using a different protocol (iec6205621) from smart meters (ie client) to the gateway (ie server).
Any idea will be helpful how to use my application protocol to transfer data after setting up a tls connection.

@maryamzainy
Copy link

hello
What is keyfile and certfile and how do you create it?

@NguyenKhue09
Copy link

@music-cat-bread
Copy link

music-cat-bread commented Mar 13, 2022

@NguyenKhue09 Provided really good documentation,
but assuming that you don't know what they are you must
be new in this topic. certfile is path to your X.509
certificate (Or just an SSL certificate). keyfile is a path
to file with NOT encrypted private key (If you don't
know what is that read yourself something about asymmetric
cryptology). Personally I used instruction below. You can
generate a Root CA, Sub CA and Certificate for server
Sorry for it being a little bit messy and some typing errors but
I made it when I still was learning english.
PS Please install OpenSSL and then follow the instruction, also
I preformed it on windows but you should be fine on linux too.

Folder Tree:
CA
|
|- root-ca
|  |- certs ;FOLDER
|  |- crl ;FOLDER
|  |- csr ;FOLDER
|  |- newcerts ;FOLDER
|  |- private ;FOLDER
|  |- index ;FILE
|  |- serial ;FILE
|
|- sub-ca
|  |-- certs ;FOLDER
|  |-- crl ;FOLDER
|  |-- csr ;FOLDER
|  |-- newcerts ;FOLDER
|  |-- private ;FOLDER
|  |-- index ;FILE
|  |-- serial ;FILE
|
|- server
|  |-- certs ;FOLDER
|  |-- crl ;FOLDER
|  |-- csr ;FOLDER
|  |-- newcerts ;FOLDER
|  |-- private ;FOLDER

# Generate random 16 hex into serial
CA: openssl rand -hex 16 > root-ca/serial
CA: openssl rand -hex 16 > sub-ca/serial

===== START COMMENT SECTION =====

# Genrate pair of RSA 4096 Bits keys and x509 certificate for localhost
CA/server/private: openssl req -new -newkey rsa:4096 -days 1825 -nodes -x509 -subj "/CN=localhost" -keyout server.key -out server.crt

# Show server x509 certificate without text file
CA/server/private: openssl x509 -text -in server.crt -noout

===== END COMMENT SECTION =====


===== GENERATING PRiVATE KEYS FOR CA, SUB CA AND SERVER =====
# Generate RSA 4096 Bits Keys encrypted with AES256 for Root And Sub CA Certificates
CA: openssl genrsa -aes256 -out root-ca/private/ca.key 4096
CA: openssl genrsa -aes256 -out sub-ca/private/sub-ca.key 4096
# Generate RSA 2048 Bits Keys for server Certificate
CA: openssl genrsa -out server/private/server.key 2048

===== GENERATING PUBLIC KEYS BASED ON PRIVATE KEYS =====
# Create OpenSSL configuration file for root-ca
CA: echo  > root-ca/root-ca.conf
# Paste that text into above file

++ TEXT START ++
[ca]
#/root/ca/root-ca/root-ca.conf
#see man ca
default_ca                  = CA_default

[CA_default]
# dir                         = 
certs                       = certs
crl_dir                     = crl
new_certs_dir               = newcerts
database                    = index
serial                      = serial
# RANDFILE                    = private/.rand

private_key                 = private/ca.key
certificate                 = certs/ca.crt

crlnumber                   = crlnumber
crl                         = crl/ca.crl
crl_extensions              = crl_ext
default_crl_days            = 1825

default_md                  = sha256

name_opt                    = ca_default
cert_opt                    = ca_default
default_days                = 1825
preserve                    = no
policy                      = policy_strict

[ policy_strict ]
countryName                 = supplied
stateOrProvinceName         = supplied
organizationName            = match
organizationalUnitName      = optional
commonName                  = supplied
emailAddress                = optional

[ policy_loose ]
countryName                 = optional
stateOrProvinceName         = optional
localityName                = optional
organizationName            = optional
organizationalUnitName      = optional
commonName                  = supplied
emailAddress                = optional

[ req ]
# Options for the req tool, man req.
default_bits                = 2048
distinguished_name          = req_distinguished_name
string_mask                 = utf8only
default_md                  = sha256
# Extension to add when the -x509 option is used.
x509_extensions             = v3_ca

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
stateOrProvinceName         = State or Province Name
localityName                = Locality Name
0.organizationName          = Organization Name
organizationalUnitName      = Organizational Unit Name
commonName                  = Common Name
emailAddress                = Email Address
countryName_default         = PL
stateOrProvinceName_default = Mazowsze
0.organizationName_default  = c1vDev

[ v3_ca ]
# Extensions to apply when createing root ca
# Extensions for a typical CA, man x509v3_config
subjectKeyIdentifier        = hash
authorityKeyIdentifier      = keyid:always,issuer
basicConstraints            = critical, CA:true
keyUsage                    = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions to apply when creating intermediate or sub-ca
# Extensions for a typical intermediate CA, same man as above
subjectKeyIdentifier        = hash
authorityKeyIdentifier      = keyid:always,issuer
#pathlen:0 ensures no more sub-ca can be created below an intermediate
basicConstraints            = critical, CA:true, pathlen:0
keyUsage                    = critical, digitalSignature, cRLSign, keyCertSign

[ server_cert ]
# Extensions for server certificates
basicConstraints            = CA:FALSE
nsCertType                  = server
nsComment                   = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier        = hash
authorityKeyIdentifier      = keyid,issuer:always
keyUsage                    = critical, digitalSignature, keyEncipherment
extendedKeyUsage            = serverAuth
++ TEXT END ++

# Create OpenSSL CA ROOT AUTHoriTy
CA/root-ca: openssl req -config root-ca.conf -key private/ca.key -new -x509 -days 1825 -sha256 -extensions v3_ca -out certs/ca.crt
# Show ROOT certificate
CA/root-ca: openssl x509 -noout -in certs/ca.crt -text

===== GENERATING INERMINADE CERTIFICATE =====
# Create OpenSSL configuration file for root-ca
CA: echo  > sub-ca/sub-ca.conf
# Paste that text into above file

++ TEXT START ++
[ca]
#/sub/ca/sub-ca/sub-ca.conf
#see man ca
default_ca                  = CA_default

[CA_default]
# dir                         = /
certs                       = certs
crl_dir                     = crl
new_certs_dir               = newcerts
database                    = index
serial                      = serial
# RANDFILE                    = private/.rand

private_key                 = private/sub-ca.key
certificate                 = certs/sub-ca.crt

crlnumber                   = crlnumber
crl                         = crl/sub-ca.crl
crl_extensions              = crl_ext
default_crl_days            = 1825

default_md                  = sha256

name_opt                    = ca_default
cert_opt                    = ca_default
default_days                = 1825
preserve                    = no
policy                      = policy_strict

[ policy_strict ]
countryName                 = supplied
stateOrProvinceName         = supplied
organizationName            = match
organizationalUnitName      = optional
commonName                  = supplied
emailAddress                = optional

[ policy_loose ]
countryName                 = optional
stateOrProvinceName         = optional
localityName                = optional
organizationName            = optional
organizationalUnitName      = optional
commonName                  = supplied
emailAddress                = optional

[ req ]
# Options for the req tool, man req.
default_bits                = 2048
distinguished_name          = req_distinguished_name
string_mask                 = utf8only
default_md                  = sha256
# Extension to add when the -x509 option is used.
x509_extensions             = v3_ca

[ req_distinguished_name ]
countryName                 = Country Name (2 letter code)
stateOrProvinceName         = State or Province Name
localityName                = Locality Name
0.organizationName          = Organization Name
organizationalUnitName      = Organizational Unit Name
commonName                  = Common Name
emailAddress                = Email Address
countryName_default         = PL
stateOrProvinceName_default = Mazowsze
0.organizationName_default  = c1vDev

[ v3_ca ]
# Extensions to apply when createing root ca
# Extensions for a typical CA, man x509v3_config
subjectKeyIdentifier       = hash
authorityKeyIdentifier     = keyid:always,issuer
basicConstraints           = critical, CA:true
keyUsage                   = critical, digitalSignature, cRLSign, keyCertSign

[ v3_intermediate_ca ]
# Extensions to apply when creating intermediate or sub-ca
# Extensions for a typical intermediate CA, same man as above
subjectKeyIdentifier       = hash
authorityKeyIdentifier     = keyid:always,issuer
#pathlen:0 ensures no more sub-ca can be created below an intermediate
basicConstraints           = critical, CA:true, pathlen:0
keyUsage                   = critical, digitalSignature, cRLSign, keyCertSign

[ server_cert ]
# Extensions for server certificates
basicConstraints           = CA:FALSE
nsCertType                 = server
nsComment                  = "OpenSSL Generated Server Certificate"
subjectKeyIdentifier       = hash
authorityKeyIdentifier     = keyid,issuer:always
keyUsage                   = critical, digitalSignature, keyEncipherment
extendedKeyUsage           = serverAuth
subjectAltName              = @alt_names

[alt_names]
DNS.1 = exmaple.domain.pl
DNS.2 = *.example.pl.com
DNS.3 = more_examples.net
++ TEXT END ++

# Creating Signing Request for Inerminade Certificate
CA/sub-ca: openssl req -config sub-ca.conf -new -key private/sub-ca.key -sha256 -out csr/sub-ca.csr
# Sign Signing request using Root CA
CA/root-ca: openssl ca -config root-ca.conf -extensions v3_intermediate_ca -days 3650 -notext -in ../sub-ca/csr/sub-ca.csr -out ../sub-ca/certs/sub-ca.crt
# Show signed certificate
CA/root-ca: openssl x509 -noout -text -in ../sub-ca/certs/sub-ca.crt

===== CREATING SERVER CERTIFICATE =====
# Create csr for server using server's private key
CA/server: openssl req -key private/server.key -new -sha256 -out csr/server.csr

# Sign server certificate signing request
!IMPORTANT: Edit [alt_names] in sub-ca/sub-ca.conf and change DNS names to that are needed because borwers ignore CN.
CA/sub-ca: openssl ca -config sub-ca.conf -extensions server_cert -days 1825 -notext -in ../server/csr/server.csr -out ../server/certs/server.crt
# (Optional) Create Certificate Chain
CA/server/certs: type server.crt ..\..\sub-ca\certs\sub-ca.crt > chained.crt

@maryamzainy
Copy link

Welcome
Sorry, I'm new to Python and security. Is it possible to make a connection without this file because, frankly, I didn't understand how to create it despite reading about the topic?

@music-cat-bread
Copy link

music-cat-bread commented Mar 13, 2022

You have to understand what make connection secure.
If client and server would communicate in plain text then anyone could see what is going in the connection and make date change on it's way. When you are connecting to the https://github.com it first sends you it's certificate and also public key. Them your web browser (or anything else) can encrypt data using Public Key (That anyone can know) and sends it to server. Now I want to mention the Asymmetric Encryption. It is bases on two keys. Public and Private one. You can encrypt using Public key BUT ONLY DECRYPT USING PRIVATE KEY. And going back to example with GitHub. Only GitHub knows the private key.

This is pretty much it.
After that you sent some key for symmetric encryption (one key is for encryption and decryption), because we don't want to expose private key. But you don't have to handle it, it happens in the background and you don't have to care about it. But if you want encrypted/safe connection you have to generate your own certificate and key.

If you still don't understand maybe watch this.
And if also here is in my opinion a great tutorial on generating SSL certificates. https://www.youtube.com/watch?v=d8OpUcHzTeg

If you have any more question contact me on Discord.
Name: COVAND#6369

@sriramb12
Copy link

Hi COVAND
I tried messaging you on discord but it fails as I need to be your friend? I sent a friend request as well (sriram#2471)
thanks
Sriram

@sriramb12
Copy link

can you please show me which files (key, cert) need to be present within the run folder?
I can imagine server public key (for client to encrypt) and vice versa.

@NguyenKhue09
Copy link

@anson2416
Copy link

Hey @oborichkin , may I know how I could create a thread to handle client connection?
Giving the code, if a client disconnected, the server side will exit. I do not want the server side pgm ended abnormally.

@nns33213
Copy link

nns33213 commented May 5, 2023

Quite concerning that this gist is so popular for some reason. It's remarkably bad.


from tls_server import HOST as SERVER_HOST
from tls_server import PORT as SERVER_PORT

Just why...


PORT = 60002

Ports 49152–65535 are ephemeral ports. It's not recommended to use them.


client.bind((HOST, PORT))

You do not need to bind in client. OS will automatically pick an ephemeral source port.


client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

Ok, I guess you found a fix for the useless thing above.


    while True:
        from time import sleep

Move that to the top. It's also not a good idea to import directly to a global namespace.


client.send("Hello World!".encode("utf-8"))

Use .sendall instead of .send in synchronous sockets to avoid surprises.

Fixed

import socket
import ssl
import time

SERVER_HOST = "127.0.0.1"
SERVER_PORT = 40000

if __name__ == "__main__":
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    client = ssl.wrap_socket(client, keyfile="path/to/keyfile", certfile="path/to/certfile")

    client.connect((SERVER_HOST, SERVER_PORT))

    while True:
        client.sendall("Hello World!".encode("utf-8"))

        time.sleep(1)
import socket
import ssl

HOST = "127.0.0.1"
PORT = 40000

if __name__ == "__main__":
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    server = ssl.wrap_socket(
        server, server_side=True, keyfile="path/to/keyfile", certfile="path/to/certfile"
    )

    server.bind((HOST, PORT))
    server.listen(0)

    while True:
        connection, client_address = server.accept()

        while True:
            data = connection.recv(1024)

            if not data:
                break

            print(f"Received: {data.decode('utf-8')}")

@Slayerx96
Copy link

To avoid getting the deprecation warning and update the code SSLContext method must be used and the unwrapped socket closed as specified at https://pythontic.com/ssl/sslcontext/sslcontext :

import socket
import ssl

HOST = "127.0.0.1"
PORT = 8443

if __name__ == "__main__":
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain(certfile="/path/to/certfile", keyfile="/path/to/keyfile")
    context.load_verify_locations(cafile="/path/to/certfile")

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server = context.wrap_socket(s)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    s.close()

    server.bind((HOST, PORT))
    server.listen(0)

    while True:
        connection, client_address = server.accept()
        while True:
            data = connection.recv(1024)
            if not data:
                break
            print(f"Received: {data.decode('utf-8')}")
import socket
import ssl
import time

HOST = "127.0.0.1"
PORT = 8443

if __name__ == "__main__":
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    context.load_cert_chain(certfile="/path/to/certfile", keyfile="/path/to/keyfile")
    context.load_verify_locations(cafile="/path/to/certfile")
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client = context.wrap_socket(s, server_hostname=HOST)
    s.close()

    client.connect((HOST, PORT))

    while True:
        client.sendall("Hello World!".encode("utf-8"))
        time.sleep(1)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment