Trying to automate certificate generation with python3.cryptography; not having luck - python-3.x

I'm trying to programmatically create client certificates for mosquitto clients to use. Using XCA, I've been able to create a self signed CA, an intermediate, and then a certificate from the intermediate. I've configured mosquitto with the `require_certificate true' option. Using keys exported from XCA, I'm able to use the paho clients for testing:
mosquitto_sub -h ubuntu -p 8765 -t /1/2/3 --cafile ~/CA2/Chain.crt -d --cert ./test1.crt --key ./test1.pem
This works.
Now I'm trying to automate the key generation with a python3 script using the cryptography module.
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.x509.oid import NameOID
from pathlib import Path
from datetime import datetime, timezone, timedelta
import uuid
privateKey = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
backend=default_backend())
with Path('test2.pem').open('wb') as stream:
stream.write(privateKey.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()))
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, uuid.uuid4().hex),
]))
builder = builder.issuer_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, u'ubuntu'),
]))
builder = builder.not_valid_before(datetime.now(timezone.utc) - timedelta(days=1))
builder = builder.not_valid_after(datetime.now(timezone.utc) + timedelta(days=365*50))
builder = builder.serial_number(x509.random_serial_number())
builder = builder.public_key(privateKey.public_key())
builder = builder.add_extension(
x509.BasicConstraints(ca=False, path_length=None), critical=True,
)
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(privateKey.public_key()), critical=False,
)
builder = builder.add_extension(
x509.KeyUsage(digital_signature=True,
content_commitment=False,
key_encipherment=True,
data_encipherment=True,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False), critical=False,
)
certificate = builder.sign(
private_key=privateKey, algorithm=hashes.SHA256(),
backend=default_backend()
)
print(certificate)
with Path('test2.crt').open('wb') as stream:
stream.write(certificate.public_bytes(encoding=serialization.Encoding.PEM))
I've used openssl x509 -in certFile.crt -text -noout to compare the XCA variant vs my automated one. They look darn near identical to me. However, the sub doesn't work:
$ mosquitto_sub -h ubuntu -p 8765 -t /1/2/3 --cafile ~/CA2/Chain.crt -d --cert ./cert.crt --key ./key.pem
Client mosqsub|34613-ubuntu sending CONNECT
Error: A TLS error occurred.

The problem as pointed out in another source, was that I was signing the new certificate with the same key as I was generating the certificate from. Like the issue name is to be the same name as the issuing certificate, the signing key should be the private key of that same issuing certificate. Replacing the signing clause with something like:
parentKey = serialization.load_pem_private_key(
Path('path/to/intermediate_key.pem').read_bytes(),
password=None,
backend=default_backend())
certificate = builder.sign(
private_key=parentKey,
algorithm=hashes.SHA256(),
backend=default_backend()
)

Related

ValueError: Invalid password or PKCS12 data when retrieving certificate with a password from azure key vault

I'm writing code to retrieve certificate (as key, cert) from Azure Key Vault. The code runs perfectly when i remove the password from the certificate. How can i make it work with a password for example: abc()^WER123.
I've already tried several methods for decoding the password, transforming to bytes, using hashes.SHA256 as kdf algorithm. Nothing works.. It gives me this error:
"ValueError: Invalid password or PKCS12 data"
Here my latest code:
import base64
from azure.identity import ClientSecretCredential
from azure.keyvault.certificates import CertificateClient
from azure.keyvault.secrets import SecretClient
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.serialization import pkcs12
def get_keyCert_fromKeyVault():
TENANT_ID = ''
CLIENT_ID = ''
CLIENT_SECRET = ''
KEYVAULT_NAME = ''
KEYVAULT_URI = f"https://{KEYVAULT_NAME}.vault.azure.net/"
credential = ClientSecretCredential(
tenant_id=TENANT_ID,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET
)
#get certificate from vault
certificate_client = CertificateClient(vault_url=KEYVAULT_URI, credential=credential)
certificate = certificate_client.get_certificate("cert_name")
#get certificate from secret id of the cert.
secret_client = SecretClient(vault_url=KEYVAULT_URI, credential=credential)
certificate_secret = secret_client.get_secret(name=certificate.name)
cert_bytes = base64.b64decode(certificate_secret.value)
password_provided =b'abc()^WER123'
salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA1(),
length=64,
salt=salt,
iterations=480000,
backend=default_backend()
)
password = base64.urlsafe_b64encode(kdf.derive(password_provided))
private_key, public_certificate, additional_certificates =
pkcs12.load_key_and_certificates(
data=cert_bytes,
password=password
)
return private_key, public_certificate
Please Help!
Thanks in advance
I have followed the below steps to retrieve the certificate from the Azure key vault.
Step 1: Create Azure key vault and upload certificates to it.
Step 2: Setup python and run below code to retrieve certificates.
import os
import base64
import requests
import json
KEY_VAULT_URL = "https://<keyvault-name>.vault.azure.net"
TENANT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
CLIENT_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
CERT_NAME = "testcert010203"
def get_access_token():
auth_url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
auth_data = {
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"grant_type": "client_credentials",
"scope": f"https://vault.azure.net/.default"
}
response = requests.post(auth_url, data=auth_data)
response.raise_for_status()
return response.json()["access_token"]
def get_certificate(access_token):
"""Get the certificate from Azure Key Vault."""
cert_url = f"{KEY_VAULT_URL}/certificates/{CERT_NAME}?api-version=7.0"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json"
}
response = requests.get(cert_url, headers=headers)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
access_token = get_access_token()
cert_data = get_certificate(access_token)
print("Certificate data:")
print(json.dumps(cert_data, indent=4))
Step 3: Verify the certificate in the output.

