Importing Android/Java signing keys into NitroKey HSM2

I’m working on a procedure to allow migrating Android signing keys to a NitroKey HSM. Android APK signing keys are the same thing as Java JAR signing keys, and APK v1 Signatures are literally just JAR signatures. Android signing keys are for the lifetime of the app, so this means the keys will need to be imported into the NitroKey for any existing app.

I’m using a NitroKey HSM that was updated from firmware v2.3 to v2.5.

I have tried using the Smart Card Shell, but that fails:

I would really rather avoid using mystery binary tools like Smart Card Shell anyway, so I also found the S/MIME procedure, which in theory should work: https://www.nitrokey.com/documentation/smime-email-encryption

But that is failing for me:

$ nitrotool format --so-pin 3537363231383830
Using slot 0 with a present token (0x0)
Using reader with a card: Nitrokey Nitrokey HSM (010000000000000000000000) 00 00
Smartcard successfully formatted. New SO-PIN: 3537363231383830 and PIN: 648219
$ nitrotool init --verbose 
Using reader with a card: Nitrokey Nitrokey HSM (010000000000000000000000) 00 00
Version              : 2.5
Config options       :
  User PIN reset with SO-PIN enabled
SO-PIN tries left    : 15
User PIN tries left  : 3

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Default SO-PIN: 3537363231383830    Default PIN: 648219
Error: Cannot initialize NitroKey -- already initialized.
$ pkcs15-init --delete-objects privkey,pubkey --id 3 --store-private-key keystore.p12 --format pkcs12 --auth-id 3 --verify-pin --so-pin 3537363231383830
Using reader with a card: Nitrokey Nitrokey HSM (010000000000000000000000) 00 00
Unable to find PIN code: Requested object not found
Failed to verify User PIN : Requested object not found
$ pkcs15-init --delete-objects privkey,pubkey --id 3 --store-private-key keystore.p12 --format pkcs12 --auth-id 3 --verify-pin --pin 648219 
Using reader with a card: Nitrokey Nitrokey HSM (010000000000000000000000) 00 00
Unable to find PIN code: Requested object not found
Failed to verify User PIN : Requested object not found

I tried this S/MIME method: https://www.nitrokey.com/documentation/smime-email-encryption, I could not get it to work. I suspect because the NitroKey HSM only allows importing via the CardContact DKEK method. Another thread mentioned that pkcs15-init does not work with NitroKey HSM. I always got these errors:

$ pkcs15-init --delete-objects privkey,pubkey --id 3 --store-private-key keystore.p12 --format pkcs12 --auth-id 3 --verify-pin
Using reader with a card: Nitrokey Nitrokey HSM (DENK01012340000         ) 00 00
Unable to find PIN code: Requested object not found
Failed to verify User PIN : Requested object not found

The SmartCard-HSM does not support key import in clear, which is what pkcs15-init is trying to do. You need to use a DKEK instead, which is the method supported in the Smart Card Shell.

Importing keys in clear is a major security issue, as the key material traverses a lot of unprotected interfaces (PC/SC API, CCID API, USB-HW, Reader, CL or CC interface).

We of course understand that quickly importing a key in clear would be handy, but for professional key management that is not acceptable. Advanced key management, in particular using key domains with DKEK or XKEK, is what make the SmartCard-HSM different from other PKI cards.

I appreciate that there is the DKEK mechanism, and people should be
encouraged to use it as much as possible. It is also difficult to use,
and that alone will prevent people from upgrading from keys on disk.
Then this strict policy actually harms their security. The human
component cannot be ignored. For example, Debian Developers need PGP
keys to uploading. The vast majority just store those keys on disk
because using HSMs is so much extra work. NitroKey and Yubikey
understand this better than most smartcard companies, and have improved
the situation quite a bit in the past years. There is still much work
to be done.

Providing the DKEK mechanism does not mean direct key import cannot
still be an option. In most cases, if you do not trust the software
that interfaces with the HSM, you have already lost. The HSM does not
exist in a bubble, there are plenty of ways to exploit it even if the
DKEK method is used. For example, if the encrypted keystore to import
is stored on an machine that also has the password, malicious PC/SC
could just read that info directly without getting involved in the HSM.
Malicious software can also just intercept and modify the things being
sent to the HSM to be signed.

Then this strict policy actually harms their security.

No, it harms their security to generate keys outside a secure enclosure first hand.

The purpose of using a HSM is that you know if copies of your key exist. For a file based key you never know that.

The right way is to create the organizational overhead to manage DKEK shares, then generate keys inside the device and then export and backup key material.

I understand that this is a little more inconvenient, but it is the commonly used approach in professional applications.

I agree it is better to generate using secure methods. If your keys
are already generated and sitting on a disk, that is reality and cannot
be changed. Basically zero Android devs created their app signing keys
in an HSM. The Android model means an app must use the same signing key
forever. Google only recently added the possibility of migrating an
app’s signing key, and from what I’ve seen, its barely used. So making
it easy to import Android signing keys into an HSM is only a win, based
on the reality that exists today. This is a professional application of
HSMs that has different issues than running a CA, HTTPS server or other
classic uses of HSMs. Arbitrary restrictions like this one are the key
reason that has prevented HSMs becoming standard practice in the Android
world.

Also, HSMs are not the only way to provide a secure enclosure. A
properly setup, dedicated, offline laptop is not as good as an HSM, but
it certainly is a good way to protect private keys. And for Android
signing keys, this approach is still vastly easier than using an HSM.