randombit / botan

Cryptography Toolkit

Home Page:https://botan.randombit.net

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

m_key.module()->C_SignInit(m_key.session().handle(), m_mechanism.data(), m_key.handle());

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.