Я написал простой скрипт на Python, который создает цифровую подпись с использованием моей смарт-карты (Rutoken ECP SC), библиотеки PKCS # 11 (реализованной моим поставщиком) и оболочки PyKCS11 для Python.
Я уже сгенерировал пару секретный / открытый ключ, используя эту карту, и создал подпись, используя pkcs11, но я не знаю, как проверить эти подписанные данные.
Проблема в том, что моя библиотека pkcs11 (реализованная в c ++) имеет методы проверки, а обертка PyKCS11 — нет. И сейчас я не знаю, как решить эту проблему. Я буду признателен, если кто-нибудь может сказать мне, как решить эту проблему.
Это мой сценарий:
import PyKCS11
import getopt
import sys
import platform
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()
lib_path = "/usr/lib/pkcs11-arm/rtpkcs11ecp/librtpkcs11ecp.so"pkcs11.load(lib_path)
info = pkcs11.getInfo()
print "Library manufacturerID: " + info.manufacturerID
slots = pkcs11.getSlotList()
print "Available Slots:", len(slots)
# As I understand we need only first slot
if len(slots) > 0:
slot = slots[0]
slotInfo = pkcs11.getSlotInfo(slot)
tokenInfo = pkcs11.getTokenInfo(slot)
flags = PyKCS11.CKF_RW_SESSION
session = pkcs11.openSession(slot, flags)
print "Opened session 0x%08X" % session.session.value()
pin = "12345678"session.login(pin)
objects = session.findObjects()
all_attributes = PyKCS11.CKA.keys() # all keys supported by SC
print "Defining KEY_GENERATION mechanism"mech = PyKCS11.Mechanism(PyKCS11.CKM_RSA_PKCS_KEY_PAIR_GEN, None)print "Generating key"public_template = [
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY),
(PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
(PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
(PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
(PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE),
(PyKCS11.CKA_WRAP, PyKCS11.CK_TRUE),
(PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_RSA),
(PyKCS11.CKA_VERIFY_RECOVER, PyKCS11.CK_TRUE),
(PyKCS11.CKA_MODULUS_BITS, 2048),
]
private_template = [
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY),
(PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
(PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
(PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
(PyKCS11.CKA_SIGN, PyKCS11.CK_TRUE),
(PyKCS11.CKA_UNWRAP, PyKCS11.CK_TRUE)
]
(pub, priv) = session.generateKeyPair(public_template, private_template, mech)
# ==================================================
# Signing data
sourceText = "Hello World"binaryData = ' '.join(format(ord(x), 'b') for x in sourceText)
signMechanism = PyKCS11.Mechanism(PyKCS11.CKM_RSA_PKCS, None)
signedData = session.sign(priv, binaryData, signMechanism)
print signedData#====================================================
# now we have to verify signedData using the private keysession.logout()
session.closeSession()
print "Close session 0x%08X" % session.session.value()
Это вывод метода sign:
[83L, 29L, 52L, 93L, 228L, 220L, 13L, 187L, 224L, 212L, 112L, 204L, 198L, 91L, 207L, 6L, 215L, 38L, 233L, 194L, 252L, 140L, 106L, 62L, 69L, 94L, 252L, 89L, 194L, 18L, 58L, 240L, 174L, 2L, 26L, 212L, 152L, 134L, 40L, 67L, 163L, 53L, 226L, 74L, 15L, 47L, 200L, 131L, 58L, 199L, 22L, 103L, 145L, 235L, 196L, 117L, 196L, 78L, 160L, 223L, 118L, 0L, 147L, 91L, 9L, 146L, 218L, 142L, 1L, 47L, 192L, 20L, 96L, 230L, 77L, 242L, 124L, 232L, 77L, 130L, 207L, 226L, 165L, 108L, 241L, 198L, 33L, 9L, 79L, 238L, 35L, 53L, 127L, 31L, 118L, 167L, 4L, 84L, 158L, 98L, 171L, 37L, 221L, 208L, 80L, 17L, 142L, 61L, 207L, 204L, 17L, 94L, 38L, 136L, 44L, 161L, 191L, 131L, 237L, 213L, 108L, 175L, 14L, 31L, 61L, 2L, 85L, 6L, 104L, 226L, 201L, 71L, 141L, 243L, 72L, 2L, 142L, 83L, 87L, 140L, 1L, 83L, 26L, 93L, 96L, 96L, 207L, 217L, 222L, 168L, 78L, 221L, 158L, 199L, 213L, 82L, 212L, 45L, 62L, 14L, 22L, 128L, 68L, 76L, 205L, 247L, 124L, 23L, 69L, 123L, 68L, 116L, 239L, 49L, 130L, 207L, 43L, 194L, 9L, 4L, 55L, 35L, 51L, 21L, 233L, 198L, 121L, 212L, 61L, 244L, 117L, 98L, 174L, 173L, 209L, 252L, 218L, 51L, 63L, 217L, 160L, 18L, 45L, 167L, 161L, 79L, 10L, 130L, 80L, 63L, 234L, 48L, 155L, 66L, 84L, 116L, 186L, 42L, 119L, 250L, 177L, 206L, 90L, 117L, 159L, 98L, 165L, 70L, 141L, 39L, 108L, 212L, 33L, 20L, 163L, 181L, 113L, 177L, 201L, 129L, 108L, 182L, 94L, 14L, 200L, 213L, 22L, 29L, 182L, 45L, 16L, 242L, 227L, 242L, 192L, 42L]
Этот код работает для меня (будьте осторожны, предполагается, что публичный показатель имеет длину 3 байта):
import PyKCS11
import getopt
import sys
import platform
import hashlib
from M2Crypto import RSA
pkcs11 = PyKCS11.PyKCS11Lib()
lib_path = "/opt/safenet/protecttoolkit5/ptk/lib/libcryptoki.so"pkcs11.load(lib_path)
info = pkcs11.getInfo()
slots = pkcs11.getSlotList()
if len(slots) > 0:
session = pkcs11.openSession(slots[0], PyKCS11.CKF_RW_SESSION)
session.login("12345678")
mech = PyKCS11.Mechanism(PyKCS11.CKM_RSA_PKCS_KEY_PAIR_GEN, None)
public_template = [
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PUBLIC_KEY),
(PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
(PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
(PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
(PyKCS11.CKA_VERIFY, PyKCS11.CK_TRUE),
(PyKCS11.CKA_WRAP, PyKCS11.CK_TRUE),
(PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_RSA),
(PyKCS11.CKA_VERIFY_RECOVER, PyKCS11.CK_TRUE),
(PyKCS11.CKA_MODULUS_BITS, 2048),
]
private_template = [
(PyKCS11.CKA_CLASS, PyKCS11.CKO_PRIVATE_KEY),
(PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
(PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
(PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
(PyKCS11.CKA_SIGN, PyKCS11.CK_TRUE),
(PyKCS11.CKA_UNWRAP, PyKCS11.CK_TRUE)
]
(pub, priv) = session.generateKeyPair(public_template, private_template, mech)
(pubExp,pubModulus) = session.getAttributeValue(pub,[PyKCS11.CKA_PUBLIC_EXPONENT,PyKCS11.CKA_MODULUS], True)
# ==================================================
# Signing data
binaryData = "Hello world"# Generate SHA1
sha1 = hashlib.sha1()
sha1.update(str(bytearray(binaryData)))
digest=sha1.digest()
# Indicate SHA1 is used
binaryData2='\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'+digest
signMechanism = PyKCS11.Mechanism(PyKCS11.CKM_RSA_PKCS, None)
signedData = session.sign(priv, binaryData2, signMechanism)
session.logout()
session.closeSession()
# ==================================================
# Verify
pubkey = RSA.new_pub_key(('\x00\x00\x00\x03' + str(bytearray(pubExp)), '\x00\x00\x01\x01\x00'+str(bytearray(pubModulus))))
result=pubkey.verify(str(bytearray(digest)), str(bytearray(signedData)), 'sha1')
print "VERIFY:" + str(result)
Я не в питоне, поэтому, пожалуйста, примите это как доказательство концепции, а не как решение. Для интересных частей:
так как ваш драйвер PKCS # 11 не поддерживает подпись RSA с хешем, необходимо вычислить хеш и построить DigestInfo ASN.1 часть вручную (результат находится в binaryData2
переменная)
как RSA.new_pub_key()
принимает кортеж в формате openssl для BN_mpi2bn (который он использует внутри), это было необходимо, чтобы префикс модуля с одним дополнительным \x00
чтобы убедиться, что оно интерпретируется как положительное число ( '\x00\x00\x01\x01\x00'
часть)
учитывая функцию verify()
использует OpenSSL RSA_verify который принимает в качестве аргумента дайджест подписанных данных (а не сами данные), которые необходимо было подчинить и дать ему дайджест (который повторно используется в части генерации подписи, и вам придется генерировать свежую, если вы планируете иметь отдельную функцию проверки)
Примечание: например, SHA256, вам нужно будет использовать соответствующий префикс строки digestInfo magic ASN.1 (см. Вот для полезных значений) + соответствующий объект дайджеста из hashlib + правильный 3-й аргумент проверки вызова.
Удачи!
Других решений пока нет …