Import AES 128 key - Smart Card Shell - How to get dkekshare

Hello,

I know about the examples of Smart Card Shell to see how to import AES keys.

But these examples require a “dkekshare”, we would like to know how it’s possible to get the value when the key is initialised externally (with sc hsm tools).

When we create a DKEK Share using sc hsm tools we get a .pbe file with the double of required size. I think we can’t use the content of this file AS-IS as it is also supposed to be secured.

Also is it possible to import an AES key in a Nitrokey HSM 2 secured with DKEK Share n-of-m ?

If the only choice for us is to import key with Smart Card Shell AND create the DKEK share in Smart Card Shell in order to init the HSM and import key, then how do we create a backup of the HSM to another one ?

Here is how we can import AES and private keys (P12 files)

/**
 *  ---------
 * |.##> <##.|  SmartCard-HSM Support Scripts
 * |#       #|
 * |#       #|  Copyright (c) 2011-2012 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 * Consult your license package for usage terms and conditions.
 *
 * @fileoverview Import RSA Key and certificate from PKCS#12 file into SmartCard-HSM
 */

SmartCardHSM = require("scsh/sc-hsm/SmartCardHSM").SmartCardHSM;
HSMKeyStore = require("scsh/sc-hsm/HSMKeyStore").HSMKeyStore;
DKEK = require("scsh/sc-hsm/DKEK").DKEK;

function readfile(filename)
{
    var FileReader = java.io.FileReader;
    var BufferedReader = java.io.BufferedReader;
    var content = "";

    var reader = new BufferedReader(new FileReader("/tmp/cmn_key_idx0.txt"));
    var line;

    while ((line = reader.readLine()) != null) {
        content += line;
    }
    reader.close();
    return content;
}


var crypto = new Crypto();
var dkekshare = crypto.generateRandom(32);

var doinit = true;

var page = 	"<html>" +
		"<p>The DKEK is used to encrypt the private key for import into the SmartCard-HSM.</p>" +
		"<p>If you already have initialized your SmartCard-HSM and know the DKEK from a previous key import,<br>" +
		"then you can replace the random DKEK below with the known DKEK from the previous import</p>" +
		"</html>";

var str = Dialog.prompt(page, dkekshare.toString(HEX));

if ((str != null) && (str.length > 0)) {
	newdkekshare = new ByteString(str, HEX);
	assert(newdkekshare.length == 32, "DKEK share must be 32 bytes long");
	if (!newdkekshare.equals(dkekshare)) {
		doinit = false;
		dkekshare = newdkekshare;
	}
}

var card = new Card(_scsh3.reader);
card.reset(Card.RESET_COLD);
var sc = new SmartCardHSM(card);

if (doinit) {
	if (sc.queryUserPINStatus() == 0x6984) {
		assert(Dialog.prompt("WARNING: If you continue, then your SmartCard-HSM will be initialized to the initialization code selected in the next step"));
	} else {
		assert(Dialog.prompt("WARNING: If you continue, then your SmartCard-HSM will be initialized and all key contained will be lost"));
	}

	var initializationCode = Dialog.prompt("Enter initialization code for SmartCard-HSM", "0000000000000000");
	assert(initializationCode != null);
	initializationCode = new ByteString(initializationCode, HEX);

	var pin = Dialog.prompt("Enter PIN for newly initialized SmartCard-HSM", "000000");
	assert(pin != null);
	pin = new ByteString(pin, ASCII);

	sc.initDevice(new ByteString("0001", HEX), pin, initializationCode, 3, 1);

	var status = sc.importKeyShare(dkekshare);

	print("Device initialized:");
	print("-------------------");
	print("SW          : " + status.sw.toString(HEX));
	print("Shares      : " + status.shares);
	print("Outstanding : " + status.outstanding);
	print("KVC         : " + status.kcv.toString(HEX));
	print("");
} else {
	assert(sc.queryUserPINStatus() != 0x6984, "SmartCard-HSM is not initialized");

	var pin = Dialog.prompt("Enter PIN for SmartCard-HSM", "648219");
	assert(pin != null);
	pin = new ByteString(pin, ASCII);
}

sc.verifyUserPIN(pin);

var file = "";
var pwd = "";

