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 " 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": "",
   "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.)

Web UI

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

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.

Web details of the created ECC key

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”.

openssl_conf = openssl_init


pkcs11 = 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   @

% dnssec-keyfromlabel -E pkcs11 -a13 -l "token=hsm010123F13C56131E1EEE00000025;object=JP.example;pin-value=12345678" -fKSK jp.example 

% 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:
Zone fully signed:
Algorithm: ECDSAP256SHA256: KSKs: 1 active, 0 stand-by, 0 revoked
                            ZSKs: 0 active, 0 stand-by, 0 revoked
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.

% awk '/PrivateKey:/ { print $2 }' Kexample.com.+013+37354.private | base64 -d | ./hexlify

% ./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.

dnssec and hsm :: 02 Oct 2022 :: e-mail