extracting public key from certificate and encrypting data - python

This is for a homework assignment!
I get the server's certificate using get_peer_certificate()
and the calling dump_certificate to dump the certificate in a variable. The format is PEM and looks right to me.
-----BEGIN CERTIFICATE-----
GIBBERISH................
......................
........................
-----END CERTIFICATE-----
How do I extract the server's public key from this file ('server.pubkey') and encrypt plaintext using RSA algorithm and any python library. At the time of writing this, I am using pyOpenSSL

I'd recommend using a more broad crypto library such as M2Crypto which has the X509 certificate functions as well as RSA encryption:
from M2Crypto import RSA, X509
data = ssl_sock.getpeercert(1)
# load the certificate into M2Crypto to manipulate it
cert = X509.load_cert_string(data, X509.FORMAT_DER)
pub_key = cert.get_pubkey()
rsa_key = pub_key.get_rsa()
cipher = rsa_key.public_encrypt('plaintext', RSA.pkcs1_padding)

from OpenSSL import crypto
crtObj = crypto.load_certificate(crypto.FILETYPE_ASN1, config.x509_certificate)
pubKeyObject = crtObj.get_pubkey()
pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM, pubKeyObject)

from cryptography.x509 import load_pem_x509_certificate
cert_str = b"-----BEGIN CERTIFICATE-----MIIDETCCAfm..."
cert_obj = load_pem_x509_certificate(cert_str)
public_key = cert_obj.public_key()
private_key = cert_obj.private_key()
Source: https://pyjwt.readthedocs.io/en/stable/faq.html

Note that OpenSSL library is not recommended to be used for those purposes. Instead, cryptography library is pointed. It is maintained and regularly updated.
Assuming you have the certificate in Pem format, the following code block will give you public key in string.
from cryptography import x509
from cryptography.hazmat.primitives import serialization
def read_pub_key_from_cert()
# Read certificate file.
with open("tls.crt") as certificate:
cert = certificate.read()
# Convert it into bytes.
cert_in_bytes = bytes(cert, 'utf-8')
# Create x509 certificate object.
cert_obj = x509.load_pem_x509_certificate(cert_in_bytes)
# Create Public key object.
public_key_obj = cert_obj.public_key()
# Convert Public key object into Pem format in bytes.
public_pem = public_key_obj.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
# Convert Public key into string.
pub_key_string = public_pem.decode("utf-8")
return(pub_key_string)

Related

Generating fingerprint from X509 certificate using Pycryptodome

I'm trying to generate the same SHA1 fingerprint for a X509 certificate using pycryptodome that I get from the openssl command:
openssl x509 -noout -fingerprint -sha1 -inform pem -in certificate.crt
My certificate is in PEM format on disk
However, the code snippet below gives me a different value.
from Crypto.PublicKey import RSA
import hashlib
contents = open("/home/ubuntu/certificate.crt", "r").read().encode()
certificate = RSA.import_key(contents)
bytes = certificate.export_key("DER")
hashlib.sha1(bytes).hexdigest()
Anyone any idea what I'm doing wrong?
Still don't know how to do it using Pycryptodome, but I found there's no need for it at all. The following code fragment generates the same fingerprint as openssl does
pem = open("/home/ubuntu/certificate.crt", "r").read().encode()
pem = pem.removeprefix("-----BEGIN CERTIFICATE-----\n")
pem = pem.removesuffix("-----END CERTIFICATE-----\n")
public_bytes = base64.b64decode(pem)
sha1digest = hashlib.sha1(public_bytes).hexdigest()
fingerprint = ":".join(sha1digest[i : i + 2] for i in range(0, len(sha1digest), 2))

Website authentication using smart cards' certificate and public key

