0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PyKCS11を使ったHSM操作

Posted at

PyKCS11 は、Python で PKCS#11 インタフェース関数を利用するラッパーライブラリで、マイナンバーカードの電子署名ツールとしても紹介されているライブラリです。
他には python-pkcs11 というライブラリもあります。

テスト環境の準備

SoftHSM のインストール

  • Debian Linux

    sudo apt install -y softhsm2 libsofthsm2 opensc
    

SoftHSM の初期化

  • 初期化

    softhsm2-util \
        --init-token \
        --slot 0 \
        --label "SoftHSMToken" \
        --so-pin 4321 \
        --pin 1234
    
  • トークンの確認

    softhsm2-util --show-slots
    
    pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -L
    
pkcs11-tool での操作
  • サポートアルゴリズム一覧の確認

    pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -M
    
  • 登録鍵一覧取得

    pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -O \
        --login --pin 1234
    
  • 鍵作成

    • 対称鍵

      pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
          --login --pin 1234 \
          --keygen \
          --key-type aes:32 \
          --id 01 \
          --label "aeskey"
      
    • RSA 鍵

      pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
          --login --pin 1234 \
          --keypairgen \
          --key-type rsa:4096 \
          --id 02 \
          --label "rsakey"
      

      公開鍵のエクスポート

      pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
          --login --pin 1234 \
          --read-object \
          --type pubkey \
          --id 02 \
          --output-file public_key.der
      

      DER 形式 ⇒ PEM 形式に変換

      openssl rsa \
          -in public_key.der \
          -inform der -pubin \
          -outform pem \
          -out public_key.pem
      
    • EC 鍵 (NIST P-256 の例)

      pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
          --login --pin 1234 \
          --keypairgen \
          --key-type EC:secp256r1 \
          --id 03 \
          --label "eckey"
      
  • 鍵の削除

    • 対称鍵

      • ID 指定削除

        pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
            --login --pin 1234 \
            --delete -d 01 -y secrkey
        
      • Label 指定削除

        pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so \
            --login --pin 1234 \
            --delete -y secrkey --label "aeskey"
        

サンプルコード

対称鍵で暗号・復号
import binascii
import secrets

from PyKCS11 import PyKCS11

""" 設定 """
# PKCS11 モジュールのパス
PKCS11_LIB = "/usr/lib/softhsm/libsofthsm2.so"

# 暗号化対称データ
plaintext = b"I hate working overtime..."
""" 設定 """

# PKCS11 モジュールをロード
pkcs11 = PyKCS11.PyKCS11Lib()
pkcs11.load(PKCS11_LIB)

# スロットを取得し、セッションを開始
slot = pkcs11.getSlotList(tokenPresent=True)[0]
session = pkcs11.openSession(slot, PyKCS11.CKF_RW_SESSION | PyKCS11.CKF_SERIAL_SESSION)

# ログイン
session.login("1234")

# AES 256 キーを作成
key_template = [
    (PyKCS11.CKA_LABEL, "aes-key"),
    (PyKCS11.CKA_ID, binascii.unhexlify("9876")),
    (PyKCS11.CKA_VALUE_LEN, 32),  # 32バイト = 256ビット
    (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),  # トークンに保存
    (PyKCS11.CKA_PRIVATE, PyKCS11.CK_TRUE),
]
# AES-256 対称鍵の生成
key_handle = session.generateKey(key_template)

""" 暗号化・復号 """
# イニシャルベクタ (16バイト)
# iv = b"\x00" * 16
iv = secrets.token_bytes(16)

# 暗号化
enc_mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
ciphertext = session.encrypt(key_handle, plaintext, enc_mechanism)
print("Encrypted:", binascii.hexlify(bytearray(ciphertext)))

# 復号
decrypted = session.decrypt(key_handle, ciphertext, enc_mechanism)

print("Decrypted:", decrypted)
# print(bytes(decrypted).decode("utf-8"))

