Giter Site home page Giter Site logo

php-pkcs11's Introduction

PKCS11 bindings for PHP

Tested with the following HSMs:

  • SafeNET Luna SA 4
  • SoftHSM 2.6
  • Nitrokey HSM 2
  • AWS CloudHSM

Supports PHP version >= 7.4

Compile

phpize
./configure
make

Running tests

To make tests, ensure that SoftHSM2 is installed, configured and initialized.

Install SoftHSM2

From Source

  1. Get the source
  2. Extract Archive
  3. cd /path/to/extracted/archive
  4. ./configure
  5. make
  6. make install

Module will be located in /usr/local/lib/softhsm/libsofthsm2.so

From Ubuntu package repository

As of this writing, Ubuntu 20.04 provides v2.2 of SoftHSM2 in which not all mechanisms will be available

  1. sudo apt install softhsm2

Module will be located in /usr/lib/softhsm/libsofthsm2.so

Configure and initialize SoftHSM2

  1. Create a directory where HSM files will be stored /home/user/.softhsm
  2. Create a configuration file in your home directory ~/.config/softhsm2/softhsm2.conf
  3. Initialize token softhsm2-util --init-token --slot 0 --label "My token 1" --pin 123456 --so-pin 12345678
  4. Grab the resulting slot ID softhsm2-util --show-slots

Example configuration file

directories.tokendir = /home/user/.softhsm
objectstore.backend = file
log.level = INFO
slots.removable = false
slots.mechanisms = ALL

Run tests

export PHP11_MODULE=/path/to/libsofthsm2.so
export PHP11_SLOT={SLOT_ID_FROM_INITIALIZATION}
export PHP11_PIN=123456

make test

How to use

All examples assume the use of a locally compiled installation of SoftHSM, but it will work with other modules as well.

Loading a module

To load a PKCS11 module, create a new PKCS11\Module object.

$modulePath = '/path/to/libsofthsm2.so';
$module = new Pkcs11\Module($modulePath);

From the PKCS11\Module object, you can call most PKCS11 methods.

To get information the module:

$moduleInfo = $module->getInfo();

Slot Information

There are 2 methods to retrieve slots information. getSlotList can be used to retrieve a simple list of slots like C_GetSlotList would, while getSlots returns de result of C_GetSlotInfo for each available slot.

$slotList = $module->getSlotList();
$slots = $module->getSlots();

$slotId = $slotList[0];

$slotInfo = $module->getSlotInfo($slotId);

Token Information

$tokenInfo = $module->getTokenInfo($slotId);

Mechanisms

All mechanisms declared in PKCS11 version 3 are available under the Pkcs11 namespace.

$mechanismList = $module->getMechanismList($slotId);
$mechanismInfo = $module->getMechanismInfo($slotId, Pkcs11\CKM_AES_GCM);

Initializing a token

This extension supports initializing simple tokens via the C_InitToken function.

$module->initToken($slotId, $label, $soPin);

Opening a session

You can open a session and login as either a Security Officer or user. From the returned Pkcs11\Session object, more methods are available.

$session = $module->openSession($slotId, Pkcs11\CKF_RW_SESSION);
$session->login(Pkcs11\CKU_SO, $soPin);
$sessionInfo = $session->getInfo();
$session = $module->openSession($slotId, Pkcs11\CKF_RW_SESSION);
$session->login(Pkcs11\CKU_USER, $userPin);
$sessionInfo = $session->getInfo();

PIN Management

As a Security Officer, the user PIN can be set to any value.

$session->login(Pkcs11\CKU_SO, $soPin);
$session->initPin($userPin);
$session->logout();

As either user, the current user PIN can be changed.

$session->login(Pkcs11\CKU_SO, $soPin);
$session->setPin($soPin, $newPin);
$session->logout();
$session->login(Pkcs11\CKU_USER, $userPin);
$session->setPin($userPin, $newPin);
$session->logout();

Generating Keys

You can generate symmetric keys for any available mechanism.

$key = $session->generateKey(new Pkcs11\Mechanism(Pkcs11\CKM_AES_KEY_GEN), [
  Pkcs11\CKA_CLASS => Pkcs11\CKO_SECRET_KEY,
  Pkcs11\CKA_SENSITIVE => true,
  Pkcs11\CKA_ENCRYPT => true,
  Pkcs11\CKA_DECRYPT => true,
  Pkcs11\CKA_VALUE_LEN => 32,
  Pkcs11\CKA_KEY_TYPE => Pkcs11\CKK_AES,
  Pkcs11\CKA_LABEL => "Test AES",
  Pkcs11\CKA_PRIVATE => true,
]);
$keypair = $session->generateKeyPair(new Pkcs11\Mechanism(Pkcs11\CKM_EC_KEY_PAIR_GEN), [
  Pkcs11\CKA_VERIFY => true,
  Pkcs11\CKA_LABEL => "Test ECDSA Public",
  Pkcs11\CKA_EC_PARAMS => hex2bin('06082A8648CE3D030107'),
],[
  Pkcs11\CKA_TOKEN => false,
  Pkcs11\CKA_PRIVATE => true,
  Pkcs11\CKA_SENSITIVE => true,
  Pkcs11\CKA_SIGN => true,
  Pkcs11\CKA_LABEL => "Test ECDSA Private",
]);

