Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to verify PyKCS11 signature using python cryptography library #126

Open
the-unreal-adi opened this issue Nov 9, 2024 · 5 comments

Comments

@the-unreal-adi
Copy link

the-unreal-adi commented Nov 9, 2024

Your system information

  • Operating system used: windows 11
  • PyKCS11 version: 1.5.17
  • Python version: 3.13.0
  • PKCS#11 library used:

Please describe your issue in as much detail as possible:

I am creating a data signing module which will sign data using inserted DSC token. I am able to sign the data and verify signature using PyKCS11 library. But when I am trying to verify the signature using python cryptography module, I am getting verification failure.

Sharing the code

import os
from cryptography.hazmat.primitives import hashes
from PyKCS11 import *


PKCS11_LIB_PATH = "eps2003csp11v264.dll"  

data = b"ilovepython"
data2 = b"iloveupython"

def compute_hash(data, algorithm=hashes.SHA256):
    hash_obj = hashes.Hash(algorithm(), backend=None)
    hash_obj.update(data)
    return hash_obj.finalize()

digest = compute_hash(data)
digest2 = compute_hash(data2)

pkcs11 = PyKCS11Lib()
pkcs11.load(PKCS11_LIB_PATH)

slot = pkcs11.getSlotList(tokenPresent=True)[0]
session = pkcs11.openSession(slot)
session.login("12345678")  # Replace with your token PIN

certs = session.findObjects([(PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_CERTIFICATE)])
if not certs:
    raise ValueError("No certificate found on the DSC token")
    
cert_der = bytes(session.getAttributeValue(certs[0], [PyKCS11.LowLevel.CKA_VALUE], True)[0])

priv_keys = session.findObjects([(PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_PRIVATE_KEY)])
if not priv_keys:
    raise ValueError("No private key found on the DSC token")
priv_key = priv_keys[0]

signature = bytes(session.sign(priv_key, digest, PyKCS11.MechanismRSAPKCS1))

pub_keys = session.findObjects([(PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_PUBLIC_KEY)])
if not pub_keys:
    raise ValueError("No public found on the DSC token")
pub_key = pub_keys[0]

result=session.verify(pub_key,digest,signature,PyKCS11.MechanismRSAPKCS1)

print("Digest",digest.hex())
print("Certificate: ", cert_der.hex())
print("Signature (hex): ", signature.hex())
print("Verified",result)

session.logout()
session.closeSession()

Verification code

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import binascii

# Public key in DER format (hexadecimal)
PUBLIC_KEY_DER_HEX = (
    "30820122300d06092a864886f70d01010105000382010f003082010a0282010100e3e22a2bb61397445dfebfa718c50a3ca1098e0e76d7a7bda97eed799df7cf5e59b9f693c614efae4d5ba7406c926ada743d368c81c79237466c49d30a44827d642061af8efddecc62187d66371e3ceb458cc9b04d43d5c040994e3085b93e8640c9241b6e740804afb3bf36f4790c58571ecd10e77408751124723fc63e9a133a04229c457d2e663fbcd6bc18780332820dea78373dcfd769af6edaf0e5dcca1bdb256104c555b82386f3104028636b3fa269be5811e2b56c95e2bac0a1b5b6858cef711846ea0ddd7a24c716eaf8c5e370149522f839abd4d09fcfe8f227b565dce920f5416da4916258d309f15a553444188da2a0af802e308daa9b8785930203010001"
)

# Data to verify
data = b"ilovepython"

# Signature in hexadecimal format
SIGNATURE_HEX = (
    "ae726f61a595d8ce08e127b2e2f2da44a3130c30dcf61fc8cb1bbac01c821cc544e688cb5dbb61b82c0f64271e2a5ca39b16d6b62c8bc9f99d1dd8ca86668bb973a9aea1eb005fd42f7329abde65a6e63a67253599980ada38099481eb4d6ccece6a1ae3095be5c93962ef876a68c3347bfd58c8353ea247b41916e1db78f6d22e76f2847b541777dcc52f3181aeec4ae9aabe639555fca638727339e5742bfc7d72fbbd35a9321ef2fb3ad861eb4ddad2f1eb4a337040429036ce43c09600fb67e2021020586e0764a000e2ad8282f31c6042b11c7415ec6797a9e02d6ba5c39f0df67d64b1037853ff3e4bf27fc760ed84ad5b8d2531bbc377a91da2d0613c"
)


def load_public_key_from_der_hex(der_hex):
    """
    Load a public key from DER format (hexadecimal string).
    """
    public_key_der = binascii.unhexlify(der_hex)
    return serialization.load_der_public_key(public_key_der, backend=default_backend())