So I have a credit card looking like smart card with a chip. This card logins on a website after the card is inserted into the card reader.
Now I have to write a program in python which can read the card and login on that website. After research on internet I found out that I need to extract :
Certificate and
Public key (since private key cannot be extracted)
from the card and then use these 2 things to create a HTTPs connection (example here) . So far I am able to extract certificate in pem format. But i cant find a way to extract key in pem format till now. I used PyKCS11 to read the card. Below is my code:
from asn1crypto import pem, x509
from PyKCS11 import *
import binascii
pkcs11 = PyKCS11Lib()
pkcs11.load(r'C:\Windows\System32\XXXX.dll')
print(pkcs11.getSlotList(tokenPresent=False))
slot = pkcs11.getSlotList(tokenPresent=False)[0]
print(pkcs11.getTokenInfo(slot))
session = pkcs11.openSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION)
session.login('123456')
result = []
result_pem = []
# find public key and print modulus
pubKey = session.findObjects([(CKA_CLASS, CKO_PUBLIC_KEY)])[0]
modulus = session.getAttributeValue(pubKey, [CKA_MODULUS])[0]
print("\nmodulus: {}".format(binascii.hexlify(bytearray(modulus))))
#find certificates
certs = session.findObjects([(CKA_CLASS, CKO_CERTIFICATE)])
for cert in certs:
cka_value, cka_id = session.getAttributeValue(cert, [CKA_VALUE, CKA_ID])
cert_der = bytes(cka_value)
cert = x509.Certificate.load(cert_der)
# Write out a PEM encoded value
cert_pem = pem.armor('CERTIFICATE', cert_der)
result.append(cert)
result_pem.append(cert_pem)
with open('cert.pem','wb') as f:
f.write(cert_pem)
print(result)
So here are my questions:
1. Is my approach right?
If yes, then how to extract public key in pem format?
How this smart card authentication actually works on client side and server side?
Public key extraxction
If you already have exported the certificate, it is probably easier to extract the public key from there, instead of from the smartcard. You can use openssl for that:
openssl x509 -in cert.pem -pubkey -out pubkey.pem -noout
Authentication
What you are trying to achieve is to open a TLS connection with mutual authentication using a client certificate. If you do this, the private key of your client certificate signs parts of the handshake to authenticate itself towards the server.
Extracting the certificate and the public key from the smartcard won't help you here. You need to find a library, which allows you to use your private key straight from your PKCS#11 token.

Python rsa module troubles

I need to encrypt some data, so I decided to use rsa module. I need to generate public and private keys. I want to send my public keys to some clients. How can I do this?
import rsa
(public_key, private_key) = rsa.newkeys(512)
So I've already generated it, but public_key now is a structure with two fields e and n. how can I transform it into bytes? And back from bytes to such structure?
You can use PyCrypto module
from Crypto.PublicKey import RSA
key = RSA.generate(512)
private_key = key.exportKey(passphrase='', pkcs=8)
public_key = key.publickey().exportKey()
In your case, try using save_pkcs1 method for saving into PEM format.
UPD:
Code sample for encrypt using rsa:
message = b'message'
# Alternative way - message = 'message'.encode('utf-8')
encrypted = rsa.encrypt(message, public_key)
For decrypt:
rsa.decrypt(encrypted, private_key).decode('utf-8')

How to get public key using PyOpenSSL?

I'm tring to create python script, that would take PKCS#12 package and print some information contained in x509 certificate and using for this purpouses PyOpenSSL module. So far i want to fetch from certificate public key. But PKey object doesn't have appropriate method. Where can I move out of here ? Any ideas how to get public key ?
pfx=open('./1.p12','rb').read()
PKCS=crypto.load_pkcs12(pfx)
cert=PKCS.get_certificate()
PKey=cert.get_pubkey()
print PKey
<OpenSSL.crypto.PKey object at 0x012432D8>
Thanks.
First you can load the certificate like this
from OpenSSL import crypto
#cert is the encrypted certificate int this format -----BEGIN -----END
crtObj = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
pubKeyObject = crtObj.get_pubkey()
pubKeyString = crypto.dump_publickey(crypto.FILETYPE_PEM,pubKeyObject)
print pubKeyString
you will see something like
-----BEGIN PUBLIC KEY-----
....
....
-----END PUBLIC KEY-----
I am assuming you want to read the public key from the file.
First install pyopenssl
pip install pyopenssl
from OpenSSL import crypto
import os
file_path = os.path.join(os.getcwd(), '/certificates/test.crt')
with open(file_path, "r") as f:
cert = f.read()
pub_key_obj = crypto.load_certificate(crypto.FILETYPE_PEM, cert).get_pubkey()
pub_key = crypto.dump_publickey(crypto.FILETYPE_PEM, pub_key_obj)
print(pub_key)
or
file_path = '~/.ssh/private_key'
with open(file_path, "rb") as f:
raw = f.read()
private_key_obj = crypto.load_privatekey(crypto.FILETYPE_PEM, raw)
pub_key = crypto.dump_publickey(crypto.FILETYPE_PEM, private_key_obj)
print(pub_key)
You will get output as:
-----BEGIN PUBLIC KEY-----
....
-----END PUBLIC KEY-----
Would this work?
print PKey
<OpenSSL.crypto.PKey object at 0x012432D8>
from OpenSSL import crypto
crypto.dump_privatekey(PKey)
Instead use:
c.dump_privatekey(c.FILETYPE_TEXT,pubkey)