$pkey = $keypair->pkey;
$skey = $keypair->skey;

Encrypt/Decrypt

Given a symmetric key, you can encrypt something. Certain encryption mechanisms require a special Parameters object. Currently, the following are available:

  • Pkcs11\GcmParams
  • Pkcs11\Salsa20Params
  • Pkcs11\ChaCha20Params
  • Pkcs11\Salsa20Chacha20Poly1305Params
$iv = random_bytes(16);
$aad = '';
$tagLength = 128;
$gcmParams = new Pkcs11\GcmParams($iv, $aad, $tagLength);

$data = 'Hello World!';
$mechanism = new Pkcs11\Mechanism(Pkcs11\CKM_AES_GCM, $gcmParams);
$ciphertext = $key->encrypt($mechanism, $data);
var_dump(bin2hex($ciphertext));
// string(56) "67940e19213d68c88d163b12d6cd565300f70d693309b5b744085b35"

$plaintext = $key->decrypt($mechanism, $ciphertext);
var_dump($plaintext);
// string(12) "Hello World!"

AWS CloudHSM does things in a particular way. The initialization vector is generated by the HSM for you and returned prepended to the ciphertext.

$aad = '';
$tagLength = 128;
// No need to specify the $iv
$gcmParams = new Pkcs11\GcmParams('', $aad, $tagLength);

$data = 'Hello World!';
$mechanism = new Pkcs11\Mechanism(Pkcs11\CKM_CLOUDHSM_AES_GCM, $gcmParams);
$ciphertext = $key->encrypt($mechanism, $data);
var_dump(bin2hex($ciphertext));
// string(88) "0000000000000000000000000000000067940e19213d68c88d163b12d6cd565300f70d693309b5b744085b35"

$plaintext = $key->decrypt($mechanism, $ciphertext);
var_dump($plaintext);
// string(12) "Hello World!"

Given an RSA public key, you can encrypt a symmetric key. Similarly to symmetric mechanisms, some asymmetric encryption mechanisms require a special Parameters object. Currently, the following are available:

  • Pkcs11\RsaOaepParams
$keypair = $session->generateKeyPair(new Pkcs11\Mechanism(Pkcs11\CKM_RSA_PKCS_KEY_PAIR_GEN), [
  Pkcs11\CKA_ENCRYPT => true,
  Pkcs11\CKA_MODULUS_BITS => 2048,
  Pkcs11\CKA_PUBLIC_EXPONENT => hex2bin('010001'),
  Pkcs11\CKA_LABEL => "Test RSA Encrypt Public",
],[
  Pkcs11\CKA_TOKEN => false,
  Pkcs11\CKA_PRIVATE => true,
  Pkcs11\CKA_SENSITIVE => true,
  Pkcs11\CKA_DECRYPT => true,
  Pkcs11\CKA_LABEL => "Test RSA Encrypt Private",
]);

// SoftHSM2 only supports CKG_MGF1_SHA1
$oaepParam = new Pkcs11\RsaOaepParams(Pkcs11\CKM_SHA_1, Pkcs11\CKG_MGF1_SHA1);
$mechanism = new Pkcs11\Mechanism(Pkcs11\CKM_RSA_PKCS_OAEP, $oaepParam);

$symkey = random_bytes(32);
$ciphertext = $keypair->pkey->encrypt($mechanism, $symkey);
var_dump($ciphertext);

$plaintext = $keypair->skey->decrypt($mechanism, $ciphertext);
var_dump($plaintext);

Derivation

Given an EC public key, you can derive a shared secret.

// P-384
$domainParameters = hex2bin('06052B81040022');
$rawPublickeyOther = hex2bin('049f0a09e8a6fc87f4804642c782b2cd3e566b3e62262090d94e12933a00916f559b62ea33197706a302f0722b781a9349ea8f0f2bcea854cdcf5d9ff0e0a19c3c35d63578292307d1d83031c0134700c2990ed5b38f6c92245103c2c1352132a3');

$keypair = $session->generateKeyPair(new Pkcs11\Mechanism(Pkcs11\CKM_EC_KEY_PAIR_GEN), [
  Pkcs11\CKA_LABEL => "Test ECDH Public",
  Pkcs11\CKA_EC_PARAMS => $domainParameters,
],[
  Pkcs11\CKA_PRIVATE => true,
  Pkcs11\CKA_SENSITIVE => true,
  Pkcs11\CKA_DERIVE => true,
  Pkcs11\CKA_LABEL => "Test ECDH Private",
]);

