I pretty much stumbled over sematicon and their N200 Series on my quest for Hardware Security Modules (HSM) of almost any shape and size, and after a very pleasant phone call with one of their managers, I was quickly given documentation to study and access to a dedicated device over a Wireguard VPN.
Every appliance features two or more independently configurable crypto processors, which work with a variety of keys. Therefore, test and production can be operated separately of each other on only one platform.
Although the N200 series isn’t specifically tailored to PKCS#11, it’s what I’m mainly interested in so what I focus on. I pretty much ignored the actual PKI capabilities of the device. It can be a certification authority (CA), produce X.509 certificates, perform symmetric encryption, and a load of other features. I toyed with creating a user and assigning access rules. The Web-based UI is neat and functional. There were some labels and captions which appeared confusing to me, but I got used to them.
PKCS#11 support is relatively new, and the documentation doesn’t yet mention about it, but I’m told that will change for the upcoming new version. A small interim PDF gave some tips, but I had a bit trouble actually using the interface because after initializing the device, I wasn’t aware that I had to create an access rule for a user who wants to use the PKCS#11 interface without which I kept getting an access denied. A brief meeting with the person who’s blog article initially led me to sematicon got me set up.
I copy the PKCS#11 sematicon-provided shared object library to the directory I want it in and configure the n200.conf
file, adding a high log level to begin with, but it turns out the level is ignored: setting it to 0 doesn’t change much. Beware the log: it will contain the user and password in clear, but I’ve reported this.
% echo "10.195.10.11 seSAM-nserie-101" >> /etc/hosts # avoid TLS verification errors on hostname
# the appliance has a cert with subject CN= this name
% install -m 555 pkcs11-sesam-x64-3.so /usr/local/lib
% export p=/usr/local/lib/pkcs11-sesam-x64-3.so
% cat $N200_CONF
{
"url": "https://10.195.10.11/n200/web/postv1",
"tlscert": "/etc/tls/server63.crt",
"user": "pkmaster",
"pass": "<password>",
"core": 1,
"keygen_pin": "12345678",
"log_level": 10,
"log_output": "/tmp/seSAM_p11.log"
}
% pkcs11-tool --module $p --show-info
Cryptoki version 3.0
Manufacturer sematiconAG
Library se.SAM N200 pkcs11 (ver 1.0)
Using slot 0 with a present token (0xc13cc61)
The pkcs11-sesam.so
speaks to the N200 via their REST API using curl, and judging by the number of CURL_GLOBAL_INIT
occurrences, there is plenty of opportunity to improve performance of the shared library. (I will gloss over performance, because the network connection I had to the HSM was so flaky as to not be funny.)
I’ve already mentioned the N200 has a REST API which I want to test. After learning how to do so in the Web UI, I use the API to generate a key-pair on the device:
% curl \
--cacert server63.crt \
-u $user:$pass \
--no-progress-meter \
-d command=genecckey \
-d core=1 \
-d curve=NIST \
-d keysize=256 \
-d acl=FFFF \
-d pin=12345678 \
-d nice_name=JP.example \
https://seSAM-nserie-101/n200/web/postv1
{
"command": "genecckey",
"core": 1,
"clustersync": "n/a",
"nice_name": "JP.example",
"publickey": "FACD4CF599AD0CBD43A5C38B294AF3DDC06FBC0883D419EEBCBF8039C132FC61FE972236B38A1C236E5EF0F1E78B64B31C9632C0D5B9CFA49AECDB89144A2C3C",
"result": "hsm010123F13C56131E1EEE0000000000000025"
}
% pkcs11-tool --module $p --token-label hsm010123F13C56131E1EEE00000025 --list-objects
Public Key Object; EC EC_POINT 256 bits
EC_POINT: 044104facd4cf599ad0cbd43a5c38b294af3ddc06fbc0883d419eebcbf8039c132fc61fe972236b38a1c236e5ef0f1e78b64b31c9632c0d5b9cfa49aecdb89144a2c3c
EC_PARAMS: 06082a8648ce3d030107
label: JP.example
ID: aaab68606be2
Usage: encrypt, verify
Access: local
Private Key Object; EC
label: JP.example
ID: aaab68606be2
Usage: decrypt, sign, non-repudiation
Access: sensitive, always sensitive, never extractable, local
Allowed mechanisms: ECDSA,ECDSA-SHA1,ECDSA-SHA224,ECDSA-SHA256,ECDSA-SHA384,ECDSA-SHA512
The result from the key generation has an identifier (also visible in the UI) which sadly has eight more zeroes in it than the token label in the slot; I don’t know if that’s on purpose. (A limit of 32 characters in a token slot name maybe?) I say sadly because it requires an extra step (pkcs11-tool --module $p -T
) to find the correct token label.
It’s time to sign our zone with our Combined Signing Key or Single Signing Key, both euphemisms for “I’m too poor to afford a second key pair”.
% cat $OPENSSL_CONF
openssl_conf = openssl_init
[openssl_init]
engines=engine_section
[engine_section]
pkcs11 = pkcs11_section
[pkcs11_section]
engine_id = pkcs11
dynamic_path = /usr/lib64/engines-3/libpkcs11.so
MODULE_PATH = /usr/local/lib/pkcs11-sesam-x64-3.so
init = 1
After configuring OpenSSL’s engine to use our pkcs11-sesam.so module, I generate the pair of key files referencing the key material in the HSM. Use of the PIN value here depends on the setting in the HSM proper: the N200 can store the PIN on the device in order to not require it on the client, IMO six of one and half-a-dozen of the other in terms of security.
% cat jp.example
$TTL 3600
@ SOA ns jpmens 1 3H 1H 1W 1H
NS @
A 192.0.2.43
% dnssec-keyfromlabel -E pkcs11 -a13 -l "token=hsm010123F13C56131E1EEE00000025;object=JP.example;pin-value=12345678" -fKSK jp.example
Kjp.example.+013+29467
% dnssec-signzone -E pkcs11 -t -z -S -o jp.example jp.example
Fetching jp.example/ECDSAP256SHA256/29467 (KSK) from key repository.
Verifying the zone using the following algorithms:
- ECDSAP256SHA256
Zone fully signed:
Algorithm: ECDSAP256SHA256: KSKs: 1 active, 0 stand-by, 0 revoked
ZSKs: 0 active, 0 stand-by, 0 revoked
jp.example.signed
Signatures generated: 5
Signatures retained: 0
Signatures dropped: 0
Signatures successfully verified: 0
Signatures unsuccessfully verified: 0
Signing time in seconds: 5.302
Signatures per second: 0.942
Runtime in seconds: 5.984
I wrote earlier that I would gloss over performance, so I will. I’ve no idea where the device is (network-wise) so I’m taking these numbers with spoon-fulls of salt. (To be fair: I wouldn’t expect miracles if the device were close to me because of the seemingly large amount of HTTP requests I see in the log; recall that each DNS and NSEC{3} record needs to be signed and causes a HTTP request.)
I see in the Web UI that I can import a key to the device and try a PKCS#8-wrapped key, but that doesn’t work for me, but the API should work, and it does:
#!/usr/bin/env python3
import requests
import json
url = "https://seSAM-nserie-101/n200/web/postv1"
data = {
"command" : "importecckey",
"core" : 1,
"curve" : "NIST",
"acl" : "FFFF",
"pin" : "12345678",
"nice_name" : "example-dot-com-EC",
"key_to_import" : "443A5A0F630559075B8ED453E69FB134E73BED96B7E7695D55AFBBFC085278CD"
}
r = requests.post(url, data, verify='server63.crt')
print("status: %s, %s" % (r.status_code,
json.dumps(json.loads(r.text), indent=4)))
The RAW key data for key_to_import
I obtained from the ECDSA key I want to import:
% dnssec-keygen -a13 -fksk example.com
Generating key pair.
Kexample.com.+013+37354
% awk '/PrivateKey:/ { print $2 }' Kexample.com.+013+37354.private | base64 -d | ./hexlify
443A5A0F630559075B8ED453E69FB134E73BED96B7E7695D55AFBBFC085278CD
% ./jpimport.py
status: 200, {
"command": "importecckey",
"core": 1,
"result": "hsm010123F13C56131E1EEE0000000000000018"
}
There’s a so-called “TCP-RAW-API” which allows direct access to the crypto processor as well as an HTTP GET API which uses the URL to transport a request to the appliance; the response to this is JSON. Here are a few examples including symmetric encryption and decryption. (I’ve not yet tried the factoryreset
…) In the following examples I omit specifying the certificate verification option in the interest of brevity.
% curl https://seSAM-nserie-101/n200/timestamp/
{"command": "timestamp", "result": "1664657599"}
% curl https://seSAM-nserie-101/n200/getrandom/5/
{"command": "getrandom", "numbers": "5", "core": 1, "result": "EBFB604875"}
% curl https://seSAM-nserie-101/n200/getkeyattributes/k01/
{
"command": "getkeyattributes",
"result": {
"hsm010123F13C56131E1EEE0000000000000026": {
"key_id": "hsm010123F13C56131E1EEE0000000000000026",
"key_type": "SYM",
"key_len": 256,
"key_acl": "FFFF",
"key_owner": "pkmaster",
"nice_name": "k01"
}
}
}
% curl https://seSAM-nserie-101/n200/encrypt/k01/12345678/CBC/hello-world/
{
"command": "encrypt",
"core": 1,
"result": "A84663E043099BC74C11628DAFB69B6D8FAABCFBF4FC4AF33657B719C96617B772D229AB8A9A0E7B3903C6E404121980"
}
% curl https://seSAM-nserie-101/n200/decrypt/k01/12345678/CBC/A84663E043099BC74C11628DAFB69B6D8FAABCFBF4FC4AF33657B719C96617B772D229AB8A9A0E7B3903C6E404121980/
{
"command": "decrypt",
"core": 1,
"result": "hello-world"
}
% curl https://seSAM-nserie-101/n200/listkeys/NIST/
{
"command": "listkeys",
"core": 0,
"result": {
...
"hsm010123F13C56131E1EEE0000000000000025": {
"slot": "hsm010123F13C56131E1EEE00000025",
"key_id": "hsm010123F13C56131E1EEE0000000000000025",
"id": "AAAB68606BE2",
"key_type": "NIST",
"key_len": 256,
"key_acl": "FFFF",
"nice_name": "JP.example",
"date_of_creation": "2022/10/01 20:15",
"date_of_last_use": "2022/10/01 20:32",
"date_planned_lifetime_until": null,
"token_name": "hsm010123F13C56131E1EEE00000025",
"key_owner": "pkmaster",
"public_key": "FACD4CF599AD0CBD43A5C38B294AF3DDC06FBC0883D419EEBCBF8039C132FC61FE972236B38A1C236E5EF0F1E78B64B31C9632C0D5B9CFA49AECDB89144A2C3C"
}
}
}
The sematicon N200 is a network-attached HSM and is positioned as an IoT-specialized device, and I felt comfortable using it after a bit of study. The documentation is good, and the chapter on APIs describes each call in detail for the three access methods (RAW, GET, POST).
My specific use-case (signing DNS records via a PKCS#11 interface) is not a forte of the appliance, but I was told that from the outset. In spite of this, I was positively surprised that all the PKCS#11 functions required by the utilities I used were supported.
As an all-purpose HSM this appears to be a very interesting device at a price far below what the market “leaders” demand for a networked appliance.