Crates.io | nethsm-cli |
lib.rs | nethsm-cli |
version | |
source | src |
created_at | 2024-07-13 08:15:27.890482 |
updated_at | 2024-07-13 08:15:27.890482 |
description | A CLI for the Nitrokey NetHSM |
homepage | |
repository | https://gitlab.archlinux.org/archlinux/signstar |
max_upload_size | |
id | 1302142 |
size | 0 |
A command line interface (CLI) for the Nitrokey NetHSM based on the nethsm crate.
This crate can be installed using cargo
:
cargo install nethsm-cli
Afterwards the nethsm
executable is available.
It is recommended to refer to the extensive --help
output of the executable and its subcommands.
The following assumes a recent version of openssl
and podman
.
podman run --rm -ti --network=pasta:-t,auto,-u,auto,-T,auto,-U,auto docker.io/nitrokey/nethsm:testing
The configuration file uses the TOML format.
By default an Operating System specific, well-defined configuration file location is chosen.
Using -c
/ --config
/ the NETHSM_CONFIG
environment variable it is possible to provide a custom configuration file location.
# use a custom, temporary directory for all generated files
nethsm_tmpdir="$(mktemp --directory --suffix '.nethsm-test')"
# set a custom, temporary configuration file location
export NETHSM_CONFIG="$(mktemp --tmpdir="$nethsm_tmpdir" --suffix '-nethsm.toml' --dry-run)"
To be able to interact with a NetHSM (or the testing container), each device must be added to the configuration file.
# add the container using unsafe TLS connection handling for testing
nethsm env add device --label test https://localhost:8443/api/v1 Unsafe
If only one device environment is configured, it is used by default when issuing nethsm
commands.
If more than one environment is configured, the target device must be selected using the global -l
/ --label
option.
The handling of credentials is flexible: Credentials can be stored in the configuration file with or without passphrases or not at all. If credentials are not configured, they are prompted for interactively.
# prepare a temporary passphrase file for the initial admin user passphrase
nethsm_admin_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.admin-passphrase.txt')"
export NETHSM_PASSPHRASE_FILE="$nethsm_admin_passphrase_file"
printf 'my-very-unsafe-admin-passphrase' > "$NETHSM_PASSPHRASE_FILE"
# add the default admin user credentials
nethsm env add credentials admin Administrator
Before using a device for the first time, it must be provisioned. This includes setting the passphrase for the initial "admin" user, the unlock passphrase and the system time of the device.
# prepare a temporary passphrase file for the initial unlock passphrase
export NETHSM_UNLOCK_PASSPHRASE_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.unlock-passphrase.txt')"
printf 'my-very-unsafe-unlock-passphrase' > "$NETHSM_UNLOCK_PASSPHRASE_FILE"
# reuse the initial admin passphrase
export NETHSM_ADMIN_PASSPHRASE_FILE="$nethsm_admin_passphrase_file"
nethsm provision
Each user may be in exactly one role ("Administrator", "Operator", "Metrics" or "Backup").
nethsm_admin1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.admin1-passphrase.txt')"
printf 'my-very-unsafe-admin1-passphrase' > "$nethsm_admin1_passphrase_file"
nethsm_operator1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.operator1-passphrase.txt')"
printf 'my-very-unsafe-operator1-passphrase' > "$nethsm_operator1_passphrase_file"
nethsm_backup1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.backup1-passphrase.txt')"
printf 'my-very-unsafe-backup1-passphrase' > "$nethsm_backup1_passphrase_file"
nethsm_metrics1_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.metrics1-passphrase.txt')"
printf 'my-very-unsafe-metrics1-passphrase' > "$nethsm_metrics1_passphrase_file"
# we create a user in each role and add the credentials including passphrases to the configuration
# NOTE: this is for testing purposes! passphrases stored in configuration files can easily be retrieved!
export NETHSM_PASSPHRASE_FILE="$nethsm_admin1_passphrase_file"
nethsm user add "Some Admin1" Administrator admin1
nethsm env add credentials admin1 Administrator
export NETHSM_PASSPHRASE_FILE="$nethsm_operator1_passphrase_file"
nethsm user add "Some Operator1" Operator operator1
nethsm env add credentials operator1 Operator
export NETHSM_PASSPHRASE_FILE="$nethsm_backup1_passphrase_file"
nethsm user add "Some Backup1" Backup backup1
nethsm env add credentials backup1 Backup
export NETHSM_PASSPHRASE_FILE="$nethsm_metrics1_passphrase_file"
nethsm user add "Some Metrics1" Metrics metrics1
nethsm env add credentials metrics1 Metrics
The user names and accompanying information can be queried:
for user in $(nethsm user list); do
nethsm user get "$user"
done
Tags for users can only be created once keys with those tags exists.
Keys on the device are managed using a user in the "Administrator" role. Depending on restrictions (tags), the keys may then be used by users in the "Operator" role.
Below, we are generating keys of all available types (Curve25519, EcP224, EcP256, EcP384, EcP521, Generic and Rsa). When generating a key, the unique ID for it may be set manually (else it is auto-generated). Tags, which later on allow users access to the keys may also be set during key generation.
Note that some keys require to set the key bit length (i.e. Generic and Rsa).
nethsm key generate --key-id signing1 --tags signing1 Curve25519 EdDsaSignature
nethsm key generate --key-id signing2 --tags signing2 EcP224 EcdsaSignature
nethsm key generate --key-id signing3 --tags signing2 EcP256 EcdsaSignature
nethsm key generate --key-id signing4 --tags signing2 EcP384 EcdsaSignature
nethsm key generate --key-id signing5 --tags signing2 EcP521 EcdsaSignature
nethsm key generate --key-id encdec1 --tags decryption1 --length 128 Generic AesDecryptionCbc AesEncryptionCbc
nethsm key generate --key-id dec1 --tags decryption2 --length 2048 Rsa RsaDecryptionPkcs1
nethsm key generate --key-id signing6 --tags signing3 --length 2048 Rsa RsaSignaturePssSha512
All key IDs on the device and info about them can be listed:
for key in $(nethsm key list); do
nethsm key get "$key"
done
Keys can also be imported:
ed25519_cert_pem="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.ed25519_cert.pem')"
ed25519_cert_der="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.ed25519_cert.pkcs8.der')"
openssl genpkey -algorithm ed25519 -out "$ed25519_cert_pem"
openssl pkcs8 -topk8 -inform pem -in "$ed25519_cert_pem" -outform der -nocrypt -out "$ed25519_cert_der"
# import requires a PKCS#8 private key in ASN.1 DER-encoded format
nethsm key import Curve25519 "$ed25519_cert_der" EdDsaSignature --key-id signing7
# forgot to set a tag!
nethsm key tag signing7 signing1
nethsm key get signing7
To provide access to keys for users, the users have to be tagged with the same tags as the keys.
nethsm user tag operator1 signing1
export NETHSM_KEY_SIGNATURE_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.readme.signature.sig')"
export NETHSM_KEY_PUBKEY_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.pubkey.pem')"
message_digest="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.message.dgst')"
# if we add the signing2 tag for operator1 it is able to use signing{2-5}
nethsm user tag operator1 signing2
# if we add the signing3 tag for operator1 it is able to use signing6
nethsm user tag operator1 signing3
# create a signature with each key type
nethsm key sign --force signing1 EdDsa README.md
nethsm key public-key --force signing1
openssl pkeyutl -verify -in README.md -rawin -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm key sign --force signing2 EcdsaP224 README.md
nethsm key public-key --force signing2
openssl dgst -sha224 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm key sign --force signing3 EcdsaP256 README.md
nethsm key public-key --force signing3
openssl dgst -sha256 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm key sign --force signing4 EcdsaP384 README.md
nethsm key public-key --force signing4
openssl dgst -sha384 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm key sign --force signing5 EcdsaP521 README.md
nethsm key public-key --force signing5
openssl dgst -sha512 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
nethsm key sign --force signing6 PssSha512 README.md
nethsm key public-key --force signing6
openssl dgst -sha512 -binary README.md > "$message_digest"
openssl pkeyutl -verify -in "$message_digest" -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin -pkeyopt rsa_padding_mode:pss -pkeyopt digest:sha512 -pkeyopt rsa_pss_saltlen:-1
nethsm key sign --force signing7 EdDsa README.md
nethsm key public-key --force signing7
openssl pkeyutl -verify -in README.md -rawin -sigfile "$NETHSM_KEY_SIGNATURE_OUTPUT_FILE" -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin
# if we remove the signing3 tag for operator1 it is no longer able to use signing6
nethsm user untag operator1 signing3
Messages can be encrypted using keys that offer the key mechanisms for this operation.
message="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.message.txt')"
printf "Hello World! This is a message!!" > "$message"
export NETHSM_KEY_ENCRYPT_IV="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.iv.txt')"
printf "This is unsafe!!" > "$NETHSM_KEY_ENCRYPT_IV"
export NETHSM_KEY_ENCRYPT_OUTPUT="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.symmetric-encrypted-message.txt.enc')"
# we need to provide access to the key by tagging the user
nethsm user tag operator1 decryption1
# let's use our symmetric encryption key to encrypt a message
nethsm key encrypt encdec1 "$message"
# the initialization vector must be the same
export NETHSM_KEY_DECRYPT_IV="$NETHSM_KEY_ENCRYPT_IV"
export NETHSM_KEY_DECRYPT_OUTPUT="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.decrypted-message.txt')"
# now let's decrypt the encrypted message again
nethsm key decrypt encdec1 "$NETHSM_KEY_ENCRYPT_OUTPUT" AesCbc
cat "$NETHSM_KEY_DECRYPT_OUTPUT"
[[ "$(b2sum "$NETHSM_KEY_DECRYPT_OUTPUT" | cut -d ' ' -f1)" == "$(b2sum "$message" | cut -d ' ' -f1)" ]]
The same works for asymmetric keys as well:
# we need to provide access to the key by tagging the user
nethsm user tag operator1 decryption2
# unset the initialization vectors as we do not need them for this
unset NETHSM_KEY_DECRYPT_IV NETHSM_KEY_ENCRYPT_IV
# retrieve the public key of the key to use (and overwrite any previously existing)
nethsm key public-key --force dec1
asymmetric_enc_message="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.asymmetric-encrypted-message.txt.enc')"
# encrypt the previous message
openssl pkeyutl -encrypt -inkey "$NETHSM_KEY_PUBKEY_OUTPUT_FILE" -pubin -in "$message" -out "$asymmetric_enc_message"
# decrypt the asymmetrically encrypted message and replace any existing output
nethsm key decrypt dec1 "$asymmetric_enc_message" Pkcs1 --force
cat "$NETHSM_KEY_DECRYPT_OUTPUT"
[[ "$(b2sum "$NETHSM_KEY_DECRYPT_OUTPUT" | cut -d ' ' -f1)" == "$(b2sum "$message" | cut -d ' ' -f1)" ]]
Administrators and operators can retrieve the public key of any key:
# keys of type "Generic" don't have a public key, so we do not request them
for key in signing{1..7} dec1; do
nethsm key public-key "$key" --force
done
Certificate Signing Requests for a particular target can be issued using the keys.
# get a CSR for example.com
nethsm key csr signing7 example.com
The device can generate an arbitrary number of random bytes on demand.
export NETHSM_RANDOM_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.random.txt')"
nethsm random 200
[[ -f "$NETHSM_RANDOM_OUTPUT_FILE" ]]
The metrics for a device can only be retrieved by a user in the "Metrics" role.
nethsm metrics
Several aspects of the device configuration can be retrieved and modified.
The boot mode defines whether the system starts into "Locked" or "Operational" state (the former requiring to supply the unlock passphrase to get to "Operational" state).
nethsm config get boot-mode
# let's set it to unattended
nethsm config set boot-mode Unattended
nethsm config get boot-mode
Each device may send syslog to a remote host.
nethsm config get logging
The devices have a unique and static network configuration.
nethsm config get network
The device's system time can be queried and set.
nethsm config get time
nethsm config set time
nethsm config get time
We can get and set the TLS certificate used for the device.
nethsm config get tls-certificate
# this generates a new RSA 4096bit certificate on the device
nethsm config set tls-generate Rsa 4096
nethsm config get tls-certificate
We can also receive only the public key for the TLS certificate:
nethsm config get tls-public-key
Or generate a Certificate Signing Request for the TLS certificate:
nethsm config get tls-csr example.com
The backup passphrase is used to decrypt a backup created for the device, when importing. By default it is the empty string (""
).
nethsm_backup_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.backup-passphrase.txt')"
printf 'my-very-unsafe-backup-passphrase' > "$nethsm_backup_passphrase_file"
export NETHSM_NEW_PASSPHRASE_FILE="$nethsm_backup_passphrase_file"
nethsm_initial_backup_passphrase_file="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.initial-backup-passphrase.txt')"
touch "$nethsm_initial_backup_passphrase_file"
export NETHSM_OLD_PASSPHRASE_FILE="$nethsm_initial_backup_passphrase_file"
nethsm config set backup-passphrase
The unlock passphrase is set during initial provisioning and is used to unlock the device when it is locked.
export NETHSM_OLD_PASSPHRASE_FILE="$NETHSM_UNLOCK_PASSPHRASE_FILE"
export NETHSM_NEW_PASSPHRASE_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.unlock-passphrase.txt')"
printf 'my-new-unsafe-unlock-passphrase' > "$NETHSM_NEW_PASSPHRASE_FILE"
nethsm config set unlock-passphrase
The device can be locked and unlocked, which puts it into state "Locked"
and "Operational"
, respectively.
nethsm lock
nethsm health state
nethsm health alive
# as we have changed the unlock passphrase, we need to provide the new one
export NETHSM_UNLOCK_PASSPHRASE_FILE="$NETHSM_NEW_PASSPHRASE_FILE"
nethsm unlock
nethsm health state
nethsm health ready
The devices offer various system level actions, e.g.:
# reset device to factory settings
nethsm system factory-reset
# reboot device
nethsm system reboot
# shut down device
nethsm system shutdown
# get system info about the device
nethsm system info
The device offers backing up of keys and user data.
export NETHSM_BACKUP_OUTPUT_FILE="$(mktemp --tmpdir="$nethsm_tmpdir" --dry-run --suffix '-nethsm.backup-file.bkp')"
nethsm system backup
A backup can later on be used to restore a device:
export NETHSM_BACKUP_PASSPHRASE_FILE="$nethsm_backup_passphrase_file"
nethsm system restore "$NETHSM_BACKUP_OUTPUT_FILE"
Updates for the operating system/ firmware of the device are uploaded to the device and then applied or aborted.
nethsm system upload-update my-update-file.bin
# apply the update
nethsm system commit-update
# abort the update
nethsm system cancel-update
rm -r -- "$nethsm_tmpdir"
This project may be used under the terms of the Apache-2.0 or MIT license.
Changes to this project - unless stated otherwise - automatically fall under the terms of both of the aforementioned licenses.