Website authentication using smart cards' certificate and public key - python

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.

Related

What is the right way to validate a StoreKit 2 transaction jwsRepresentation in python?

It's unclear from the docs what you actually do to verify the jwsRepresentation string from a StoreKit 2 transaction on the server side.
Also "signedPayload" from the Apple App Store Notifications V2 seems to be the same, but there is also no documentation around actually validating that either outside of validating it client side on device.
What gives? What do we do with this JWS/JWT?
(DISCLAIMER: I am a crypto novice so check me on this if I'm using the wrong terms, etc. throughout)
The JWS in jwsRepresentation, and the signedPayload in the Notification V2 JSON body, are JWTs — you can take one and check it out at jwt.io. The job is to validate the JWT signature and extract the payload once you're sufficiently convinced it's really from Apple. Then the payload itself contains information you can use to upgrade the user's account/etc. server side once the data is trusted.
To validate the JWT, you need to find the signature that the JWT is signed with, specified in the JWT header's "x5c" collection, validate the certificate chain, and then validate that the signature is really from Apple.
STEP ONE: Load the well-known root & intermediate certs from Apple.
import requests
from OpenSSL import crypto
ROOT_CER_URL = "https://www.apple.com/certificateauthority/AppleRootCA-G3.cer"
G6_CER_URL = "https://www.apple.com/certificateauthority/AppleWWDRCAG6.cer"
root_cert_bytes: bytes = requests.get(ROOT_CER_URL).content
root_cert = crypto.load_certificate(crypto.FILETYPE_ASN1, root_cert_bytes)
g6_cert_bytes: bytes = requests.get(G6_CER_URL).content
g6_cert = crypto.load_certificate(crypto.FILETYPE_ASN1, g6_cert_bytes)
STEP TWO: Get certificate chain out of the JWT header
import jwt # PyJWT library
# Get the signing keys out of the JWT header. The header will look like:
# {"alg": "ES256", "x5c": ["...base64 cert...", "...base64 cert..."]}
header = jwt.get_unverified_header(apple_jwt_string)
provided_certificates: List[crypto.X509] = []
for cert_base64 in header['x5c']:
cert_bytes = base64url_decode(cert_base64)
cert = crypto.load_certificate(crypto.FILETYPE_ASN1, cert_bytes)
provided_certificates.append(cert)
STEP THREE: Validate the chain is what you think it is -- this ensures the cert chain is signed by the real Apple root & intermediate certs.
# First make sure these are the root & intermediate certs from Apple:
assert provided_certificates[-2].digest('sha256') == g6_cert.digest('sha256')
assert provided_certificates[-1].digest('sha256') == root_cert.digest('sha256')
# Now validate that the cert chain is cryptographically legit:
store = crypto.X509Store()
store.add_cert(root_cert)
store.add_cert(g6_cert)
for cert in provided_certificates[:-2]:
try:
crypto.X509StoreContext(store, cert).verify_certificate()
except crypto.X509StoreContextError:
logging.error("Invalid certificate chain in JWT: %s", apple_jwt)
return None
store.add_cert(cert)
FINALLY: Load & validate the JWT using the now-trusted certificate in the header.
# Now that the cert is validated, we can use it to verify the actual signature
# of the JWT. PyJWT does not understand this certificate if we pass it in, so
# we have to get the cryptography library's version of the same key:
cryptography_version_of_key = provided_certificates[0].get_pubkey().to_cryptography_key()
try:
return jwt.decode(apple_jwt, cryptography_version_of_key, algorithms=["ES256"])
except Exception:
logging.exception("Problem validating Apple JWT")
return None
Voila you now have a validated JWT body from the App Store at your disposal.
Gist of entire solution: https://gist.github.com/taylorhughes/3968575b40dd97f851f35892931ebf3e

How use public key with pyOpenSSL for verify a signed message?

I try to use pyOpenSSL for signed a data, I create key pair (private and publique) and certificate.
I'm a beginner with this technology, I use OpenSSl, but if you have suggestions for generate a signed message with private and public key in python, I'm take !
I want to use RSA and DSA algorithm for my tests.
I find m2Crypto, pyCrypto and other. I do not know what is the best for this.
gnupg for python and pyOpenSSl are more recent visibly.
I used function for signed a message with my private key, and I verify the data.
But when I see the function for verify the signature, in parameters I need :
private key, signature, data and digest type.
I do not know where I am wrong in this code, I find some examples, but I do not understand how this can work because the first parameters for the verify function is a X509 object "certificate is a X509 instance corresponding to the private key which generated the signature." and the second is the signature generated with the private key..
This code work perfectly with the private key :
from OpenSSL import crypto
_k = crypto.PKey()
_cert = crypto.X509()
# Create keys
_k.generate_key(crypto.TYPE_RSA, 2048)
# Add argument for create certificate
_cert.gmtime_adj_notBefore(0)
_cert.gmtime_adj_notAfter(0*365*24*60*60) #10 years expiry date
_cert.set_pubkey(_k)
_cert.sign(_k, 'sha256')
# Create key's file
with open("public_key.pem",'w') as f:
f.write(crypto.dump_publickey(crypto.FILETYPE_PEM, _k))
with open("private_key.pem",'w') as f:
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, _k))
with open("certificate.pem",'w') as f:
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, _cert))
#-------------------------------------------------------------------------------
# Open key and load in var
with open("private_key.pem",'r') as f:
priv_key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read())
with open("public_key.pem",'r') as f:
pub_key = crypto.load_publickey(crypto.FILETYPE_PEM, f.read())
with open("certificate.pem",'r') as f:
cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read())
# sign message 'hello world' with private key and certificate
sign = crypto.sign(priv_key, "hello world", 'sha256')
print crypto.verify(cert, sign, "hello world", 'sha256')
So, my question is, how use the public key for verify the data ?
If Bob give a public key to alice, How it checks the message with this public key ?
You have a idea ?
Thanks a lot,
Romain
I found a answer in this post.
from OpenSSL.crypto import load_publickey, FILETYPE_PEM, verify, X509
# ... code ...
x509 = X509()
x509.set_pubkey(pub_key)
# ... code ...
print verify(x509, sign, sha, 'sha256')
I'm wondering.. When I get a message, I use the public key for authenticating the transmitter, but why I have to use the signature to do the verification ? To generate the signature, I need the private key ..
Is there something I did not understand in the use of the library ?
ok I understood my mistake : Cryptography Digital signatures
source

