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操作

Last updated at Posted at 2025-02-01

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 での SoftHSM 操作
  • サポートアルゴリズム一覧の確認

    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"
        

サンプルコード

Welcome to PyKCS11’s documentation — PyKCS11 1.5.17 documentation に API ドキュメントはあるのですが、関数単位の解説を読んでも、ある程度使い方を把握した人じゃないとわからない気がしました。

ドキュメントの最後の方にある PyKCS11 samples codesテストコード に動作するコードがあるので、それをベースにカスタマイズした方が要領が得られると思います。

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

from PyKCS11 import PyKCS11

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

# User PIM
PIN = "1234"

# 暗号化対称データ
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(PIN)

# 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"

# User PIN
PIN = "1234"

# 暗号化対称データ
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(PIN)

# 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?