do	{
	// Prompt for PKCS#12 container and password, then select key to import
	var file = Dialog.prompt("Select PKCS#12 container", file, null, "*.p12");
	assert(file != null);

	var pwd = Dialog.prompt("Enter PKCS#12 password", pwd);
	assert(pwd != null);

	var ks = new KeyStore("BC", "PKCS12", file, pwd);

	var aliases = ks.getAliases();

	var alias = Dialog.prompt("Select key", "", aliases);
	assert(alias != null);

	var key = new Key();
	key.setType(Key.PRIVATE);
	key.setID(alias);
	ks.getKey(key);
	var cert = ks.getCertificate(alias);

	print(cert);

	var alias = Dialog.prompt("Enter key name for import", alias);
	assert(alias != null);

	var ks = new HSMKeyStore(sc);

	var dkek = new DKEK(crypto);
	dkek.importDKEKShare(dkekshare);

	var pubkey = cert.getPublicKey();
	var blob = dkek.encodeKey(key, pubkey);

	dkek.dumpKeyBLOB(blob);

	if (pubkey.getComponent(Key.MODULUS)) {
		hkey = ks.importRSAKey(alias, blob, cert.getPublicKey().getSize());
		var signalgo = Crypto.RSA_PSS_SHA256;
	} else {
		hkey = ks.importECCKey(alias, blob, cert.getPublicKey().getSize());
		var signalgo = Crypto.ECDSA_SHA256;
	}
	ks.storeEndEntityCertificate(alias, cert);

	// Test import
	var msg = new ByteString("Hello World", ASCII);

	var signature = hkey.sign(signalgo, msg);

	assert(crypto.verify(cert.getPublicKey(), signalgo, msg, signature));
} while (Dialog.prompt("Import more keys ?"));

while(Dialog.prompt("Any AES key to add ?"))
{
	var file = Dialog.prompt("Select AES key file", file, null, "*.txt");
	assert(file != null);

	var aesdata = readfile(file);
	print("AES DATA : " + aesdata);

	var aes = new Key();
        aes.setComponent(Key.AES, new ByteString(aesdata, HEX));

	const keyname = file.split("/").pop();     // "cmn_key_idx0.txt"
	const keyname2 = keyname.split(".").shift(); // "cmn_key_idx0"

	var alias = Dialog.prompt("Enter key name for import", keyname2);
	assert(alias != null);

	var ks = new HSMKeyStore(sc);

	var dkek = new DKEK(crypto);
	dkek.importDKEKShare(dkekshare);

	var blob = dkek.encodeAESKey(aes);
	dkek.dumpKeyBLOB(blob);

	ks.importAESKey(alias, blob, 128);	
}

pbe stands for password based encryption. When you create a DKEK share with sc-hsm-tool or the Smart Card Shell, then the DKEK share value is encrypted using the provided password. If you use n-of-m, then the password is split using Shamir Shared Secret.

DKEK.decryptKeyShare() decrypts the password protected share. Have a look at keymanager.js how it is used.

1 Like

Thanks,

For people looking for the solution, here is an example:

SmartCardHSM = require("scsh/sc-hsm/SmartCardHSM").SmartCardHSM;
HSMKeyStore = require("scsh/sc-hsm/HSMKeyStore").HSMKeyStore;
DKEK = require("scsh/sc-hsm/DKEK").DKEK;
load("keymanager/keymanager.js");

function read_hex_file(filename)
{
    var FileReader = java.io.FileReader;
    var BufferedReader = java.io.BufferedReader;
    var content = "";

    var reader = new BufferedReader(new FileReader(filename));
    var line;

    while ((line = reader.readLine()) != null) {
        content += line;
    }
    reader.close();
    return content;
}

function read_bin_file(filename) {
    var FileInputStream = java.io.FileInputStream;

    var f = new java.io.File(filename);
    if (!f.exists()) throw "File not found: " + filename;

    var fis = new FileInputStream(f);
    var hexString = "";
    var b;

    while ((b = fis.read()) != -1) {
        var h = (b & 0xFF).toString(16).toUpperCase();
        if (h.length === 1) h = "0" + h;
        hexString += h;
    }

    fis.close();
    return hexString;
}

function import_aes_key(ks, dkek, aesdata, label, keysize, keyid) {
    // aesdata: This variable is a String that contains the AES key to import in HEX form

    var aes = new Key();
    aes.setComponent(Key.AES, new ByteString(aesdata, HEX));
    aes.setType(Key.SECRET);

    var blob = dkek.encodeAESKey(aes);
    dkek.dumpKeyBLOB(blob);
    
    const keyidbs = new ByteString(keyid, HEX);

    ks.importAESKey(label, blob, keysize, keyidbs);	
}

