この章では、暗号化用の PKCS #11 関数を使用したユーザーレベルのアプリケー ションとプロバイダを開発する方法について説明します。次の項目について説明します。
暗号化フレームワークについての詳細は、第 8 章Oracle Solaris 暗号化フレームワークの紹介を参照してください。
Solaris 暗号化フレームワーク内のユーザーレベルアプリケーションは、libpkcs11.so モジュールで提供される cryptoki ライブラリ経由で PKCS #11 関数にアクセスします。pkcs11_softtoken.so モジュールは Sun Microsystems, Inc. が提供する PKCS #11 ソフトトークン実装であり、さまざまな暗号化機構を提供します。このソフトトークンプラグインが機構のデフォルトソースになります。暗号化機構の提供は、Sun 以外のプラグインを通じて行うことも可能です。
ここでは、このソフトトークンによってサポートされている PKCS #11 関数と戻り値のリストを示します。戻りコードは、フレームワークにプラグインされるプロバイダごとに異なります。また、いくつかの一般的な関数についても説明します。cryptoki ライブラリに含まれるすべての要素の完全な説明が必要な場合は、libpkcs11(3LIB) のマニュアルページを参照するか、RSA Laboratories Web サイトの「PKCS #11: Cryptographic Token Interface Standard」を参照してください。
次のリストは、Solaris 暗号化フレームワーク内の pkcs11_softtoken.so がサポートする PKCS #11 関数をカテゴリ別に示したものです。
一般用途 – C_Initialize()、C_Finalize()、C_GetInfo()、C_GetFunctionList()
セッション管理 – C_OpenSession()、C_CloseSession()、C_GetSessionInfo()、C_CloseAllSessions()、C_Login()、C_Logout()
スロット管理およびトークン管理 – C_GetSlotList()、C_GetSlotInfo()、C_GetMechanismList()、C_GetMechanismInfo()、C_SetPIN()
暗号化と復号化 – C_EncryptInit()、C_Encrypt()、C_EncryptUpdate()、C_EncryptFinal()、C_DecryptInit()、C_Decrypt()、 C_DecryptUpdate()、C_DecryptFinal()
メッセージダイジェスト – C_DigestInit()、C_Digest()、C_DigestKey()、C_DigestUpdate()、C_DigestFinal()
署名と MAC 適用 – C_Sign()、C_SignInit()、C_SignUpdate()、C_SignFinal()、C_SignRecoverInit()、C_SignRecover()
署名検証 – C_Verify()、C_VerifyInit()、C_VerifyUpdate()、C_VerifyFinal()、C_VerifyRecoverInit()、C_VerifyRecover()
二重目的の暗号化関数 – C_DigestEncryptUpdate()、C_DecryptDigestUpdate()、C_SignEncryptUpdate()、C_DecryptVerifyUpdate()
乱数生成 – C_SeedRandom()、C_GenerateRandom()
オブジェクト管理 – C_CreateObject()、C_DestroyObject()、C_CopyObject()、C_FindObjects()、C_FindObjectsInit()、C_FindObjectsFinal()、 C_GetAttributeValue()、C_SetAttributeValue()
鍵管理 – C_GenerateKey()、C_GenerateKeyPair()、C_DeriveKey()
ここでは、PKCS #11 を使用するための関数のうち、次のものについて説明します。
libpkcs11.so ライブラリではすべての PKCS #11 関数が利用可能になっています。C_GetFunctionList() 関数を使用して利用可能な関数のリストを取得する必要はありません。
C_Initialize() は PKCS #11 ライブラリを初期化します。C_Initialize() の構文は次のとおりです。
C_Initialize(CK_VOID_PTR pInitArgs);
pInitArgs は、空値 NULL_PTR または CK_C_INITIALIZE_ARGS 構造体へのポインタです。NULL_PTR が指定された場合、ライブラリは Solaris の相互排他ロックをロックプリミティブとして使用することで、複数スレッドによる内部共有構造体へのアクセスを制御します。Solaris 暗号化フレームワークは相互排他ロックを受け入れないことに注意してください。この実装版の cryptoki ライブラリは、マルチスレッド処理を安全かつ効率的に処理しますので、NULL_PTR を使用することをお勧めします。またアプリケーション内で、pInitArgs を使用して CKF_LIBRARY_CANT_CREATE_OS_THREADS などのフラグを設定することも可能です。C_Finalize() は、アプリケーションが PKCS #11 ライブラリを使用し終わったことを通知します。
C_Finalize() は決してライブラリ内から呼び出してはいけません。一般に、C_Finalize() を呼び出してセッションをクローズすることはアプリケーションの責任です。
CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY、CKR_OK に加え、C_Initialize() は次の戻り値を使用します。
CKR_ARGUMENTS_BAD
CKR_CANT_LOCK
CKR_CRYPTOKI_ALREADY_INITIALIZED – このエラーは致命的ではありません。
C_GetInfo() は、cryptoki ライブラリに関する開発元情報とバージョン情報を取得します。C_GetInfo() の構文は次のとおりです。
C_GetInfo(CK_INFO_PTR pInfo);
C_GetInfo() は次の値を返します。
cryptokiVersion = 2, 11
manufacturerID = Sun Microsystems, Inc.
CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY、CKR_OK に加え、C_GetInfo() は次の戻り値を使用します。
CKR_ARGUMENTS_BAD
CKR_CRYPTOKI_NOT_INITIALIZED
C_GetSlotList() は、利用可能なスロットのリストを取得します。pkcs11_softtoken.so 以外の暗号化プロバイダがインストールされていない場合、C_GetSlotList() から返されるのは、デフォルトのスロットだけです。C_GetSlotList() の構文は次のとおりです。
C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount);
tokenPresent に TRUE を設定した場合、トークンが存在するスロットだけに検索対象が限定されます。
pSlotList に NULL_PTR を設定した場合、C_GetSlotlist() はスロット数だけを返します。pulCount はスロット数を格納する場所へのポインタです。
pSlotList にスロットを格納するバッファーへのポインタを設定した場合、*pulCount には CK_SLOT_ID 要素の予想最大数を設定します。戻り時には、*pulCount に CK_SLOT_ID 要素の実際の個数が設定されます。
通常、PKCS #11 アプリケーションは C_GetSlotList() を 2 回呼び出します。1 回目の C_GetSlotList() 呼び出しでは、メモリーを割り当てる目的でスロット数を取得します。そして 2 回目の C_GetSlotList() 呼び出しでは、スロットを取得します。
スロットの順序は保証されません。スロットの順序は、PKCS #11 ライブラリを読み込むたびに変わる可能性があります。
CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY、CKR_OK に加え、C_GetSlotlist() は次の戻り値を使用します。
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
C_GetTokenInfo() は、特定のトークンに関する情報を取得します。 C_GetTokenInfo() の構文は次のとおりです。
C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo);
slotID は、目的のトークンに対するスロットの ID です。slotID は、C_GetSlotList() から返された有効な ID でなければなりません。pInfo は、トークン情報を格納する場所へのポインタです。
pkcs11_softtoken.so がインストールされている唯一のプロバイダである場合、C_GetTokenInfo() は次のフィールドと値を返します。
ラベル – Sun Software PKCS#11 softtoken。
フラグ – CKF_DUAL_CRYPTO_OPERATIONS、CKF_TOKEN_INITIALIZED、CKF_RNG、CKF_USER_PIN_INITIALIZED、および CKF_LOGIN_REQUIRED。これらのフラグには 1 が設定されます。
ulMaxSessionCount – CK_EFFECTIVELY_INFINITE が設定されます。
ulMaxRwSessionCount – CK_EFFECTIVELY_INFINITE が設定されます。
ulMaxPinLen – 256 が設定されます。
ulMinPinLen – 1 が設定されます。
ulTotalPublicMemory には CK_UNAVAILABLE_INFORMATION が設定されます。
ulFreePublicMemory には CK_UNAVAILABLE_INFORMATION が設定されます。
ulTotalPrivateMemory には CK_UNAVAILABLE_INFORMATION が設定されます。
ulFreePrivateMemory には CK_UNAVAILABLE_INFORMATION が設定されます。
CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY、CKR_OK に加え、C_GetSlotlist() は次の戻り値を使用します。
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
次の戻り値は、プラグインされたハードウェアトークンに関するものです。
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
C_OpenSession() を使えば、アプリケーションは特定のスロット内の特定のトークンとの間で暗号化セッションを開始できます。C_OpenSession() の構文は次のとおりです。
C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession);
slotID はスロットの ID です。flags は、セッションを読み書き可能にするか、あるいは読み取り専用にするかを示します。pApplication は、アプリケーションによってコールバック用として定義されたポインタです。Notify には、オプションのコールバック関数のアドレスが格納されます。phSession は、セッションハンドルの格納場所へのポインタです。
CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY、CKR_OK に加え、C_OpenSession() は次の戻り値を使用します。
CKR_ARGUMENTS_BAD
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
CKR_TOKEN_WRITE_PROTECTED – 読み取り専用のトークンで戻されます。
次の戻り値は、プラグインされたハードウェアトークンに関するものです。
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_SESSION_COUNT
CKR_SESSION_PARALLEL_NOT_SUPPORTED
CKR_SESSION_READ_WRITE_SO_EXISTS
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
C_GetMechanismList() は、指定されたトークンがサポートする機構タイプのリストを取得します。C_GetMechanismList() の構文は次のとおりです。
C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount);
slotID は、目的のトークンに対するスロットの ID です。pulCount は機構数を格納する場所へのポインタです。pMechanismList に NULL_PTR を設定した場合、*pulCount に機構数が返されます。それ以外の場合、*pulCount にはリストのサイズを、pMechanismList にはリストを格納するバッファーへのポインタを、それぞれ設定する必要があります。
PKCS #11 ソフトトークンがプラグインされている場合、C_GetMechanismList() から返されるサポート機構リストは、次のとおりです。
CKM_AES_CBC
CKM_AES_CBC_PAD
CKM_AES_ECB
CKM_AES_KEY_GEN
CKM_DES_CBC
CKM_DES_CBC_PAD
CKM_DES_ECB
CKM_DES_KEY_GEN
CKM_DES_MAC
CKM_DES_MAC_GENERAL
CKM_DES3_CBC
CKM_DES3_CBC_PAD
CKM_DES3_ECB
CKM_DES3_KEY_GEN
CKM_DH_PKCS_DERIVE
CKM_DH_PKCS_KEY_PAIR_GEN
CKM_DSA
CKM_DSA_KEY_PAIR_GEN
CKM_DSA_SHA_1
CKM_MD5
CKM_MD5_KEY_DERIVATION
CKM_MD5_RSA_PKCS
CKM_MD5_HMAC
CKM_MD5_HMAC_GENERAL
CKM_PBE_SHA1_RC4_128
CKM_PKCS5_PBKD2
CKM_RC4
CKM_RC4_KEY_GEN
CKM_RSA_PKCS
CKM_RSA_X_509
CKM_RSA_PKCS_KEY_PAIR_GEN
CKM_SHA_1
CKM_SHA_1_HMAC_GENERAL
CKM_SHA_1_HMAC
CKM_SHA_1_KEY_DERIVATION
CKM_SHA_1_RSA_PKCS
CKM_SSL3_KEY_AND_MAC_DERIVE
CKM_SSL3_MASTER_KEY_DERIVE
CKM_SSL3_MASTER_KEY_DERIVE_DH
CKM_SSL3_MD5_MAC
CKM_SSL3_PRE_MASTER_KEY_GEN
CKM_SSL3_SHA1_MAC
CKM_TLS_KEY_AND_MAC_DERIVE
CKM_TLS_MASTER_KEY_DERIVE
CKM_TLS_MASTER_KEY_DERIVE_DH
CKM_TLS_PRE_MASTER_KEY_GEN
CKR_FUNCTION_FAILED、CKR_GENERAL_ERROR、CKR_HOST_MEMORY、CKR_OK に加え、C_GetSlotlist() は次の戻り値を使用します。
CKR_ARGUMENTS_BAD
CKR_BUFFER_TOO_SMALL
CKR_CRYPTOKI_NOT_INITIALIZED
CKR_SLOT_ID_INVALID
次の戻り値は、プラグインされたハードウェアトークンに関するものです。
CKR_DEVICE_ERROR
CKR_DEVICE_MEMORY
CKR_DEVICE_REMOVED
CKR_TOKEN_NOT_PRESENT
CKR_TOKEN_NOT_RECOGNIZED
Solaris 暗号化フレームワークでは、標準の PKCS #11 関数のほかに、次の 2 つの簡易関数が提供されています。
SUNW_C_GetMechSession() は、簡易関数です。この関数はまず、Solaris 暗号化フレームワークを初期化します。続いてこの関数は、指定された機構との間でセッションを開始します。SUNW_C_GetMechSession() の構文は次のとおりです。
SUNW_C_GetMechSession(CK_MECHANISM_TYPE mech, C\ K_SESSION_HANDLE_PTR hSession)
mech パラメータでは、使用する機構を指定します。hSession は、セッションの格納場所へのポインタです。
SUNW_C_GetMechSession() は内部的に、C_Initialize() を呼び出して cryptoki ライブラリを初期化します。続いて SUNW_C_GetMechSession() は、C_GetSlotList() と C_GetMechanismInfo() を使って利用可能なスロットを検索し、指定された機構を備えたトークンを見つけ出します。目的の機構が見つかった場合、SUNW_C_GetMechSession() は C_OpenSession() を呼び出してセッションをオープンします。
SUNW_C_GetMechSession() は何度も呼び出す必要はありません。しかし、仮に SUNW_C_GetMechSession() を複数回呼び出したとしても、特に問題は生じません。
SUNW_C_KeyToObject() は、秘密鍵オブジェクトを作成します。呼び出し元のプログラムは、使用すべき機構と未処理の鍵データを指定する必要があります。SUNW_C_KeyToObject() は内部的に、指定された機構に対する鍵の種類を決定します。C_CreateObject() によって汎用的な鍵オブジェクトが作成されます。続いて SUNW_C_KeyToObject() は、 C_GetSessionInfo() と C_GetMechanismInfo() を呼び出してスロットと機構の情報を取得します。そして、C_SetAttributeValue() によって、鍵オブジェクトの属性フラグが機構の種類に従って設定されます。
この節では、次の例について説明します。
この例では、PKCS #11 関数を使って入力ファイルからダイジェストを作成します。この例では、次の手順を実行します。
ダイジェストの機構を指定します。
この例では、CKM_MD5 ダイジェスト機構が使用されます。
指定されたダイジェストアルゴリズムをサポートするスロットを検索します。
この例では、Sun の簡易関数 SUNW_C_GetMechSession() を使用しています。SUNW_C_GetMechSession() は、cryptoki ライブラリをオープンします。このライブラリには、Solaris 暗号化フレームワークで使用されるすべての PKCS #11 関数が含まれています。続いて SUNW_C_GetMechSession() は、目的の機構を備えたスロットを検索します。そして、セッションが開始されます。この簡易関数は事実上、C_Initialize() 呼び出し、C_OpenSession() 呼び出し、および指定された機構をサポートするスロットの検索に必要なすべてのコードと同等の機能を備えています。
cryptoki の情報を取得します。
この部分は実際には、メッセージダイジェストの作成に直接関係しませんが、C_GetInfo() 関数の使用法を示す意味でここに含めてあります。この例では開発元の ID を取得しています。その他の情報のオプションとして、バージョンとライブラリデータも取得できます。
スロットを使ってダイジェスト処理を実行します。
この作業におけるメッセージダイジェストの作成手順は次のとおりです。
入力ファイルをオープンします。
C_DigestInit() を呼び出してダイジェスト処理を初期化します。
C_DigestUpdate() を使ってデータを一度に一部分ずつ処理していきます。
ダイジェスト処理を終了するために、C_DigestFinal() を使って完全なダイジェストを取得します。
セッションを終了します。
このプログラムは、C_CloseSession() を使ってセッションをクローズし、C_Finalize() を使ってライブラリをクローズします。
メッセージダイジェストのソースコード例を、次に示します。
このソースコード例は、Sun ダウンロードセンターからダウンロードすることも可能です。http://www.sun.com/download/products.xml?id=41912db5 を参照してください。
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>
#define BUFFERSIZ 8192
#define MAXDIGEST 64
/* Calculate the digest of a user supplied file. */
void
main(int argc, char **argv)
{
CK_BYTE digest[MAXDIGEST];
CK_INFO info;
CK_MECHANISM mechanism;
CK_SESSION_HANDLE hSession;
CK_SESSION_INFO Info;
CK_ULONG ulDatalen = BUFFERSIZ;
CK_ULONG ulDigestLen = MAXDIGEST;
CK_RV rv;
CK_SLOT_ID SlotID;
int i, bytes_read = 0;
char inbuf[BUFFERSIZ];
FILE *fs;
int error = 0;
/* Specify the CKM_MD5 digest mechanism as the target */
mechanism.mechanism = CKM_MD5;
mechanism.pParameter = NULL_PTR;
mechanism.ulParameterLen = 0;
/* Use SUNW convenience function to initialize the cryptoki
* library, and open a session with a slot that supports
* the mechanism we plan on using. */
rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
if (rv != CKR_OK) {
fprintf(stderr, "SUNW_C_GetMechSession: rv = 0x%.8X\n", rv);
exit(1);
}
/* Get cryptoki information, the manufacturer ID */
rv = C_GetInfo(&info);
if (rv != CKR_OK) {
fprintf(stderr, "WARNING: C_GetInfo: rv = 0x%.8X\n", rv);
}
fprintf(stdout, "Manufacturer ID = %s\n", info.manufacturerID);
/* Open the input file */
if ((fs = fopen(argv[1], "r")) == NULL) {
perror("fopen");
fprintf(stderr, "\n\tusage: %s filename>\n", argv[0]);
error = 1;
goto exit_session;
}
/* Initialize the digest session */
if ((rv = C_DigestInit(hSession, &mechanism)) != CKR_OK) {
fprintf(stderr, "C_DigestInit: rv = 0x%.8X\n", rv);
error = 1;
goto exit_digest;
}
/* Read in the data and create digest of this portion */
while (!feof(fs) && (ulDatalen = fread(inbuf, 1, BUFFERSIZ, fs)) > 0) {
if ((rv = C_DigestUpdate(hSession, (CK_BYTE_PTR)inbuf,
ulDatalen)) != CKR_OK) {
fprintf(stderr, "C_DigestUpdate: rv = 0x%.8X\n", rv);
error = 1;
goto exit_digest;
}
bytes_read += ulDatalen;
}
fprintf(stdout, "%d bytes read and digested!!!\n\n", bytes_read);
/* Get complete digest */
ulDigestLen = sizeof (digest);
if ((rv = C_DigestFinal(hSession, (CK_BYTE_PTR)digest,
&ulDigestLen)) != CKR_OK) {
fprintf(stderr, "C_DigestFinal: rv = 0x%.8X\n", rv);
error = 1;
goto exit_digest;
}
/* Print the results */
fprintf(stdout, "The value of the digest is: ");
for (i = 0; i < ulDigestLen; i++) {
fprintf(stdout, "%.2x", digest[i]);
}
fprintf(stdout, "\nDone!!!\n");
exit_digest:
fclose(fs);
exit_session:
(void) C_CloseSession(hSession);
exit_program:
(void) C_Finalize(NULL_PTR);
exit(error);
}
例 9–2 は、DES アルゴリズムの CBC モードを使って暗号化するための鍵オブジェクトを作成します。このソースコードは次の手順を実行します。
鍵データを宣言します。
DES と初期化ベクトルを定義します。ここでは初期化ベクトルを静的に宣言していますが、これはあくまでもデモ用です。初期化ベクトルは常に動的に定義し、決して再利用しないでください。
鍵オブジェクトを定義します。
この作業では、鍵のテンプレートを設定する必要があります。
指定された暗号化機構をサポートするスロットを検索します。
この例では、Sun の簡易関数 SUNW_C_GetMechSession() を使用しています。SUNW_C_GetMechSession() は、cryptoki ライブラリをオープンします。このライブラリには、Solaris 暗号化フレームワークで使用されるすべての PKCS #11 関数が含まれています。続いて SUNW_C_GetMechSession() は、目的の機構を備えたスロットを検索します。そして、セッションが開始されます。この簡易関数は事実上、C_Initialize() 呼び出し、C_OpenSession() 呼び出し、および指定された機構をサポートするスロットの検索に必要なすべてのコードと同等の機能を備えています。
このスロットで暗号化処理を実行します。
この作業における暗号化の実行手順は次のとおりです。
このスロットで復号化処理を実行します。
この作業における復号化の実行手順は次のとおりです。この復号化処理は、テスト目的でのみ提供されています。
セッションを終了します。
このプログラムは、C_CloseSession() を使ってセッションをクローズし、C_Finalize() を使ってライブラリをクローズします。
対称暗号化のソースコード例を、次に示します。
このソースコード例は、Sun ダウンロードセンターからダウンロードすることも可能です。http://www.sun.com/download/products.xml?id=41912db5 を参照してください。
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>
#define BUFFERSIZ 8192
/* Declare values for the key materials. DO NOT declare initialization
* vectors statically like this in real life!! */
uchar_t des_key[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
uchar_t des_cbc_iv[] = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef};
/* Key template related definitions. */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_OBJECT_CLASS class = CKO_SECRET_KEY;
static CK_KEY_TYPE keyType = CKK_DES;
/* Example encrypts and decrypts a file provided by the user. */
void
main(int argc, char **argv)
{
CK_RV rv;
CK_MECHANISM mechanism;
CK_OBJECT_HANDLE hKey;
CK_SESSION_HANDLE hSession;
CK_ULONG ciphertext_len = 64, lastpart_len = 64;
long ciphertext_space = BUFFERSIZ;
CK_ULONG decrypttext_len;
CK_ULONG total_encrypted = 0;
CK_ULONG ulDatalen = BUFFERSIZ;
int i, bytes_read = 0;
int error = 0;
char inbuf[BUFFERSIZ];
FILE *fs;
uchar_t ciphertext[BUFFERSIZ], *pciphertext, decrypttext[BUFFERSIZ];
/* Set the key object */
CK_ATTRIBUTE template[] = {
{CKA_CLASS, &class, sizeof (class) },
{CKA_KEY_TYPE, &keyType, sizeof (keyType) },
{CKA_TOKEN, &falsevalue, sizeof (falsevalue) },
{CKA_ENCRYPT, &truevalue, sizeof (truevalue) },
{CKA_VALUE, &des_key, sizeof (des_key) }
};
/* Set the encryption mechanism to CKM_DES_CBC_PAD */
mechanism.mechanism = CKM_DES_CBC_PAD;
mechanism.pParameter = des_cbc_iv;
mechanism.ulParameterLen = 8;
/* Use SUNW convenience function to initialize the cryptoki
* library, and open a session with a slot that supports
* the mechanism we plan on using. */
rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
if (rv != CKR_OK) {
fprintf(stderr, "SUNW_C_GetMechSession: rv = 0x%.8X\n", rv);
exit(1);
}
/* Open the input file */
if ((fs = fopen(argv[1], "r")) == NULL) {
perror("fopen");
fprintf(stderr, "\n\tusage: %s filename>\n", argv[0]);
error = 1;
goto exit_session;
}
/* Create an object handle for the key */
rv = C_CreateObject(hSession, template,
sizeof (template) / sizeof (CK_ATTRIBUTE),
&hKey);
if (rv != CKR_OK) {
fprintf(stderr, "C_CreateObject: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
/* Initialize the encryption operation in the session */
rv = C_EncryptInit(hSession, &mechanism, hKey);
if (rv != CKR_OK) {
fprintf(stderr, "C_EncryptInit: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
/* Read in the data and encrypt this portion */
pciphertext = &ciphertext[0];
while (!feof(fs) && (ciphertext_space > 0) &&
(ulDatalen = fread(inbuf, 1, ciphertext_space, fs)) > 0) {
ciphertext_len = ciphertext_space;
/* C_EncryptUpdate is only being sent one byte at a
* time, so we are not checking for CKR_BUFFER_TOO_SMALL.
* Also, we are checking to make sure we do not go
* over the alloted buffer size. A more robust program
* could incorporate realloc to enlarge the buffer
* dynamically. */
rv = C_EncryptUpdate(hSession, (CK_BYTE_PTR)inbuf, ulDatalen,
pciphertext, &ciphertext_len);
if (rv != CKR_OK) {
fprintf(stderr, "C_EncryptUpdate: rv = 0x%.8X\n", rv);
error = 1;
goto exit_encrypt;
}
pciphertext += ciphertext_len;
total_encrypted += ciphertext_len;
ciphertext_space -= ciphertext_len;
bytes_read += ulDatalen;
}
if (!feof(fs) || (ciphertext_space < 0)) {
fprintf(stderr, "Insufficient space for encrypting the file\n");
error = 1;
goto exit_encrypt;
}
/* Get the last portion of the encrypted data */
lastpart_len = ciphertext_space;
rv = C_EncryptFinal(hSession, pciphertext, &lastpart_len);
if (rv != CKR_OK) {
fprintf(stderr, "C_EncryptFinal: rv = 0x%.8X\n", rv);
error = 1;
goto exit_encrypt;
}
total_encrypted += lastpart_len;
fprintf(stdout, "%d bytes read and encrypted. Size of the "
"ciphertext: %d!\n\n", bytes_read, total_encrypted);
/* Print the encryption results */
fprintf(stdout, "The value of the encryption is:\n");
for (i = 0; i < ciphertext_len; i++) {
if (ciphertext[i] < 16)
fprintf(stdout, "0%x", ciphertext[i]);
else
fprintf(stdout, "%2x", ciphertext[i]);
}
/* Initialize the decryption operation in the session */
rv = C_DecryptInit(hSession, &mechanism, hKey);
/* Decrypt the entire ciphertext string */
decrypttext_len = sizeof (decrypttext);
rv = C_Decrypt(hSession, (CK_BYTE_PTR)ciphertext, total_encrypted,
decrypttext, &decrypttext_len);
if (rv != CKR_OK) {
fprintf(stderr, "C_Decrypt: rv = 0x%.8X\n", rv);
error = 1;
goto exit_encrypt;
}
fprintf(stdout, "\n\n%d bytes decrypted!!!\n\n", decrypttext_len);
/* Print the decryption results */
fprintf(stdout, "The value of the decryption is:\n%s", decrypttext);
fprintf(stdout, "\nDone!!!\n");
exit_encrypt:
fclose(fs);
exit_session:
(void) C_CloseSession(hSession);
exit_program:
(void) C_Finalize(NULL_PTR);
exit(error);
}
ここで説明する例は、RSA 鍵ペアを生成します。そのあと、その鍵ペアで単純な文字列を署名および検証します。この例では、次の手順を実行します。
鍵オブジェクトを定義します。
公開鍵のテンプレートを設定します。
非公開鍵のテンプレートを設定します。
メッセージ例を作成します。
鍵ペアを生成する genmech 機構を指定します。
鍵ペアに署名する smech 機構を指定します。
cryptoki ライブラリを初期化します。
署名、検証、および鍵ペア生成用の機構を備えたスロットを検索します。この作業では、getMySlot() という名前の関数を使って次の手順を実行します。
関数 C_GetSlotList() を呼び出して利用可能なスロットのリストを取得します。
PKCS #11 の規約でも推奨されているように、C_GetSlotList() は 2 回呼び出されます。1 回目の C_GetSlotList() 呼び出しでは、メモリーを割り当てる目的でスロット数を取得します。そして 2 回目の C_GetSlotList() 呼び出しでは、スロットを取得します。
目的の機構を提供できるスロットを検索します。
この関数はスロットごとに、GetMechanismInfo() を呼び出して署名用の機構と鍵ペア生成用の機構を検索します。それらの機構がそのスロットでサポートされていない場合、GetMechanismInfo() はエラーを返します。GetMechanismInfo() が成功を返した場合、機構のフラグを検査し、その機構が必要な処理を実行できることを確認します。
C_OpenSession() を呼び出してセッションをオープンします。
セッションをクローズします。
このプログラムは、C_CloseSession() を使ってセッションをクローズし、C_Finalize() を使ってライブラリをクローズします。
署名と検証のソースコード例を、次に示します。
このソースコード例は、Sun ダウンロードセンターからダウンロードすることも可能です。http://www.sun.com/download/products.xml?id=41912db5 を参照してください。
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>
#define BUFFERSIZ 8192
/* Define key template */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_ULONG modulusbits = 1024;
static CK_BYTE public_exponent[] = {3};
boolean_t GetMySlot(CK_MECHANISM_TYPE sv_mech, CK_MECHANISM_TYPE kpgen_mech,
CK_SLOT_ID_PTR pslot);
/* Example signs and verifies a simple string, using a public/private
* key pair. */
void
main(int argc, char **argv)
{
CK_RV rv;
CK_MECHANISM genmech, smech;
CK_SESSION_HANDLE hSession;
CK_SESSION_INFO sessInfo;
CK_SLOT_ID slotID;
int error, i = 0;
CK_OBJECT_HANDLE privatekey, publickey;
/* Set public key. */
CK_ATTRIBUTE publickey_template[] = {
{CKA_VERIFY, &truevalue, sizeof (truevalue)},
{CKA_MODULUS_BITS, &modulusbits, sizeof (modulusbits)},
{CKA_PUBLIC_EXPONENT, &public_exponent,
sizeof (public_exponent)}
};
/* Set private key. */
CK_ATTRIBUTE privatekey_template[] = {
{CKA_SIGN, &truevalue, sizeof (truevalue)},
{CKA_TOKEN, &falsevalue, sizeof (falsevalue)},
{CKA_SENSITIVE, &truevalue, sizeof (truevalue)},
{CKA_EXTRACTABLE, &truevalue, sizeof (truevalue)}
};
/* Create sample message. */
CK_ATTRIBUTE getattributes[] = {
{CKA_MODULUS_BITS, NULL_PTR, 0},
{CKA_MODULUS, NULL_PTR, 0},
{CKA_PUBLIC_EXPONENT, NULL_PTR, 0}
};
CK_ULONG messagelen, slen, template_size;
boolean_t found_slot = B_FALSE;
uchar_t *message = (uchar_t *)"Simple message for signing & verifying.";
uchar_t *modulus, *pub_exponent;
char sign[BUFFERSIZ];
slen = BUFFERSIZ;
messagelen = strlen((char *)message);
/* Set up mechanism for generating key pair */
genmech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
genmech.pParameter = NULL_PTR;
genmech.ulParameterLen = 0;
/* Set up the signing mechanism */
smech.mechanism = CKM_RSA_PKCS;
smech.pParameter = NULL_PTR;
smech.ulParameterLen = 0;
/* Initialize the CRYPTOKI library */
rv = C_Initialize(NULL_PTR);
if (rv != CKR_OK) {
fprintf(stderr, "C_Initialize: Error = 0x%.8X\n", rv);
exit(1);
}
found_slot = GetMySlot(smech.mechanism, genmech.mechanism, &slotID);
if (!found_slot) {
fprintf(stderr, "No usable slot was found.\n");
goto exit_program;
}
fprintf(stdout, "selected slot: %d\n", slotID);
/* Open a session on the slot found */
rv = C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
&hSession);
if (rv != CKR_OK) {
fprintf(stderr, "C_OpenSession: rv = 0x%.8X\n", rv);
error = 1;
goto exit_program;
}
fprintf(stdout, "Generating keypair....\n");
/* Generate Key pair for signing/verifying */
rv = C_GenerateKeyPair(hSession, &genmech, publickey_template,
(sizeof (publickey_template) / sizeof (CK_ATTRIBUTE)),
privatekey_template,
(sizeof (privatekey_template) / sizeof (CK_ATTRIBUTE)),
&publickey, &privatekey);
if (rv != CKR_OK) {
fprintf(stderr, "C_GenerateKeyPair: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
/* Display the publickey. */
template_size = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);
rv = C_GetAttributeValue(hSession, publickey, getattributes,
template_size);
if (rv != CKR_OK) {
/* not fatal, we can still sign/verify if this failed */
fprintf(stderr, "C_GetAttributeValue: rv = 0x%.8X\n", rv);
error = 1;
} else {
/* Allocate memory to hold the data we want */
for (i = 0; i < template_size; i++) {
getattributes[i].pValue =
malloc (getattributes[i].ulValueLen *
sizeof(CK_VOID_PTR));
if (getattributes[i].pValue == NULL) {
int j;
for (j = 0; j < i; j++)
free(getattributes[j].pValue);
goto sign_cont;
}
}
/* Call again to get actual attributes */
rv = C_GetAttributeValue(hSession, publickey, getattributes,
template_size);
if (rv != CKR_OK) {
/* not fatal, we can still sign/verify if failed */
fprintf(stderr,
"C_GetAttributeValue: rv = 0x%.8X\n", rv);
error = 1;
} else {
/* Display public key values */
fprintf(stdout, "Public Key data:\n\tModulus bits: "
"%d\n",
*((CK_ULONG_PTR)(getattributes[0].pValue)));
fprintf(stdout, "\tModulus: ");
modulus = (uchar_t *)getattributes[1].pValue;
for (i = 0; i < getattributes[1].ulValueLen; i++) {
fprintf(stdout, "%.2x", modulus[i]);
}
fprintf(stdout, "\n\tPublic Exponent: ");
pub_exponent = (uchar_t *)getattributes[2].pValue;
for (i = 0; i< getattributes[2].ulValueLen; i++) {
fprintf(stdout, "%.2x", pub_exponent[i]);
}
fprintf(stdout, "\n");
}
}
sign_cont:
rv = C_SignInit(hSession, &smech, privatekey);
if (rv != CKR_OK) {
fprintf(stderr, "C_SignInit: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
rv = C_Sign(hSession, (CK_BYTE_PTR)message, messagelen,
(CK_BYTE_PTR)sign, &slen);
if (rv != CKR_OK) {
fprintf(stderr, "C_Sign: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
fprintf(stdout, "Message was successfully signed with private key!\n");
rv = C_VerifyInit(hSession, &smech, publickey);
if (rv != CKR_OK) {
fprintf(stderr, "C_VerifyInit: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
rv = C_Verify(hSession, (CK_BYTE_PTR)message, messagelen,
(CK_BYTE_PTR)sign, slen);
if (rv != CKR_OK) {
fprintf(stderr, "C_Verify: rv = 0x%.8X\n", rv);
error = 1;
goto exit_session;
}
fprintf(stdout, "Message was successfully verified with public key!\n");
exit_session:
(void) C_CloseSession(hSession);
exit_program:
(void) C_Finalize(NULL_PTR);
for (i = 0; i < template_size; i++) {
if (getattributes[i].pValue != NULL)
free(getattributes[i].pValue);
}
exit(error);
}
/* Find a slot capable of:
* . signing and verifying with sv_mech
* . generating a key pair with kpgen_mech
* Returns B_TRUE when successful. */
boolean_t GetMySlot(CK_MECHANISM_TYPE sv_mech, CK_MECHANISM_TYPE kpgen_mech,
CK_SLOT_ID_PTR pSlotID)
{
CK_SLOT_ID_PTR pSlotList = NULL_PTR;
CK_SLOT_ID SlotID;
CK_ULONG ulSlotCount = 0;
CK_MECHANISM_INFO mech_info;
int i;
boolean_t returnval = B_FALSE;
CK_RV rv;
/* Get slot list for memory alloction */
rv = C_GetSlotList(0, NULL_PTR, &ulSlotCount);
if ((rv == CKR_OK) && (ulSlotCount > 0)) {
fprintf(stdout, "slotCount = %d\n", ulSlotCount);
pSlotList = malloc(ulSlotCount * sizeof (CK_SLOT_ID));
if (pSlotList == NULL) {
fprintf(stderr, "System error: unable to allocate "
"memory\n");
return (returnval);
}
/* Get the slot list for processing */
rv = C_GetSlotList(0, pSlotList, &ulSlotCount);
if (rv != CKR_OK) {
fprintf(stderr, "GetSlotList failed: unable to get "
"slot count.\n");
goto cleanup;
}
} else {
fprintf(stderr, "GetSlotList failed: unable to get slot "
"list.\n");
return (returnval);
}
/* Find a slot capable of specified mechanism */
for (i = 0; i < ulSlotCount; i++) {
SlotID = pSlotList[i];
/* Check if this slot is capable of signing and
* verifying with sv_mech. */
rv = C_GetMechanismInfo(SlotID, sv_mech, &mech_info);
if (rv != CKR_OK) {
continue;
}
if (!(mech_info.flags & CKF_SIGN &&
mech_info.flags & CKF_VERIFY)) {
continue;
}
/* Check if the slot is capable of key pair generation
* with kpgen_mech. */
rv = C_GetMechanismInfo(SlotID, kpgen_mech, &mech_info);
if (rv != CKR_OK) {
continue;
}
if (!(mech_info.flags & CKF_GENERATE_KEY_PAIR)) {
continue;
}
/* If we get this far, this slot supports our mechanisms. */
returnval = B_TRUE;
*pSlotID = SlotID;
break;
}
cleanup:
if (pSlotList)
free(pSlotList);
return (returnval);
}
例 9–4 は、ランダムバイトを生成できる機構を備えたスロットを検索する方法を示しています。この例では、次の手順を実行します。
cryptoki ライブラリを初期化します。
GetRandSlot() を呼び出して、ランダムバイトを生成できる機構を備えたスロットを検索します。
この作業におけるスロット検索手順は次のとおりです。
関数 C_GetSlotList() を呼び出して利用可能なスロットのリストを取得します。
PKCS #11 の規約で推奨されているように、C_GetSlotList() は 2 回呼び出されます。1 回目の C_GetSlotList() 呼び出しでは、メモリーを割り当てる目的でスロット数を取得します。そして 2 回目の C_GetSlotList() 呼び出しでは、スロットを取得します。
ランダムバイトを生成できるスロットを検索します。
この関数はスロットごとに、GetTokenInfo() を使ってトークン情報を取得し、CKF_RNG フラグセットを含むエントリの有無を検査します。CKF_RNG フラグセットを含むスロットが見つかった場合、GetRandSlot() 関数が戻ります。
セッションを終了します。
このプログラムは、C_CloseSession() を使ってセッションをクローズし、C_Finalize() を使ってライブラリをクローズします。
乱数生成のソースコード例を、次に示します。
このソースコード例は、Sun ダウンロードセンターからダウンロードすることも可能です。http://www.sun.com/download/products.xml?id=41912db5 を参照してください。
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>
#define RANDSIZE 64
boolean_t GetRandSlot(CK_SLOT_ID_PTR pslot);
/* Example generates random bytes. */
void
main(int argc, char **argv)
{
CK_RV rv;
CK_MECHANISM mech;
CK_SESSION_HANDLE hSession;
CK_SESSION_INFO sessInfo;
CK_SLOT_ID slotID;
CK_BYTE randBytes[RANDSIZE];
boolean_t found_slot = B_FALSE;
int error;
int i;
/* Initialize the CRYPTOKI library */
rv = C_Initialize(NULL_PTR);
if (rv != CKR_OK) {
fprintf(stderr, "C_Initialize: Error = 0x%.8X\n", rv);
exit(1);
}
found_slot = GetRandSlot(&slotID);
if (!found_slot) {
goto exit_program;
}
/* Open a session on the slot found */
rv = C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
&hSession);
if (rv != CKR_OK) {
fprintf(stderr, "C_OpenSession: rv = 0x%.8x\n", rv);
error = 1;
goto exit_program;
}
/* Generate random bytes */
rv = C_GenerateRandom(hSession, randBytes, RANDSIZE);
if (rv != CKR_OK) {
fprintf(stderr, "C_GenerateRandom: rv = 0x%.8x\n", rv);
error = 1;
goto exit_session;
}
fprintf(stdout, "Random value: ");
for (i = 0; i < RANDSIZE; i++) {
fprintf(stdout, "%.2x", randBytes[i]);
}
exit_session:
(void) C_CloseSession(hSession);
exit_program:
(void) C_Finalize(NULL_PTR);
exit(error);
}
boolean_t
GetRandSlot(CK_SLOT_ID_PTR pslot)
{
CK_SLOT_ID_PTR pSlotList;
CK_SLOT_ID SlotID;
CK_TOKEN_INFO tokenInfo;
CK_ULONG ulSlotCount;
CK_MECHANISM_TYPE_PTR pMechTypeList = NULL_PTR;
CK_ULONG ulMechTypecount;
boolean_t result = B_FALSE;
int i = 0;
CK_RV rv;
/* Get slot list for memory allocation */
rv = C_GetSlotList(0, NULL_PTR, &ulSlotCount);
if ((rv == CKR_OK) && (ulSlotCount > 0)) {
fprintf(stdout, "slotCount = %d\n", (int)ulSlotCount);
pSlotList = malloc(ulSlotCount * sizeof (CK_SLOT_ID));
if (pSlotList == NULL) {
fprintf(stderr,
"System error: unable to allocate memory\n");
return (result);
}
/* Get the slot list for processing */
rv = C_GetSlotList(0, pSlotList, &ulSlotCount);
if (rv != CKR_OK) {
fprintf(stderr, "GetSlotList failed: unable to get "
"slot list.\n");
free(pSlotList);
return (result);
}
} else {
fprintf(stderr, "GetSlotList failed: unable to get slot"
" count.\n");
return (result);
}
/* Find a slot capable of doing random number generation */
for (i = 0; i < ulSlotCount; i++) {
SlotID = pSlotList[i];
rv = C_GetTokenInfo(SlotID, &tokenInfo);
if (rv != CKR_OK) {
/* Check the next slot */
continue;
}
if (tokenInfo.flags & CKF_RNG) {
/* Found a random number generator */
*pslot = SlotID;
fprintf(stdout, "Slot # %d supports random number "
"generation!\n", SlotID);
result = B_TRUE;
break;
}
}
if (pSlotList)
free(pSlotList);
return (result);
}
ユーザーレベルのプロバイダの例については、OpenSolaris の pkcs11_softtoken の例を参照してください。これは実際に動作するプロバイダで、独自のプロバイダを作成するときの基盤として使用できます。
このプロバイダのコードを利用するには、OpenSolaris の Web サイト http://src.opensolaris.org/source/ にアクセスします。「File Path」フィールドに「pkcs11_softtoken/common」と入力し、onnv プロジェクトが選択されていることを確認し、「Search」ボタンをクリックします。