How to load an RSA key from a PEM file and use it in python-crypto

I have not found a way to load an RSA private key from a PEM file to use it in python-crypto (signature).
python-openssl can load a PEM file but the PKey object can't be used to retrieved key information (p, q, ...) to use with Crypto.PublicKey.construct().
I recommend M2Crypto instead of python-crypto. You will need M2Crypto to parse PEM anyway and its EVP api frees your code from depending on a particular algorithm.
private = """
-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANQNY7RD9BarYRsmMazM1hd7a+u3QeMPFZQ7Ic+BmmeWHvvVP4Yj
yu1t6vAut7mKkaDeKbT3yiGVUgAEUaWMXqECAwEAAQJAIHCz8h37N4ScZHThYJgt
oIYHKpZsg/oIyRaKw54GKxZq5f7YivcWoZ8j7IQ65lHVH3gmaqKOvqdAVVt5imKZ
KQIhAPPsr9i3FxU+Mac0pvQKhFVJUzAFfKiG3ulVUdHgAaw/AiEA3ozHKzfZWKxH
gs8v8ZQ/FnfI7DwYYhJC0YsXb6NSvR8CIHymwLo73mTxsogjBQqDcVrwLL3GoAyz
V6jf+/8HvXMbAiEAj1b3FVQEboOQD6WoyJ1mQO9n/xf50HjYhqRitOnp6ZsCIQDS
AvkvYKc6LG8IANmVv93g1dyKZvU/OQkAZepqHZB2MQ==
-----END RSA PRIVATE KEY-----
"""
message = "python-crypto sucks"
# Grab RSA parameters e, n
from M2Crypto import RSA, BIO
bio = BIO.MemoryBuffer(private)
rsa = RSA.load_key_bio(bio)
n, e = rsa.n, rsa.e
# In Python-crypto:
import Crypto.PublicKey.RSA
pycrypto_key = Crypto.PublicKey.RSA.construct((n, e))
# Use EVP api to sign message
from M2Crypto import EVP
key = EVP.load_key_string(private)
# if you need a different digest than the default 'sha1':
key.reset_context(md='sha256')
key.sign_init()
key.sign_update(message)
signature = key.sign_final()
# Use EVP api to verify signature
public = """
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANQNY7RD9BarYRsmMazM1hd7a+u3QeMP
FZQ7Ic+BmmeWHvvVP4Yjyu1t6vAut7mKkaDeKbT3yiGVUgAEUaWMXqECAwEAAQ==
-----END PUBLIC KEY-----
"""
from M2Crypto import BIO, RSA, EVP
bio = BIO.MemoryBuffer(public)
rsa = RSA.load_pub_key_bio(bio)
pubkey = EVP.PKey()
pubkey.assign_rsa(rsa)
pubkey.reset_context(md="sha256")
pubkey.verify_init()
pubkey.verify_update(message)
assert pubkey.verify_final(signature) == 1
See http://svn.osafoundation.org/m2crypto/trunk/tests/test_rsa.py, but I prefer using the algorithm-independent EVP API http://svn.osafoundation.org/m2crypto/trunk/tests/test_evp.py.
How do you verify an RSA SHA1 signature in Python? addresses a similar issue.
is this (close to) what you tried doing?
public_key_filename = 'public_key.pem'
rsa = M2Crypto.RSA.load_pub_key(pk)
That should work. The issue might be with openssl too, does it work when you just use openssl (not in Python)?
Link to Me Too Crypto

Categories