# 暗号化・復号の検証
assert plaintext == bytes(decrypted)

# 鍵削除
session.destroyObject(key_handle)

# セッションを閉じる
session.logout()
session.closeSession()
暗号化鍵の Wrap/UnWrap
import binascii
import secrets

from PyKCS11 import PyKCS11

""" 設定 """
# PKCS11 モジュールのパス
PKCS11_LIB = "/usr/lib/softhsm/libsofthsm2.so"

# 暗号化対称データ
plaintext = b"I hate working overtime..."
""" 設定 """


def encrypt_decrypt(
    key_handle: PyKCS11.LowLevel.CK_OBJECT_HANDLE, plaintext: bytes
) -> None:
    """暗号・復号テスト

    指定された鍵ハンドルを使用して、指定した平文を暗号化⇒復号できることを確認する。

    Args:
        key_handle (PyKCS11.LowLevel.CK_OBJECT_HANDLE): 暗号化に使用する鍵ハンドル
        plaintext (bytes): 暗号化する平文

    Returns: None
    """
    # イニシャルベクタ (16バイト)
    iv = secrets.token_bytes(16)

    # 暗号化
    enc_mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
    ciphertext = session.encrypt(key_handle, plaintext, enc_mechanism)
    print("Encrypted:", binascii.hexlify(bytearray(ciphertext)))

    # 復号
    dec_mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_CBC_PAD, iv)
    decrypted = session.decrypt(key_handle, ciphertext, dec_mechanism)
    print("Decrypted:", decrypted)
    assert plaintext == bytes(decrypted)


# PKCS11 モジュールをロード
pkcs11 = PyKCS11.PyKCS11Lib()
pkcs11.load(PKCS11_LIB)

# スロットを取得し、セッションを開始
slot = pkcs11.getSlotList(tokenPresent=True)[0]
session = pkcs11.openSession(slot, PyKCS11.CKF_RW_SESSION | PyKCS11.CKF_SERIAL_SESSION)

# ログイン
session.login("1234")

# AES 256 キーを作成
key_template = [
    (PyKCS11.CKA_LABEL, "aes-key"),
    (PyKCS11.CKA_ID, binascii.unhexlify("9876")),
    (PyKCS11.CKA_VALUE_LEN, 32),  # 32バイト = 256ビット
    (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),  # トークンに保存
    (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
    (PyKCS11.CKA_EXTRACTABLE, PyKCS11.CK_TRUE),
]
# AES-256 対称鍵の生成
enckey_handle = session.generateKey(key_template)
wrapkey_handle = session.generateKey(key_template)

# 暗号鍵のエクスポート
# wrap using CKM_AES_KEY_WRAP
wrap_mechanism = PyKCS11.Mechanism(PyKCS11.CKM_AES_KEY_WRAP)
wrapped_key = session.wrapKey(wrapkey_handle, enckey_handle, wrap_mechanism)
assert wrapped_key is not None

# 暗号化鍵を削除
session.destroyObject(enckey_handle)

# unwrap
template = [
    (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY),
    (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES),
    (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_PRIVATE, PyKCS11.CK_FALSE),
    (PyKCS11.CKA_ENCRYPT, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_DECRYPT, PyKCS11.CK_TRUE),
    (PyKCS11.CKA_SIGN, PyKCS11.CK_FALSE),
    (PyKCS11.CKA_VERIFY, PyKCS11.CK_FALSE),
]
unwrappedkey_handle = session.unwrapKey(
    wrapkey_handle, wrapped_key, template, wrap_mechanism
)
assert unwrappedkey_handle is not None

# 暗号・復号テスト
encrypt_decrypt(unwrappedkey_handle, plaintext)

# 鍵削除
session.destroyObject(unwrappedkey_handle)
session.destroyObject(wrapkey_handle)

# セッションを閉じる
session.logout()
session.closeSession()
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?