$shared = '';

// SoftHSM2 only supports CKD_NULL
$params = new Pkcs11\Ecdh1DeriveParams(Pkcs11\CKD_NULL, $shared, $rawPublickeyOther);
$mechanism = new Pkcs11\Mechanism(Pkcs11\CKM_ECDH1_DERIVE, $params);
$secret = $keypair->skey->derive($mechanism, [
  Pkcs11\CKA_CLASS => Pkcs11\CKO_SECRET_KEY,
  Pkcs11\CKA_KEY_TYPE => Pkcs11\CKK_AES,
  Pkcs11\CKA_SENSITIVE => false,
  Pkcs11\CKA_EXTRACTABLE => true,
  Pkcs11\CKA_ENCRYPT => true,
  Pkcs11\CKA_DECRYPT => true,
]);

$rawSecret = $secret->getAttributeValue([Pkcs11\CKA_VALUE])[Pkcs11\CKA_VALUE];

Retrieving object by URI

You have the ability to retrieve object using RFC7512 URIs.

Note: Currently, only the following path attributes are supported:

  • id
  • object
  • type
$session->generateKeyPair(new Pkcs11\Mechanism(Pkcs11\CKM_RSA_PKCS_KEY_PAIR_GEN), [
  Pkcs11\CKA_ENCRYPT => true,
  Pkcs11\CKA_MODULUS_BITS => 2048,
  Pkcs11\CKA_PUBLIC_EXPONENT => hex2bin('010001'),
  Pkcs11\CKA_LABEL => "testPkcs11Url",
  Pkcs11\CKA_ID => "testPkcs11UrlPublicId",
],[
  Pkcs11\CKA_TOKEN => false,
  Pkcs11\CKA_PRIVATE => true,
  Pkcs11\CKA_SENSITIVE => true,
  Pkcs11\CKA_DECRYPT => true,
  Pkcs11\CKA_LABEL => "testPkcs11Url",
  Pkcs11\CKA_ID => "testPkcs11UrlPrivateId",
]);

$privateKeySearchResult = $session->openUri("pkcs11:object=testPkcs11Url;type=private;");

$mechanism = new Pkcs11\Mechanism(Pkcs11\CKM_SHA256_RSA_PKCS);

$data = "Hello World!";
$signature = $privateKeySearchResult[0]->sign($mechanism, $data);
var_dump(bin2hex($signature));

$publicKeySearchResult = $session->openUri("pkcs11:id=testPkcs11UrlPublicId");
$valid = $publicKeySearchResult[0]->verify($mechanism, $data, $signature);
var_dump($valid);

Retrieve object attributes

Given an Object, you can retrieve it's readable attributes.

Note: the following attributes are not implemented and retrieving them throws an exception:

  • CKA_WRAP_TEMPLATE
  • CKA_UNWRAP_TEMPLATE
  • CKA_DERIVE_TEMPLATE

Note: the following attributes internally provide a struct describing the date, but are here returned as a string:

  • CKA_START_DATE
  • CKA_END_DATE
$keypair = $session->generateKeyPair(new Pkcs11\Mechanism(Pkcs11\CKM_RSA_PKCS_KEY_PAIR_GEN), /* ... */);

$attributes = $keypair->skey->getAttributeValue([
  Pkcs11\CKA_TOKEN,
  Pkcs11\CKA_PRIVATE,
  Pkcs11\CKA_SENSITIVE,
  Pkcs11\CKA_EXTRACTABLE,
  Pkcs11\CKA_NEVER_EXTRACTABLE,
  Pkcs11\CKA_ALWAYS_SENSITIVE,
  Pkcs11\CKA_SIGN,
  Pkcs11\CKA_DECRYPT,
  Pkcs11\CKA_PUBLIC_EXPONENT,
  Pkcs11\CKA_LABEL,
]);

var_dump($attributes[Pkcs11\CKA_TOKEN]);
var_dump($attributes[Pkcs11\CKA_PRIVATE]);
var_dump($attributes[Pkcs11\CKA_SENSITIVE]);
var_dump($attributes[Pkcs11\CKA_EXTRACTABLE]);
var_dump($attributes[Pkcs11\CKA_NEVER_EXTRACTABLE]);
var_dump($attributes[Pkcs11\CKA_ALWAYS_SENSITIVE]);
var_dump($attributes[Pkcs11\CKA_SIGN]);
var_dump($attributes[Pkcs11\CKA_DECRYPT]);
var_dump(bin2hex($attributes[Pkcs11\CKA_PUBLIC_EXPONENT]));
var_dump($attributes[Pkcs11\CKA_LABEL]);

/* Outputs:
bool(false)
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)
bool(true)
bool(false)
string(6) "010001"
string(16) "Test RSA Private"
*/

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.