cpr icon indicating copy to clipboard operation
cpr copied to clipboard

Accept PKCS11 uri as key or certificate in sslOptions

Open martxel opened this issue 10 months ago • 3 comments

Is your feature request related to a problem?

curl very recently (8.12.0) added support to use keys and certificates with PKCS#11 provider (a way to access keys and certificates in a secure element, instead of files). https://github.com/curl/curl/pull/15587

If you pass a valid pkcs11 uri in the --key or --cert argument, it will use them with the provider configured in OpenSSL.

They also added a new --key-type / --cert-type named "PROV", and if not explicitly set it will take the "PROV" value if a pkcs11 uri is detected in the --key-type / --cert-type arguments.

An example curl command that uses pkcs11 for the key and a file for the cert:

curl --key-type PROV --key "pkcs11:token=mytoken;object=mykey;type=private;pin-value=111111" --cert cert.pem --cacert ca_cert.pem https://localhost/

With cpr, it is not possible to currently use this feature because even if you pass a pkcs11 uri as an argument to KeyFile, it will explicitly set the key type to "PEM".

Possible Solution

One easy solution is to create a new KeyProv class, almost the same as KeyFile, but that returns "PROV" as the key type:

class KeyProv {
  public:
    // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
    KeyProv(fs::path&& p_filename) : filename(std::move(p_filename)) {}

    template <typename FileType, typename PassType>
    KeyProv(FileType&& p_filename, PassType p_password) : filename(std::forward<FileType>(p_filename)), password(std::move(p_password)) {}

    virtual ~KeyProv() {
        util::secureStringClear(password);
    }

    fs::path filename;
    std::string password;

    virtual const char* GetKeyType() const {
        return "PROV";
    }
};

Same with CertFile:

class CertProv {
  public:
    // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
    CertProv(fs::path&& p_filename) : filename(std::move(p_filename)) {}

    virtual ~CertProv() = default;

    const fs::path filename;

    virtual const char* GetCertType() const {
        return "PROV";
    }
};

And the new SetOption:

void SetOption(const ssl::KeyProv& opt) {
    key_file = opt.filename.string();
    key_type = opt.GetKeyType();
    key_pass = opt.password;
}

void SetOption(const ssl::CertProv& opt) {
    cert_file = opt.filename.string();
    cert_type = opt.GetCertType();
}

I have tried this approach and works corrently with libcurl 8.12.1 with the key stored in the TPM:

  // key_file contains a pkcs11 uri
  cpr::SslOptions sslOpts =
      cpr::Ssl(cpr::ssl::TLSv1_2{}, cpr::ssl::CertFile{cert_file},
               cpr::ssl::KeyProv{key_file}, cpr::ssl::CaInfo{ca_file},
               cpr::ssl::VerifyPeer{true}, cpr::ssl::VerifyHost{true});

  cpr::Response r = cpr::Get(cpr::Url{"https://10.0.0.10"}, sslOpts);

Alternatives

Another alternative, and maybe more direct option would be to add another constructor to KeyFile and CertFile to accept the key type as a parameter, than then gets returned by GetCertType or CetKeyType. This way, it would be possible to just use KeyFile("pkcs11:my-uri", "PROV");

Additional Context

I can provide a pull request with either approach.

martxel avatar Mar 07 '25 11:03 martxel

@martxel thanks for reporting! I would love to see this in cpr.

I'd prefer going with the CertProv class approach since it is more verbose and aligns better with the current coding style of cpr.

COM8 avatar Mar 20 '25 14:03 COM8

Ok, I went with that approach in my project. I will provide a merge request in a few days.

Thanks!

martxel avatar Mar 21 '25 07:03 martxel

@martxel Will there be an MR for this? :-)

pntzio avatar Nov 03 '25 08:11 pntzio