PyKCS11 samples codes

Unblock a user PIN

#!/usr/bin/env python3

#   Copyright (C) 2006-2014 Ludovic Rousseau <ludovic.rousseau@free.fr>
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from __future__ import print_function

import PyKCS11

pin = "1234"
puk = "1234"

pkcs11 = PyKCS11.PyKCS11Lib()
pkcs11.load()
slot = pkcs11.getSlotList(tokenPresent=True)[0]
session = pkcs11.openSession(slot, PyKCS11.CKF_RW_SESSION)
session.login(puk, PyKCS11.CKU_SO)
session.initPin(pin)
session.logout()
session.closeSession()

Generate a key pair

#!/usr/bin/env python3

#   Copyright (C) 2015 Roman Pasechnik
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from PyKCS11 import *

# the key_id has to be the same for both objects
key_id = (0x22,)

pkcs11 = PyKCS11Lib()
pkcs11.load()  # define environment variable PYKCS11LIB=YourPKCS11Lib

# get 1st slot
slot = pkcs11.getSlotList(tokenPresent=True)[0]

session = pkcs11.openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION)
session.login("1234")

pubTemplate = [
    (CKA_CLASS, CKO_PUBLIC_KEY),
    (CKA_TOKEN, CK_TRUE),
    (CKA_PRIVATE, CK_FALSE),
    (CKA_MODULUS_BITS, 0x0400),
    (CKA_PUBLIC_EXPONENT, (0x01, 0x00, 0x01)),
    (CKA_ENCRYPT, CK_TRUE),
    (CKA_VERIFY, CK_TRUE),
    (CKA_VERIFY_RECOVER, CK_TRUE),
    (CKA_WRAP, CK_TRUE),
    (CKA_LABEL, "My Public Key"),
    (CKA_ID, key_id),
]

privTemplate = [
    (CKA_CLASS, CKO_PRIVATE_KEY),
    (CKA_TOKEN, CK_TRUE),
    (CKA_PRIVATE, CK_TRUE),
    (CKA_DECRYPT, CK_TRUE),
    (CKA_SIGN, CK_TRUE),
    (CKA_SIGN_RECOVER, CK_TRUE),
    (CKA_UNWRAP, CK_TRUE),
    (CKA_ID, key_id),
]

(pubKey, privKey) = session.generateKeyPair(pubTemplate, privTemplate)

# logout
session.logout()
session.closeSession()

Encrypt and decrypt

#!/usr/bin/env python3

#   Copyright (C) 2015 Roman Pasechnik
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from __future__ import print_function

from PyKCS11 import *
import binascii

pkcs11 = PyKCS11Lib()
pkcs11.load()  # define environment variable PYKCS11LIB=YourPKCS11Lib

# get 1st slot
slot = pkcs11.getSlotList(tokenPresent=True)[0]

session = pkcs11.openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION)
session.login("1234")

# "Hello world" in hex
message = "48656c6c6f20776f726c640d0a"

# get first public and private keys
pubKey = session.findObjects([(CKA_CLASS, CKO_PUBLIC_KEY)])[0]
privKey = session.findObjects([(CKA_CLASS, CKO_PRIVATE_KEY)])[0]
enc = session.encrypt(pubKey, binascii.unhexlify(message))
dec = session.decrypt(privKey, enc)

print("\nmessage: " + message)
print("\nencrypted: {}".format(binascii.hexlify(bytearray(enc))))
print("\ndecrypted: {}".format(bytearray(dec)))

# logout
session.logout()
session.closeSession()

Get token events

#!/usr/bin/env python3

#   Copyright (C) 2009-2014 Ludovic Rousseau (ludovic.rousseau@free.fr)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from __future__ import print_function

import PyKCS11
import getinfo

if __name__ == "__main__":
    import getopt
    import sys

    def usage():
        print("Usage:", sys.argv[0], end=" ")
        print("[-p pin][--pin=pin] (use 'NULL' for pinpad)", end=" ")
        print("[-c lib][--lib=lib]", end=" ")
        print("[-h][--help]", end=" ")
        print("[-o][--opensession]")
        print("[-f][--full]")

    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            "p:c:homf",
            ["pin=", "lib=", "help", "opensession", "mechanisms", "full"],
        )
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)

    lib = None
    pin = ""
    open_session = False
    pin_available = False
    list_mechanisms = False
    full = False
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-p", "--pin"):
            pin = a
            if pin == "NULL":
                pin = None
            pin_available = True
            open_session = True
        if o in ("-c", "--lib"):
            lib = a
        if o in ("-o", "--opensession"):
            open_session = True
        if o in ("-m", "--mechanisms"):
            list_mechanisms = True
        if o in ("-f", "--full"):
            full = True

    gi = getinfo.getInfo(lib)
    gi.getInfo()

    slots = gi.pkcs11.getSlotList()
    print("Available Slots:", len(slots), slots)

    if len(slots) == 0:
        sys.exit(2)

    while True:
        print("waiting...")
        slot = gi.pkcs11.waitForSlotEvent()

        try:
            print("Slot %d changed" % slot)
            gi.getSlotInfo(slot, 0, len(slots))
            if full:
                gi.getSessionInfo(slot, pin)
                gi.getTokenInfo(slot)
                if list_mechanisms:
                    gi.getMechanismInfo(slot)
        except PyKCS11.PyKCS11Error as e:
            print("Error:", e)

Get a public key modulus

#!/usr/bin/env python3

#   Copyright (C) 2015 Roman Pasechnik
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from __future__ import print_function

from PyKCS11 import *
import binascii

pkcs11 = PyKCS11Lib()
pkcs11.load()  # define environment variable PYKCS11LIB=YourPKCS11Lib

# get 1st slot
slot = pkcs11.getSlotList(tokenPresent=True)[0]

session = pkcs11.openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION)
session.login("1234")

# key ID in hex (has to be tuple, that's why trailing comma)
keyID = (0x22,)

# find public key and print modulus
pubKey = session.findObjects([(CKA_CLASS, CKO_PUBLIC_KEY), (CKA_ID, keyID)])[0]
modulus = session.getAttributeValue(pubKey, [CKA_MODULUS])[0]
print("\nmodulus: {}".format(binascii.hexlify(bytearray(modulus))))

# logout
session.logout()
session.closeSession()

RSA sign & verify

#!/usr/bin/env python3

#   Copyright (C) 2015 Roman Pasechnik
#   Copyright (C) 2018 Ludovic Rousseau
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from PyKCS11 import *
import binascii

pkcs11 = PyKCS11Lib()
pkcs11.load()  # define environment variable PYKCS11LIB=YourPKCS11Lib

# get 1st slot
slot = pkcs11.getSlotList(tokenPresent=True)[0]

session = pkcs11.openSession(slot, CKF_SERIAL_SESSION | CKF_RW_SESSION)
session.login("1234")

# message to sign
toSign = "Hello World!"
mechanism = Mechanism(CKM_SHA256_RSA_PKCS, None)

# find first private key and compute signature
privKey = session.findObjects([(CKA_CLASS, CKO_PRIVATE_KEY)])[0]
signature = session.sign(privKey, toSign, mechanism)
print("\nsignature: {}".format(binascii.hexlify(bytearray(signature))))

# find first public key and verify signature
pubKey = session.findObjects([(CKA_CLASS, CKO_PUBLIC_KEY)])[0]
result = session.verify(pubKey, toSign, signature, mechanism)
print("\nVerified:", result)

# logout
session.logout()
session.closeSession()

Get token information

#!/usr/bin/env python3

#   Copyright (C) 2006-2014 Ludovic Rousseau (ludovic.rousseau@free.fr)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from __future__ import print_function

import PyKCS11
import platform
import sys


class getInfo(object):
    red = blue = magenta = normal = ""

    def colorize(self, text, arg):
        print(self.magenta + text + self.blue, arg, self.normal)

    def display(self, obj, indent=""):
        dico = obj.to_dict()
        for key in sorted(dico.keys()):
            type = obj.fields[key]
            left = indent + key + ":"
            if type == "flags":
                self.colorize(left, ", ".join(dico[key]))
            elif type == "pair":
                self.colorize(left, "%d.%d" % dico[key])
            else:
                self.colorize(left, dico[key])

    def __init__(self, lib=None):
        if sys.stdout.isatty() and platform.system().lower() != "windows":
            self.red = "\x1b[01;31m"
            self.blue = "\x1b[34m"
            self.magenta = "\x1b[35m"
            self.normal = "\x1b[0m"

        self.pkcs11 = PyKCS11.PyKCS11Lib()
        self.pkcs11.load(lib)

    def getSlotInfo(self, slot, slot_index, nb_slots):
        print()
        print(
            self.red
            + "Slot %d/%d (number %d):" % (slot_index, nb_slots, slot)
            + self.normal
        )
        self.display(self.pkcs11.getSlotInfo(slot), " ")

    def getTokenInfo(self, slot):
        print(" TokenInfo")
        self.display(self.pkcs11.getTokenInfo(slot), "  ")

    def getMechanismInfo(self, slot):
        print("  Mechanism list: ")
        m = self.pkcs11.getMechanismList(slot)
        for x in m:
            self.colorize("  ", x)
            i = self.pkcs11.getMechanismInfo(slot, x)
            if not i.flags & PyKCS11.CKF_DIGEST:
                if i.ulMinKeySize != PyKCS11.CK_UNAVAILABLE_INFORMATION:
                    self.colorize("    ulMinKeySize:", i.ulMinKeySize)
                if i.ulMaxKeySize != PyKCS11.CK_UNAVAILABLE_INFORMATION:
                    self.colorize("    ulMaxKeySize:", i.ulMaxKeySize)
            self.colorize("    flags:", ", ".join(i.flags2text()))

    def getInfo(self):
        self.display(self.pkcs11.getInfo())

    def getSessionInfo(self, slot, pin=""):
        print(" SessionInfo", end=" ")
        session = self.pkcs11.openSession(slot)

        if pin != "":
            if pin is None:
                print("(using pinpad)")
            else:
                print("(using pin: %s)" % pin)
            session.login(pin)
        else:
            print()

        self.display(session.getSessionInfo(), "  ")

        if pin:
            session.logout()


def usage():
    print("Usage:", sys.argv[0], end=" ")
    print("[-a][--all]", end=" ")
    print("[-p pin][--pin=pin] (use 'NULL' for pinpad)", end=" ")
    print("[-s slot][--slot=slot]", end=" ")
    print("[-c lib][--lib=lib]", end=" ")
    print("[-m][--mechanisms]", end=" ")
    print("[-h][--help]")


if __name__ == "__main__":
    import getopt

    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            "p:s:c:ham",
            ["pin=", "slot=", "lib=", "help", "all", "mechanisms"],
        )
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)

    slot = None
    lib = None
    pin = ""
    token_present = True
    list_mechanisms = False
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-p", "--pin"):
            pin = a
            if pin == "NULL":
                pin = None
        if o in ("-s", "--slot"):
            slot = int(a)
        if o in ("-c", "--lib"):
            lib = a
        if o in ("-a", "--all"):
            token_present = False
        if o in ("-m", "--mechanisms"):
            list_mechanisms = True

    gi = getInfo(lib)
    gi.getInfo()

    slots = gi.pkcs11.getSlotList(token_present)
    print("Available Slots:", len(slots), slots)

    if len(slots) == 0:
        sys.exit(2)

    if slot is not None:
        slots = [slots[slot]]
        print("Using slot:", slots[0])

    slot_index = 0
    nb_slots = len(slots)
    for slot in slots:
        slot_index += 1
        try:
            gi.getSlotInfo(slot, slot_index, nb_slots)
            gi.getSessionInfo(slot, pin)
            gi.getTokenInfo(slot)
            if list_mechanisms:
                gi.getMechanismInfo(slot)
        except PyKCS11.PyKCS11Error as e:
            print("Error:", e)

Dump all the token objects

#!/usr/bin/env python3

#   Copyright (C) 2006-2014 Ludovic Rousseau (ludovic.rousseau@free.fr)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

import PyKCS11
import binascii
import getopt
import sys
import platform

# from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/142812
# Title: Hex dumper
# Submitter: Sebastien Keim (other recipes)
# Last Updated: 2002/08/05
# Version no: 1.0


def dump(src, length=16):
    def to_ascii(x):
        if x >= 32 and x <= 127:
            return chr(x)
        else:
            return '.'
    N = 0
    result = ""
    while src:
        s, src = src[:length], src[length:]
        text_hexa = " ".join(["%02X" % x for x in s])
        text_ascii = "".join(map(to_ascii , s))
        result += "%04X   %-*s   %s\n" % (N, length * 3, text_hexa, text_ascii)
        N += length
    return result


def usage():
    print("Usage:", sys.argv[0], end=" ")
    print("[-a][--all]", end=" ")
    print("[-p pin][--pin=pin] (use --pin=NULL for pinpad)", end=" ")
    print("[-c lib][--lib=lib]", end=" ")
    print("[-s slot][--slot=slot]", end=" ")
    print("[-h][--help]", end=" ")
    print()


try:
    opts, args = getopt.getopt(
        sys.argv[1:],
        "p:c:Sdhs:a",
        ["pin=", "lib=", "slot=", "help", "all"],
    )
except getopt.GetoptError:
    # print help information and exit:
    usage()
    sys.exit(2)

pin_available = False
lib = None
slot = None
token_present = True
for o, a in opts:
    if o in ("-h", "--help"):
        usage()
        sys.exit()
    elif o in ("-p", "--pin"):
        pin = a
        if pin == "NULL":
            pin = None
        pin_available = True
    elif o in ("-c", "--lib"):
        lib = a
        print("using PKCS11 lib:", lib)
    elif o in ("-s", "--slot"):
        slot = int(a)
    if o in ("-a", "--all"):
        token_present = False

red = blue = magenta = normal = ""
if sys.stdout.isatty() and platform.system().lower() != "windows":
    red = "\x1b[01;31m"
    blue = "\x1b[34m"
    magenta = "\x1b[35m"
    normal = "\x1b[0m"

format_long = magenta + "  %s:" + blue + " %s (%s)" + normal
format_binary = magenta + "  %s:" + blue + " %d bytes" + normal
format_normal = magenta + "  %s:" + blue + " %s" + normal

pkcs11 = PyKCS11.PyKCS11Lib()
pkcs11.load(lib)
info = pkcs11.getInfo()
print("Library manufacturerID:", info.manufacturerID)

slots = pkcs11.getSlotList(token_present)
print("Available Slots:", len(slots), slots)

if slot is not None:
    slots = [slots[slot]]
    print("Using slot:", slots[0])

for s in slots:
    try:
        i = pkcs11.getSlotInfo(s)
        print("Slot no:", s)
        print(format_normal % ("slotDescription", i.slotDescription.strip()))
        print(format_normal % ("manufacturerID ", i.manufacturerID.strip()))
        print(format_normal % ("hardwareVersion", i.hardwareVersion))
        print(format_normal % ("firmwareVersion", i.firmwareVersion))
        print(format_normal % ("flags          ", ", ".join(i.flags2text())))

        if not (i.flags & PyKCS11.CKF_TOKEN_PRESENT):
            print("  Token not present")
            continue

        t = pkcs11.getTokenInfo(s)
        print("TokenInfo")
        print(format_normal % ("label", t.label.strip()))
        print(format_normal % ("manufacturerID", t.manufacturerID.strip()))
        print(format_normal % ("model", t.model.strip()))

        session = pkcs11.openSession(s)
        print("Opened session 0x%08X" % session.session.value())
        if pin_available:
            try:
                if (pin is None) and (
                    PyKCS11.CKF_PROTECTED_AUTHENTICATION_PATH & t.flags
                ):
                    print("\nEnter your PIN for %s on the pinpad" % t.label.strip())
                session.login(pin=pin)
            except PyKCS11.PyKCS11Error as e:
                print("login failed, exception:", e)
                break

        objects = session.findObjects()
        print()
        print("Found %d objects: %s" % (len(objects), [x.value() for x in objects]))

        all_attributes = list(PyKCS11.CKA.keys())
        # remove the CKR_ATTRIBUTE_SENSITIVE attributes since we can't get
        # their values and will get an exception instead
        all_attributes.remove(PyKCS11.CKA_PRIVATE_EXPONENT)
        all_attributes.remove(PyKCS11.CKA_PRIME_1)
        all_attributes.remove(PyKCS11.CKA_PRIME_2)
        all_attributes.remove(PyKCS11.CKA_EXPONENT_1)
        all_attributes.remove(PyKCS11.CKA_EXPONENT_2)
        all_attributes.remove(PyKCS11.CKA_COEFFICIENT)
        # only use the integer values and not the strings like 'CKM_RSA_PKCS'
        all_attributes = [e for e in all_attributes if isinstance(e, int)]

        n_obj = 1
        for o in objects:
            print()
            print(
                (
                    red
                    + "==================== Object: %d/%d (%d) ===================="
                    + normal
                )
                % (n_obj, len(objects), o.value())
            )
            n_obj += 1
            try:
                attributes = session.getAttributeValue(o, all_attributes)
            except PyKCS11.PyKCS11Error as e:
                print("getAttributeValue failed:", e)
                continue
            attrDict = dict(list(zip(all_attributes, attributes)))

            print("Dumping attributes:")
            for q, a in zip(all_attributes, attributes):
                if a is None:
                    # undefined (CKR_ATTRIBUTE_TYPE_INVALID) attribute
                    continue
                if q == PyKCS11.CKA_CLASS:
                    print(format_long % (PyKCS11.CKA[q], PyKCS11.CKO[a], a))
                elif q == PyKCS11.CKA_CERTIFICATE_TYPE:
                    print(format_long % (PyKCS11.CKA[q], PyKCS11.CKC[a], a))
                elif q == PyKCS11.CKA_KEY_TYPE:
                    print(format_long % (PyKCS11.CKA[q], PyKCS11.CKK[a], a))
                elif session.isBin(q):
                    print(format_binary % (PyKCS11.CKA[q], len(a)))
                    if a:
                        print(dump(a), end="")
                elif q == PyKCS11.CKA_SERIAL_NUMBER:
                    print(format_binary % (PyKCS11.CKA[q], len(a)))
                    if a:
                        print(dump(a), end="")
                else:
                    print(format_normal % (PyKCS11.CKA[q], a))
        print()

        if pin_available:
            try:
                session.logout()
            except PyKCS11.PyKCS11Error as e:
                print("logout failed, exception:", e)
                break

        session.closeSession()

    except PyKCS11.PyKCS11Error as e:
        print("Error:", e)
        raise