Python JWT and RSA

I would like to learn about creating JWT using RSA public and private key. I am a beginner to learn securing my services. I am using pyjwt right now. I got something error with my testing, here it is:
SAMPLEKEY:
privatekey = """-----BEGIN RSA PRIVATE KEY-----MIICXQIBAAKBgQCkC2AfenNMfrU4oMfMZt9aZGBbFjzBTjV9Yttp0GHeVjIKboTwLkiKNqSKrm2Jralbteii2J9h6BeUBpv3B/Os7M0eNeM8B+5Rzm44vcmkzdtufTuX2utjoz8BFjelXw5og2i67NtxgiSHv2x1KCHbGZG+jpDOgjorxFusKbeGjQIDAQABAoGADbMJfvd543x9Y9JBfTdmFaVmSpUL09TVMLhtvGNzmN635RkfrvMeibRQf2hbq3C+QPNrDxZqEQIR3gHDSpj2Z2tGrE95a5o8+I3NBARkKOz41lMFm2AnXZLsM0ma+8S61j8AtELgFuKZWyi2t9A3Otf1+vayZVS/F8pyof0wD10CQQDXDloBpki887jVXtnIauV3Z1744P/uvVkWZOMTMiNF5Xh8SRPn2mNR80vUAAN5SL7zjGyDQeoYKZMRJaLFGsaPAkEAw0bCyz2aA+aWaTM1iyK4XK9/dNPoztE0lmeaHXvI7d1Zp0ipbLwewt4gRbSL7UpxdRQy0ELep4HoSTLt2dQPIwJBANCtS2c4XHKFKIBa5oaUO4+OjdiAM7gMoeqaAMG6sAF99ljbbGZZQnDd3WGclcJVdXzMcOs4xZeml99WnsgWAD8CQAWIfsKFh1Su9voaIl1D6ZduvZzQ2Frr4KKWYu6M8F+VExJDY9GZ7wE0jBONjx11K4vWu63dBzQV4UAZulWexaMCQQCmfoq0l1JtnYhV3LhEN3E8gwUK/0456An5YKunwO8nPrBsrdt/TQ6ZAUzh7JkmabV3h2KXQ+H0/cvfWBOhYaov-----END RSA PRIVATE KEY-----"""
publickey = """-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkC2AfenNMfrU4oMfMZt9aZGBbFjzBTjV9Yttp0GHeVjIKboTwLkiKNqSKrm2Jralbteii2J9h6BeUBpv3B/Os7M0eNeM8B+5Rzm44vcmkzdtufTuX2utjoz8BFjelXw5og2i67NtxgiSHv2x1KCHbGZG+jpDOgjorxFusKbeGjQIDAQAB-----END PUBLIC KEY-----"""
Here is my sample code:
#app.route('/token')
def token():
encodedToken = jwt.encode({'userid':'1'}, publickey, algorithm = 'RS256')
print(jwt.decode(encodedToken, privatekey, verify=True, algorithms='RS256'))
return "OKE"
And, I am getting this error when running it:
Is there any solution into this ?
Thanks
Basically you'll want to switch the keys, use the private key for "encoding" (signing) and public for "decoding" (verification).
This issue is reported on GitHub here: https://github.com/jpadilla/pyjwt/issues/81
And marked as "closed".