function main() {
    card.reset(Card.RESET_COLD);
    var sc = new SmartCardHSM(card);
    assert(sc.queryUserPINStatus() != 0x6984, "SmartCard-HSM is not initialized");

    var pin = Dialog.prompt("Enter PIN code", "000000");
    sc.verifyUserPIN(new ByteString(pin, ASCII));

    var ks = new HSMKeyStore(sc);
    var keys = ks.enumerateKeys();
    if (keys.length != 0) {
        var erasekeys = Dialog.prompt("<html><p>The HSM is already initialised with somes keys:</p><p>- " + keys + ".</p><p>We need to erase all keys to continue.</p><p>Do you want to continue ?</p></html>");
        assert(erasekeys == "OK", "Impossible to continue, exit!");
        for (var i = 0; i < keys.length; i++) {
            print("Erasing '" + keys[i] + "'...");
            ks.deleteKey(keys[i]);
        }
    }
    


    var crypto = new Crypto();
    var dkek = new DKEK(crypto);
    dkek.importDKEKShare(km.inputDKEKShare());

    const prime256v1 = "1.2.840.10045.3.1.7";
    
    print("Creating 'bl2' EC Key Pair...");
    ks.generateECCKeyPair("bl2", prime256v1);
    print("Creating 'bl31' EC Key Pair...");
    ks.generateECCKeyPair("bl31", prime256v1);
    print("Creating 'bl32' EC Key Pair...");
    ks.generateECCKeyPair("bl32", prime256v1);
    print("Creating 'bl33' EC Key Pair...");
    ks.generateECCKeyPair("bl33", prime256v1);
    print("Creating 'root_of_trust_key' EC Key Pair...");
    ks.generateECCKeyPair("root_of_trust_key", prime256v1);
    
    print("Creating 'cmn_key_idx0' AES Key...");
    var aesfile = Dialog.prompt("Select the 'cmn_key_idx0.bin' file", GPSystem.mapFilename("", GPSystem.USR), null, "*.bin");
    assert(aesfile != "", "No selected file");
    var aesdata = read_bin_file(aesfile);
    import_aes_key(ks, dkek, aesdata, "cmn_key_idx0", 128, "06");

    print("Creating 'fitImage' RSA Key Pair...");
    ks.generateRSAKeyPair("fitImage", 2048);
    print("Creating 'OTA' RSA Key Pair...");
    ks.generateRSAKeyPair("OTA", 2048);
    
    print("Finish!");
}

main();

I use inputDKEKShare() from the file mentioned by sc-hsm.

This function open dialogs and permit to choose any type of DKEK Share (I only try password protected, but should work with n-of-m).

1 Like

@sc-hsm : Sorry to re-open this…

I just found that If I want to “export” the keys it works for all the keys except for the AES imported key.

I tried with sc-hsm-tool:

$ sc-hsm-tool --wrap-key /tmp/cmn_key_idx0-wrap.bin --key-reference 6 --pin 000000
Using reader with a card: Nitrokey Nitrokey HSM (DENK04021460000         ) 00 00
Connecting to card in reader Nitrokey Nitrokey HSM (DENK04021460000         ) 00 00...
Using card driver SmartCard-HSM.
sc_card_ctl(*, SC_CARDCTL_SC_HSM_WRAP_KEY, *) failed with Card does not support the requested operation

And also from Smart Card Shell:

>GPError: Card (CARD_INVALID_SW/27265) - "Unexpected SW1/SW2=6A81 (Checking error: Function not supported) received" in /pathto/scsh-3.18.59/scsh/sc-hsm/SmartCardHSM.js#1566
    at /pathto/scsh-3.18.59/scsh/sc-hsm/SmartCardHSM.js#1566
    at /pathto/scsh-3.18.59/scsh/sc-hsm/HSMKeyStore.js#287
    at /pathto/scsh-3.18.59/keymanager/keymanager.js#2882
    at /pathto/scsh-3.18.59/keymanager/keymanager.js#3229

But it works if the key is generated from Smart Card Shell.

$ sc-hsm-tool --wrap-key /tmp/cmn_key_idx0-wrap.bin --key-reference 9 --pin 000000
Using reader with a card: Nitrokey Nitrokey HSM (DENK04021460000         ) 00 00
Connecting to card in reader Nitrokey Nitrokey HSM (DENK04021460000         ) 00 00...
Using card driver SmartCard-HSM.

If I check the attributes of the “secrkey” (the one imported by SCS and the one generated by SCS), it seems to be the same attributes:

$ pkcs11-tool --module /usr/local/lib/libsc-hsm-pkcs11.so -l --pin 000000 --list-objects --type secrkey
Using slot 0 with a present token (0x1)
Secret Key Object; AES length 16
  label:      cmn_key_idx0
  ID:         06
warning: PKCS11 function C_GetAttributeValue(VERIFY_RECOVER) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)

  Usage:      encrypt, decrypt
  Access:     sensitive, always sensitive, never extractable, local
Secret Key Object; AES length 16
  label:      test
  ID:         2c9f2450033933b7
warning: PKCS11 function C_GetAttributeValue(VERIFY_RECOVER) failed: rv = CKR_ATTRIBUTE_TYPE_INVALID (0x12)

  Usage:      encrypt, decrypt
  Access:     sensitive, always sensitive, never extractable, local

Any ideas ?

Also I don’t see how to change key attributes, with ks.importAESKey() There is no parameters to SmartCardHSMKeySpecGenerator.

I found the reason about the last question:

The conclusion is that:

  • SmartCard–HSM tends to lock everything that seems to be “bad practice”.

But as said on another topic, there are situations where importing keys is legit:

  • We are interfacing an HSM in the signature mechanism of a manufacturer (for the secure boot of our boards)
  • The signature mechanism is made to work with key files by design, we did some modifications to make it working with HSM, but we can’t change the signature algorithms.
  • And this is not going to change for this series of chip

With their algorithm, we need to “encrypt and then sign” the AES Key, with another key:

  • If the AES Key (to encrypt) is in the HSM then it’s impossible.
    • We cant use an internal key as plain text input of AES-CBC encryption, as seen here.
  • PKCS11 WRAP algorithm exists, but this is not the same algorithm as what the manufacturer uses.
  • So we need to do “these operations outside the HSM and then import the keys”.
    • Of course, using a specific procedure with N people, watching what 1 person is doing

For the backup, we are simply going to “import the key” in the backup Nitrokey using Smart Card HSM, like the first import.

Topic closed.

The error code SW1/SW2=6A81 (Checking error: Function not supported) can have a number of reasons:

  1. The WRAP attribute is not set for the key in the algorithm list (not to be confused with the PKCS#15 attributes that --list-object shows. There is no mapping between both).
  2. The KEK was cleared.
  3. The key has a key use counter (Exporting and import a key with a use counter defeats the purpose of having a single instance that counts key uses).

I guess in your case, the AES key imported with ks.importAESKey() does not have the WRAP (‘92’) attribute in the algorithm list, which is not set in DKEK.encodeAESKey().

Line 265 in DKEK.js says:

var aaid = new ByteString("10 11 18 99", HEX);

meaning that only ENC, DEC, CMAC and DERIVE are asserted. You could add ‘92’ to the list to see the effect.

1 Like

:tada: Congratulations :tada:

:heart_eyes: This is perfectly working :heart_eyes:

I transfered the function in my code, to change it without modifying Smart Card Shell sources.

SmartCardHSM = require("scsh/sc-hsm/SmartCardHSM").SmartCardHSM;
HSMKeyStore = require("scsh/sc-hsm/HSMKeyStore").HSMKeyStore;
DKEK = require("scsh/sc-hsm/DKEK").DKEK;
load("keymanager/keymanager.js");

function read_hex_file(filename)
{
    var FileReader = java.io.FileReader;
    var BufferedReader = java.io.BufferedReader;
    var content = "";

    var reader = new BufferedReader(new FileReader(filename));
    var line;

    while ((line = reader.readLine()) != null) {
        content += line;
    }
    reader.close();
    return content;
}

function read_bin_file(filename) {
    var FileInputStream = java.io.FileInputStream;

    var f = new java.io.File(filename);
    if (!f.exists()) throw "File not found: " + filename;

    var fis = new FileInputStream(f);
    var hexString = "";
    var b;

    while ((b = fis.read()) != -1) {
        var h = (b & 0xFF).toString(16).toUpperCase();
        if (h.length === 1) h = "0" + h;
        hexString += h;
    }

    fis.close();
    return hexString;
}

function encodeAESKey(dkek, key) {
	var bb = new ByteBuffer();
	bb.append(dkek.getKCV());

	assert(key.getType() == Key.SECRET)

	bb.append(15);

	var daid = new ByteString("2.16.840.1.101.3.4.1", OID);
	bb.append(ByteString.valueOf(daid.length, 2));
	bb.append(daid);

	var aaid = new ByteString("10 11 18 99 92", HEX);
	bb.append(ByteString.valueOf(aaid.length, 2));
	bb.append(aaid);

	bb.append(ByteString.valueOf(0, 2));
	bb.append(ByteString.valueOf(0, 2));

	var kb = new ByteBuffer(256);
	kb.append(dkek.crypto.generateRandom(8));

	kb.append(ByteString.valueOf(key.getSize() / 8, 2));
	kb.append(key.getComponent(Key.AES));

	var unpadded = kb.toByteString();
	var plain = unpadded.pad(Crypto.ISO9797_METHOD_2);
	unpadded.clear();

	if (plain.length & 0xF) {		// pad() padds to 8 byte blocks, but we 16 byte blocks
		var nplain = plain.concat(new ByteString("0000000000000000", HEX));
		plain.clear();
		plain = nplain;
	}

	var iv = new ByteString("00000000000000000000000000000000", HEX);
	var kenc = dkek.getKENC();
	var cipher = dkek.crypto.encrypt(kenc, Crypto.AES_CBC, plain, iv);
	kenc.getComponent(Key.AES).clear();
	plain.clear();

	bb.append(cipher);

	var kmac = dkek.getKMAC();
	bb.append(dkek.crypto.sign(kmac, Crypto.AES_CMAC, bb.toByteString()));
	kmac.getComponent(Key.AES).clear();

	return bb.toByteString();
}

function import_aes_key(ks, dkek, aesdata, label, keysize, keyid) {
    // aesdata: This variable is a String that contains the AES key to import in HEX form

    var aes = new Key();
    aes.setComponent(Key.AES, new ByteString(aesdata, HEX));
    aes.setType(Key.SECRET);

    //var blob = dkek.encodeAESKey(aes);
    var blob = encodeAESKey(dkek, aes);
    dkek.dumpKeyBLOB(blob);
    
    const keyidbs = new ByteString(keyid, HEX);

    ks.importAESKey(label, blob, keysize, keyidbs);	
}

function main() {
    card.reset(Card.RESET_COLD);
    var sc = new SmartCardHSM(card);
    assert(sc.queryUserPINStatus() != 0x6984, "SmartCard-HSM is not initialized");

    var pin = Dialog.prompt("Enter PIN code", "000000");
    sc.verifyUserPIN(new ByteString(pin, ASCII));

    var ks = new HSMKeyStore(sc);
    var keys = ks.enumerateKeys();
    if (keys.length != 0) {
        var erasekeys = Dialog.prompt("<html><p>The HSM is already initialised with somes keys:</p><p>- " + keys + ".</p><p>We need to erase all keys to continue.</p><p>Do you want to continue ?</p></html>");
        assert(erasekeys == "OK", "Impossible to continue, exit!");
        for (var i = 0; i < keys.length; i++) {
            print("Erasing '" + keys[i] + "'...");
            ks.deleteKey(keys[i]);
        }
    }
    


    var crypto = new Crypto();
    var dkek = new DKEK(crypto);
    dkek.importDKEKShare(km.inputDKEKShare());

    const prime256v1 = "1.2.840.10045.3.1.7";
    
    print("Creating 'bl2' EC Key Pair...");
    ks.generateECCKeyPair("bl2", prime256v1);
    print("Creating 'bl31' EC Key Pair...");
    ks.generateECCKeyPair("bl31", prime256v1);
    print("Creating 'bl32' EC Key Pair...");
    ks.generateECCKeyPair("bl32", prime256v1);
    print("Creating 'bl33' EC Key Pair...");
    ks.generateECCKeyPair("bl33", prime256v1);
    print("Creating 'root_of_trust_key' EC Key Pair...");
    ks.generateECCKeyPair("root_of_trust_key", prime256v1);
    print("Creating 'fitImage' RSA Key Pair...");
    ks.generateRSAKeyPair("fitImage", 2048);
    print("Creating 'OTA' RSA Key Pair...");
    ks.generateRSAKeyPair("OTA", 2048);
    
    print("Importing 'cmn_key_idx0' AES Key...");
    var aesfile = Dialog.prompt("Select the 'cmn_key_idx0.bin' file", GPSystem.mapFilename("", GPSystem.USR), null, "*.bin");
    assert(aesfile != "", "No selected file");
    var aesdata = read_bin_file(aesfile);
    import_aes_key(ks, dkek, aesdata, "cmn_key_idx0", 128, "");
    
    print("Finish!");
}

main();

I am able to export the key now! Thanks