Connecting to Neptune using aiogremlin

I am trying to connect to AWS Neptune using aiogremlin but keep getting SSL certificate errors. I tried using 3 different certificates downloaded from Amazon Trust Repository but none of them work. With AmazonRootCA1.pem and SFSRootCAG2.pem I keep getting File Not Found error and with SFSRootCAG2.cer I get ssl_context.load_cert_chain(ssl.SSLError: [SSL] PEM lib (_ssl.c:4046).
Here is the snippet which I am using to interact with Neptune.
import asyncio
from aiogremlin import DriverRemoteConnection, Graph
from .constants import NEPTUNE_ENDPOINT, CERT_DIR
async def go():
remote_connection = await DriverRemoteConnection.open(f'https://{NEPTUNE_ENDPOINT}:8182/gremlin', 'g',
ssl_certfile=CERT_DIR+'SFSRootCAG2.cer')
g = Graph().traversal().withRemote(remote_connection)
vertices = await g.V().toList()
await remote_connection.close()
return vertices
print(asyncio.get_event_loop().run_until_complete(go()))
Having trouble figuring out if I am using the wrong certificate file or doing something else that is wrong.
I got that exact same error (down to the line 4046 in ssl.c) when I converted my keystore to pem file without the certificate section.
I converted it over again without the '-nocerts' flag and the I got both section in the file like this:
Bag Attributes
friendlyName: mykey
localKeyID: ...
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
... [magic key details] ...
-----END PRIVATE KEY-----
Bag Attributes
friendlyName: mykey
localKeyID: ...
subject=C = NO, O = ..., CN = Server Administrator
issuer=C = NO, O = ..., CN = Server Administrator
-----BEGIN CERTIFICATE-----
... [magic key details] ...
-----END CERTIFICATE-----
I also created a new certificate like this that worked without trouble:
# openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout cert.pem

crypto.load_certificate / Get public key encryption

How is it possible from crypto.load_certificate to get the public key encryption ? (for example "RSA (2048 Bits").
I can get the public key easily as below :
from OpenSSL import crypto
cert = crypto.load_certificate(crypto.FILETYPE_PEM, open("certificate.crt")).read()
pubKey = cert.get_pubkey()
But I couldn't find anything in the documentation concerning the encryption. Any ideas?
Actually it is really simple :
from OpenSSL import crypto
cert = crypto.load_certificate(crypto.FILETYPE_PEM, open("certificate.crt")).read()
pubKey = cert.get_pubkey()
keySize = pubKey.bits()
if pubKey.type() == crypto.TYPE_RSA:
keyType = 'RSA'
elif pubKey.type() == crypto.TYPE_DSA:
keyType = 'DSA'
print(keyType + "-" + str(keySize))

TLS/SSL socket python server

I am trying to build a simple HTTPS server with Python3 using the socket and ssl modules.
I have a self signed certificate and a private key files generated by OpenSSL and I tried to use them with the ssl module but every time I try, I get a "ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1076)" error.
My code is
import socket
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.load_cert_chain(certfile='cert.pem', keyfile='my_key.key')
context.verify_mode = ssl.CERT_NONE
sock = socket.socket()
sock.bind(('', 443))
sock.listen(5)
while True:
new_conn, addr = sock.accept()
ssl_conn = context.wrap_socket(new_conn, server_side=True)
print(ssl_conn.recv(1024).decode()) # this is where i get the error
The error I get is:
File "C:\AllInOne\PortableApps\Python374\lib\ssl.py", line 1139, in do_handshake
self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1076)
Does anyone know why this happens or how to fix it?
Generate server.pem with the following command:
mkdir .ssh
openssl req -new -x509 -keyout .ssh/key.pem -out .ssh/cert.pem -days 365 -nodes
run as follows:
python3 simple-https-server.py
Then in your browser, visit:
https://localhost:4443
Here is the code:
import http.server
from http.server import HTTPServer, BaseHTTPRequestHandler, SimpleHTTPRequestHandler
import ssl
import sys
# This class will handles any incoming request from the browser
class myHandler(BaseHTTPRequestHandler):
# Handler for the GET requests
def do_GET(self):
print(self.requestline)
# print(self.rfile.read(content_length))
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
# Send the html message
self.wfile.write("Hello World !".encode())
return
try:
separator = "-" * 80
server_address = ("", 4443)
# server_address = ('localhost', 4443)
httpd = http.server.HTTPServer(server_address, myHandler)
# httpd = http.server.HTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(
httpd.socket,
server_side=True,
certfile=".ssh/cert.pem",
keyfile=".ssh/key.pem",
ssl_version=ssl.PROTOCOL_TLS,
)
print(separator)
print("Server running on https://localhost:4443")
print(separator)
# Wait forever for incoming htto requests
httpd.serve_forever()
except KeyboardInterrupt:
print("^C received, shutting down the web server")
server.socket.close()
ssl.SSLError: [SSL: SSLV3_ALERT_CERTIFICATE_UNKNOWN] sslv3 alert certificate unknown (_ssl.c:1076)
The client signals your server that it is does not trust your certificate. Which is expected since this is not a certificate issued by a trusted CA and you did not make the client explicit trust this certificate. If the client would not complain it would be insecure since every man in the middle could just use a fake certificate to identify itself as a trusted server.