def verify_raw_rsa_signature(public_key, data, signature):
    """
    Verify an RSA signature with no padding (raw RSA operation).
    """
    try:
        # Compute the hash of the original data
        hash_obj = hashes.Hash(hashes.SHA256(), backend=default_backend())
        hash_obj.update(data)
        digest = hash_obj.finalize()

        # Log the hash (digest) and signature
        print("Hash (digest):", digest.hex())
        print("Signature (decoded):", signature.hex())

        # Perform raw RSA verification
        public_key.verify(
            signature,
            digest,  # Hash of the data
            padding=padding.PKCS1v15(),  # PKCS#1 v1.5 padding
            algorithm=hashes.SHA256()  # Explicitly specify the hash algorithm
        )
        print("Signature is valid.")
    except Exception as e:
        print(f"Signature verification failed: {e}")


# Main execution
if __name__ == "__main__":
    # Load the public key from DER hexadecimal
    public_key = load_public_key_from_der_hex(PUBLIC_KEY_DER_HEX)

    # Decode the signature from hex
    signature = binascii.unhexlify(SIGNATURE_HEX)

    # Verify the signature
    verify_raw_rsa_signature(public_key, data, signature)


Please let me know where I am getting wrong?
I am also getting verification failure using openssl.

@LudovicRousseau
Copy link
Owner

If you use SHA256 in your verification side maybe you should use Mechanism(CKM_SHA256_RSA_PKCS, None) for the signature, instead of PyKCS11.MechanismRSAPKCS1

@the-unreal-adi
Copy link
Author

the-unreal-adi commented Nov 10, 2024

I started with Mechanism(CKM_SHA256_RSA_PKCS, None). Here's what I did, I first hashed the data using sha256 and then signed it using Mechanism(CKM_SHA256_RSA_PKCS, None). It didn't work out.
I read somewhere that if we are prehashing the data then we should use Mechanism(CKM_RSA_PKCS, None). Then I read in your documentation that use PyKCS11.MechanismRSAPKCS1 for Mechanism(CKM_RSA_PKCS, None), that's why I am using PyKCS11.MechanismRSAPKCS1.
I have tried every permutation and combination, in every case signature is being verified using PyKCS11 library but verification is failing if I use cryptography library.

Here's how I am fetching certificate and public key in PKCS11 for verification.

def fetch_certificate_publicKey_ownerName(session):
    certs = session.findObjects([(PyKCS11.CKA_CLASS, PyKCS11.CKO_CERTIFICATE)])
    if not certs:
        raise ValueError("No certificate found on the DSC token")

    cert_der = bytes(session.getAttributeValue(certs[0], [PyKCS11.CKA_VALUE], True)[0])

    certificate = x509.load_der_x509_certificate(cert_der, default_backend())

    public_key = certificate.public_key()
    public_key_der = public_key.public_bytes(encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo) 

    owner_name = certificate.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)[0].value

    return cert_der, public_key_der, owner_name

@LudovicRousseau
Copy link
Owner

Do not hash before you sign.

@the-unreal-adi
Copy link
Author

the-unreal-adi commented Nov 16, 2024

Thanks this finally worked for me...

Signing

import os
from cryptography.hazmat.primitives import hashes
from PyKCS11 import *

# Path to your PKCS#11 library
PKCS11_LIB_PATH = "eps2003csp11v264.dll"  # Replace with your PKCS#11 library path

# Data to be signed
data = b"ilovepython"
data2 = b"iloveupython"
# Step 1: Compute the hash of the data
def compute_hash(data, algorithm=hashes.SHA256):
    hash_obj = hashes.Hash(algorithm(), backend=None)
    hash_obj.update(data)
    return hash_obj.finalize()

digest = compute_hash(data)
digest2 = compute_hash(data2)

pkcs11 = PyKCS11Lib()
pkcs11.load(PKCS11_LIB_PATH)

# Open session and login
slot = pkcs11.getSlotList(tokenPresent=True)[0]
session = pkcs11.openSession(slot)
session.login("0")  # Replace with your token PIN

certs = session.findObjects([(PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_CERTIFICATE)])
if not certs:
    raise ValueError("No certificate found on the DSC token")
    
cert_der = bytes(session.getAttributeValue(certs[0], [PyKCS11.LowLevel.CKA_VALUE], True)[0])

# Find the private key
priv_keys = session.findObjects([(PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_PRIVATE_KEY)])
if not priv_keys:
    raise ValueError("No private key found on the DSC token")
priv_key = priv_keys[0]

