Creating a CSR using Botan and softHSM2
TLEUI opened this issue · comments
When attempting to create a CSR using Botan and softHSM2 I get the error CKR_MECHANISM_INVALID. Using Botan 2.19.3 and softHsm2 v2.4.0. Eventually we are going to use a real HSM. I was expecting this to work with the softHSM.
I cooked up a little sandbox mostly based on the Botan PKCS11 examples. I expected this to give me a CSR.
Only when I export the private key and give that to the 'create' function, I will get the CSR. However, I don't want to export the private key.
What am I doing wrong here that causes the CKR_MECHANISM_INVALID?
Here is the code of the sandbox:
#include <botan/der_enc.h>
#include <botan/ec_group.h>
#include <botan/ecdsa.h>
#include <botan/p11.h>
#include <botan/p11_ecc_key.h>
#include <botan/p11_ecdsa.h>
#include <botan/p11_types.h>
#include <botan/p11_randomgenerator.h>
#include <botan/pubkey.h>
#include <botan/x509_ext.h>
#include <botan/pkix_types.h>
#include <botan/x509cert.h>
#include <botan/x509path.h>
#include <botan/pkcs10.h>
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
#define ONLY_RETURN_TOKENS_PRESENT true
#define SESSION_RW false
#define SESSION_RO true
Botan::PKCS11::Module module("/usr/lib/softhsm/libsofthsm2.so"); // This must be configurable
Botan::PKCS11::secure_string user_pin = { '1', '2', '3', '4', '5', '6' }; // This must be configurable
Botan::PKCS11::secure_string so_pin(8, '0'); // This must be configurable
Botan::PKCS11::Slot *slot;
Botan::PKCS11::Session *sessionForKeyHandles;
Botan::PKCS11::PKCS11_ECDSA_PrivateKey *privateKeyHandle;
Botan::PKCS11::PKCS11_ECDSA_PublicKey *publicKeyHandle;
const std::string privateKeyLabel = "test ECDSA hw key";
const std::string publicKeyLabel = "test ECDSA hw pub key";
bool isKeyPresent(const std::string label);
void getSlot(void) {
printf("**** getSlot\n");
// only slots with connected token
std::vector<Botan::PKCS11::SlotId> slots = Botan::PKCS11::Slot::get_available_slots(module, ONLY_RETURN_TOKENS_PRESENT);
// use first slot
slot = new Botan::PKCS11::Slot(module, slots.at(0));
}
void createToken(void) {
// initialize the token
printf("**** createToken\n");
slot->initialize("sandbox", so_pin);
}
void initUserPin(void) {
printf("**** initUserPin\n");
Botan::PKCS11::Session session(*slot, SESSION_RW);
session.login(Botan::PKCS11::UserType::SO, so_pin);
session.init_pin(user_pin);
}
bool isSlotInitialized(void) {
bool initialized = (slot->get_token_info().flags & CKF_USER_PIN_INITIALIZED) != 0;
if (initialized) {
printf(" Slot is already initialized!\n");
} else {
printf(" Slot is NOT initialized!\n");
}
return initialized;
}
void generateKeysInHW(void) {
printf("**** generateKeysInHW\n");
Botan::PKCS11::Session session(*slot, SESSION_RW);
session.login(Botan::PKCS11::UserType::User, user_pin);
printf(" set the private key generation properties\n");
Botan::PKCS11::EC_PrivateKeyGenerationProperties priv_gen_props;
// TODO: What properties do we need?
priv_gen_props.set_token(true);
priv_gen_props.set_private(true);
priv_gen_props.set_sign(true);
priv_gen_props.set_extractable(true);
priv_gen_props.set_unwrap(true);
priv_gen_props.set_wrap_with_trusted(true);
priv_gen_props.set_label(privateKeyLabel);
printf(" set the public key generation properties\n");
Botan::PKCS11::EC_PublicKeyGenerationProperties pub_gen_props(
Botan::EC_Group("secp256r1").DER_encode(Botan::EC_Group_Encoding::EC_DOMPAR_ENC_OID));
pub_gen_props.set_token(true);
pub_gen_props.set_verify(true);
pub_gen_props.set_private(false);
pub_gen_props.set_modifiable(true);
//pub_gen_props.set_trusted(true); <-- set this and youll get CKR_ATTRIBUTE_READ_ONLY error
pub_gen_props.set_wrap(true);
pub_gen_props.set_label(publicKeyLabel);
printf(" create keypair in hardware\n");
Botan::PKCS11::PKCS11_ECDSA_KeyPair keypair = generate_ecdsa_keypair(session,
pub_gen_props,
priv_gen_props);
}
bool isKeyPresent(const std::string label) {
Botan::PKCS11::Session session(*slot, SESSION_RO);
session.login(Botan::PKCS11::UserType::User, user_pin);
Botan::PKCS11::AttributeContainer search_template;
search_template.add_string(Botan::PKCS11::AttributeType::Label, label);
auto found_objs = Botan::PKCS11::Object::search<Botan::PKCS11::Object>(session, search_template.attributes());
if (found_objs.size() > 1) { // We expect 0 or 1. Anything else is WRONG!
printf(" TOO MANY %s!", label.c_str());
exit(1); // Do not continue. We have more keys!
}
bool keyPresent = found_objs.size() == 1;
if (keyPresent) {
printf(" %s is present.\n", label.c_str());
} else {
printf(" %s NOT present.\n", label.c_str());
}
return keyPresent;
}
void getKeyHandles(void) {
printf("**** getKeyHandles\n");
// This session is kept as the key handles will be used by the next sandbox function.
sessionForKeyHandles = new Botan::PKCS11::Session(*slot, SESSION_RW);
sessionForKeyHandles->login(Botan::PKCS11::UserType::User, user_pin);
Botan::PKCS11::AttributeContainer search_template_priv;
search_template_priv.add_string(Botan::PKCS11::AttributeType::Label, privateKeyLabel);
auto found_objs_priv = Botan::PKCS11::Object::search<Botan::PKCS11::Object>(*sessionForKeyHandles, search_template_priv.attributes());
if (found_objs_priv.size() > 0) {
privateKeyHandle = new Botan::PKCS11::PKCS11_ECDSA_PrivateKey(*sessionForKeyHandles, found_objs_priv.at(0).handle());
} else {
printf(" ????1\n");
exit(1); // Should not happen
}
printf(" Private key algo %s\n", privateKeyHandle->algo_name().c_str()); // Just to have a sign of life
Botan::PKCS11::AttributeContainer search_template_pub;
search_template_pub.add_string(Botan::PKCS11::AttributeType::Label, publicKeyLabel);
auto found_objs_pub = Botan::PKCS11::Object::search<Botan::PKCS11::Object>(*sessionForKeyHandles, search_template_pub.attributes());
if (found_objs_pub.size() > 0) {
publicKeyHandle = new Botan::PKCS11::PKCS11_ECDSA_PublicKey(*sessionForKeyHandles, found_objs_pub.at(0).handle());
} else {
printf(" ????1\n");
exit(1); // Should not happen
}
privateKeyHandle->set_public_point(publicKeyHandle->public_point(), Botan::PKCS11::PublicPointEncoding::Raw);
printf(" Public key algo %s\n", publicKeyHandle->algo_name().c_str()); // Just to have a sign of life
}
std::string generateCSR(void) {
printf("**** generateCSR\n");
Botan::X509_DN dn;
Botan::Extensions extentions;
Botan::PKCS11::PKCS11_RNG p11_rng(*sessionForKeyHandles);
dn.add_attribute("2.5.4.11", "BANANAS");
dn.add_attribute("2.5.4.3", "PKCS11_SANDBOX");
auto csr = Botan::PKCS10_Request::create(*privateKeyHandle, dn, extentions, "SHA-256", p11_rng);
return csr.PEM_encode();
}
bool areKeysPresent(void) {
printf("**** areKeysPresent\n");
// Because C(++) is lazy!
bool privPres = isKeyPresent(privateKeyLabel);
bool pubPres = isKeyPresent(publicKeyLabel);
return privPres && pubPres;
}
int main() {
getSlot();
if (!isSlotInitialized()) {
createToken();
initUserPin();
}
if(!areKeysPresent()) {
generateKeysInHW();
}
getKeyHandles();
std::string csr = generateCSR();
printf("%s\n", csr.c_str());
return 0;
}
*/
I can reproduce the problem with your code on my device (Ubuntu 22.04 on WSL2, softhsm 2.6.1).
Debugging into the PKCS#11 wrapper didn't bring up something obviously wrong. Neither did the SoftHSM logs in syslog. Unfortuately, I'm out of time for this week.
FWIW: The exception comes from this invocation:
botan/src/lib/prov/pkcs11/p11_ecdsa.cpp
Line 69 in 1f1d514
when the signature operation first contacts the PKCS#11 module. The
m_mechanism
seems properly set up for signing with EcdsaSha256
, as far as I can tell.