Python RSA key, recieved the key but getting error "This is not a private key"

code for cilent
import socket, json
from Cryptodome.Cipher import PKCS1_OAEP, PKCS1_v1_5
from Cryptodome.Random import get_random_bytes
from Cryptodome.PublicKey import RSA
def getnewsocket():
return socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientsocket = getnewsocket()
clientsocket.connect(('localhost', 8089))
rsa_public = clientsocket.recv(99999)
encyrpted = clientsocket.recv(99999)
print(rsa_public)
rsakey = RSA.import_key(rsa_public.decode())
print(rsakey)
cipher = PKCS1_OAEP.new(rsakey)
decrypted = cipher.decrypt(encyrpted)
print(decrypted)
code for server
from Cryptodome.Cipher import PKCS1_OAEP, PKCS1_v1_5
from Cryptodome.Random import get_random_bytes
from Cryptodome.PublicKey import RSA
import socket
import json
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind(('0.0.0.0', 8089)) # 0.0.0.0 is a special address
print("Server activated, waiting for client to connect")
serversocket.listen(5)
connection, address = serversocket.accept()
rsakey_pair=RSA.generate(2048)
rsa_private = rsakey_pair
rsa_public = rsakey_pair.publickey().export_key()
hi = b"this is a plain text"
print(rsa_public)
cipher = PKCS1_OAEP.new(rsa_private)
encyrpted = cipher.encrypt(hi)
connection.sendall(rsa_public)
connection.sendall(encyrpted)
tried alot of ways but is either getting bytes cannot be n or this is not a private key. Always unable to decrypt the content of ciper text at client. I guess the error is something related to socket only can send bytes, so when the key had send through the socket, although it is still in bytes but is a different kind of bytes
error :
File "C:\Users\shang\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Cryptodome\Cipher\PKCS1_OAEP.py", line 171, in decrypt
m_int = self._key._decrypt(ct_int)
File "C:\Users\shang\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Cryptodome\PublicKey\RSA.py", line 151, in _decrypt
raise TypeError("This is not a private key")
TypeError: This is not a private key
Well, yes, that's because it is a public key.
rsakey = RSA.import_key(rsa_public.decode())
cipher = PKCS1_OAEP.new(rsakey)
decrypted = cipher.decrypt(encyrpted)
It is not possible to encrypt with a private key by definition. Encryption is performed using the public key, decryption using the private key. Public and private keys are not interchangeable for RSA. Maybe you want to generate a signature instead?
The only reason why the encryption with the private key succeeds is that it is likely that it also contains the public exponent and therefore the public key. Of course, the public key doesn't contain the private key as that needs to be kept private.
Note that even if it would be secure to encrypt with a private key if both keys (and thus the modulus) are kept private then you might as well use symmetric encryption, e.g. using the hash over the modulus as AES key.

Resources