Disable SSL certificate validation in Python

I'm writing a script that connects to a bunch of URLs over HTTPS, downloads their SSL certificate and extracts the CN. Everything works except when I stumble on a site with an invalid SSL certificate. I absolutely do not care if the certificate is valid or not. I just want the CN but Python stubbornly refuses to extract the certificate information if the certificate is not validated. Is there any way to get around this profoundly stupid behavior? Oh, I'm using the built-in socket and ssl libraries only. I don't want to use third-party libraries like M2Crypto or pyOpenSSL because I'm trying to keep the script as portable as possible.
Here's the relevant code:
file = open("list.txt", "r")
for x in file:
server = socket.getaddrinfo(x.rstrip(), "443")[0][4][0]
sslsocket = socket.socket()
sslsocket.connect((server, 443))
sslsocket = ssl.wrap_socket(sslsocket, cert_reqs=ssl.CERT_REQUIRED, ca_certs="cacerts.txt")
certificate = sslsocket.getpeercert()`
The ssl.get_server_certificate can do it:
import ssl
ssl.get_server_certificate(("www.sefaz.ce.gov.br",443))
I think function doc string is more clear than python doc site:
"""Retrieve the certificate from the server at the specified address,
and return it as a PEM-encoded string.
If 'ca_certs' is specified, validate the server cert against it.
If 'ssl_version' is specified, use it in the connection attempt."""
So you can extract common name from binary DER certificate searching for common name object identifier:
def get_commonname(host,port=443):
oid='\x06\x03U\x04\x03' # Object Identifier 2.5.4.3 (COMMON NAME)
pem=ssl.get_server_certificate((host,port))
der=ssl.PEM_cert_to_DER_cert(pem)
i=der.find(oid) # find first common name (certificate authority)
if i!=-1:
i=der.find(oid,i+1) # skip and find second common name
if i!=-1:
begin=i+len(oid)+2
end=begin+ord(der[begin-1])
return der[begin:end]
return None
Ok. I cleaned up olivecoder's code to solve the problem that it assumes there will always be three CNs in the certificate chain (root, intermediate, server) and I condensed it. This is the final code I will be using.
cert = ssl.get_server_certificate(("www.google.com", 443)) #Retrieve SSL server certificate
cert = ssl.PEM_cert_to_DER_cert(cert) #Convert certificate to DER format
begin = cert.rfind('\x06\x03\x55\x04\x03') + 7 #Find the last occurence of this byte string indicating the CN, add 7 bytes to startpoint to account for length of byte string and padding
end = begin + ord(cert[begin - 1]) #Set endpoint to startpoint + the length of the CN
print cert[begin:end] #Retrieve the CN from the DER encoded certificate

extracting public key from certificate and encrypting data

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)

Categories