# Perform raw RSA signing
signature = bytes(session.sign(priv_key, data, PyKCS11.Mechanism(PyKCS11.CKM_SHA256_RSA_PKCS)))

pub_keys = session.findObjects([(PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_PUBLIC_KEY)])
if not pub_keys:
    raise ValueError("No public found on the DSC token")
pub_key = pub_keys[0]

result=session.verify(pub_key,data,signature,PyKCS11.Mechanism(PyKCS11.CKM_SHA256_RSA_PKCS))

print("Certificate: ", cert_der.hex())
print("Signature (hex): ", signature.hex())
print("Verified",result)

# Logout and close session
session.logout()
session.closeSession()

Verification

from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
import binascii

# Public key in DER format (hexadecimal)
PUBLIC_KEY_DER_HEX = (
    "30820122300d06092a864886f70d01010105000382010f003082010a02820101009a5f273b913292f10381be4d2a1e3a88cb24575b5d9c7792b7ff07d7e92e720c3ef139c4a830e3114799a3b1959838bd9a13b673df04e9f98699d5d4628662256209ac734f6d4870ef8473e2089fd3a4633f999c72397b060fd031d682f698ba4c6f4bf8393621422a2ce91aad21375d0b2fcd03ed9b4ddb5731011c50bb5a9fea2c3755bf07f1d6e53e76b337ddeb51228fa443bc6f09ffddca4cb0db8a751699e93688449c98d57ddaf2c5742dd8a2b085c8ed93f8cf1b7d45342168e028876a2e3be580b7af7840283250289f0e728b24bbc1cbd4f64ee6dcfac4dbff48c084df13aaa9fdc775b77c73734d63249010928160a296364539b94e7fe86470bf0203010001"
)

# Data to verify
data = b"ilovepython"

# Signature in hexadecimal format
SIGNATURE_HEX = (
    "6d9f3e29c4e7ed9925dfd614dd66c717322945d9355d9631b1594d41f15cb31c645aa5fd8f667258fdca015873a983c1311b2e07c4fcab1cd3c8774b2883b71506cf76bf07e8bd3a7765661b46313a58eaf3d73ea1b40270337d0652bd1327a0d93541621924f117804143c8a93d5bc07b66ec9d134cbdbb9cc1f4b310f8b4bfc9ba0eb9356c0d2fbbb8872358eeed90338c2d72db94e9b2b4419beb51c74aa6cd5bffa607c30616df8a4ab096cf5015fb7bf72fa5d59b00b587e848ed38bcaee2c5183aa68a6f1cd60eaa276cb221c7746fa17fd5ee74f022555a3a2347dbccbd75d463c340d6144b2c2894e6d79f5da6df2c99a8ad0b18a27db32b16067c0d"
)


def load_public_key_from_der_hex(der_hex):
    """
    Load a public key from DER format (hexadecimal string).
    """
    public_key_der = binascii.unhexlify(der_hex)
    return serialization.load_der_public_key(public_key_der, backend=default_backend())


def verify_raw_rsa_signature(public_key, data, signature):
    """
    Verify an RSA signature with no padding (raw RSA operation).
    """
    try:
        # Compute the hash of the original data
        hash_obj = hashes.Hash(hashes.SHA256(), backend=default_backend())
        hash_obj.update(data)
        digest = hash_obj.finalize()

        # Log the hash (digest) and signature
        print("Hash (digest):", digest.hex())
        print("Signature (decoded):", signature.hex())

        # Perform raw RSA verification
        public_key.verify(
            signature,
            data,  # Hash of the data
            padding=padding.PKCS1v15(),  # PKCS#1 v1.5 padding
            algorithm=hashes.SHA256()  # Explicitly specify the hash algorithm
        )
        print("Signature is valid.")
    except Exception as e:
        print(f"Signature verification failed: {e}")


# Main execution
if __name__ == "__main__":
    # Load the public key from DER hexadecimal
    public_key = load_public_key_from_der_hex(PUBLIC_KEY_DER_HEX)

    # Decode the signature from hex
    signature = binascii.unhexlify(SIGNATURE_HEX)

    # Verify the signature
    verify_raw_rsa_signature(public_key, data, signature)

Although I was wondering can we handle data hashing on our own and just sign the data without internal hashing...

Also I don't want to hardcode the pin and want to use dsc token prompt, can we do that here?

@LudovicRousseau
Copy link
Owner

Thanks this finally worked for me...

Good!

Although I was wondering can we handle data hashing on our own and just sign the data without internal hashing...

You can sign the hash if you want to hash yourself. But the signature will also involve its own hash step.

Also I don't want to hardcode the pin and want to use dsc token prompt, can we do that here?

What is "dsc token prompt"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants