From 88b05b320acb12f22725baf5e3205deb8b4d2e4f Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Mon, 18 Mar 2024 23:12:44 -0400 Subject: [PATCH 1/2] Enable support for keys obtained through PKCS#11 This allows signing using a HSM or a Smartcard (ex. Yubikey) Look at PKCS11.md for help on getting started. Sponsered by: @ZonD80 --- Makefile | 4 + PKCS11.md | 24 ++++++ README.md | 1 + docs/ldid.1 | 17 +++-- ldid.cpp | 208 +++++++++++++++++++++++++++++++++++++++++++--------- ldid.hpp | 14 +++- 6 files changed, 224 insertions(+), 44 deletions(-) create mode 100644 PKCS11.md diff --git a/Makefile b/Makefile index 9372a58..e7b5dd6 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ LIBCRYPTO_INCLUDES ?= $(shell pkg-config --cflags libcrypto) LIBCRYPTO_LIBS ?= $(shell pkg-config --libs libcrypto) endif +ifeq ($(SMARTCARD),1) +CPPFLAGS += -DSMARTCARD +endif + MANPAGE_LANGS := zh_TW zh_CN EXT ?= diff --git a/PKCS11.md b/PKCS11.md new file mode 100644 index 0000000..e5582d0 --- /dev/null +++ b/PKCS11.md @@ -0,0 +1,24 @@ +## Setup +1. Build with `make SMARTCARD=1` +2. Install the OpenSSL engine for PKCS#11 (`libengine-pkcs11-openssl` on Debian, part of `libp11`) + +## Load Key Into Smartcard +It is recommend that you generate the key on the card itself, but you can import it if needed. + +For yubikeys: +1. Extract Cert and Key from p12 +``` +openssl pkcs12 -in Certificates.p12 -out cert.crt.pem -clcerts -nokeys -legacy +openssl pkcs12 -in Certificates.p12 -out key.pem -nocerts -nodes -legacy +``` +2. Import into Key +``` +yubico-piv-tool -s 9c -a import-certificate -i cert.crt.pem +yubico-piv-tool -s 9c -a import-key -i key.pem +yubico-piv-tool -s 9c -a set-chuid +``` +3. You can use `p11tool --list-privkeys --login` to identify the URI for the slot (make sure that `type` is not in the URI, as seperate URIs for the cert and private key are not currently supported from the command line) + +## Sign +1. `ldid -K'pkcs11:model=YubiKey%20YK5;id=%02' -Sents.xml ls.bin` +2. If the correct PKCS#11 module is not being loaded, try setting `PKCS11_MODULE_PATH` in your environment (ex. `export PKCS11_MODULE_PATH="/usr/local/lib/p11-kit-proxy.so"` or `PKCS11_MODULE_PATH="/usr/local/lib/libykcs11.so"`) diff --git a/README.md b/README.md index 9f9cda0..f7e4a73 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ Changes from https://git.saurik.com/ldid.git: - Allow p12 keys to have a password (@sunflsks) - Add a `-arch arch_type` flag so that typing the raw CPU type is not needed - Proper error messages +- Load key using PKCS#11 diff --git a/docs/ldid.1 b/docs/ldid.1 index 99fec31..b6a447a 100644 --- a/docs/ldid.1 +++ b/docs/ldid.1 @@ -20,7 +20,7 @@ .Op Fl H Ns Op Ar sha1 | Ar sha256 .Op Fl h .Op Fl I Ns Ar name -.Op Fl K Ns Ar key.p12 Op Fl U Ns Ar password +.Op Fl K Ns Ar file Op Fl U Ns Ar password .Op Fl M .Op Fl P Ns Op Ar num .Op Fl Q Ns Ar requirements @@ -93,14 +93,19 @@ hash types, flags, CDHash, and CodeDirectory version to Set the identifier used in the binaries signature to .Ar name . If not specified, the basename of the binary is used. -.It Fl K Ns Ar key.p12 +.It Fl K Ns Ar file Sign using the identity in -.Ar key.p12 . +.Ar file . +Must be either a +.Ar p12 +or +.Ar pkcs11: +URI. This will give the binary a valid signature so that it can be run on a system with signature validation. -If -.Ar key.p12 -has a password you will be prompted for it, +If the +.Ar p12 +has a password, you will be prompted for it, or you can specify from the command line with .Fl U . .It Fl M diff --git a/ldid.cpp b/ldid.cpp index 237b1c7..ae46ad8 100644 --- a/ldid.cpp +++ b/ldid.cpp @@ -54,9 +54,12 @@ #include #include #include - #include +# if SMARTCARD +# include +# endif + #include #include "ldid.hpp" @@ -1835,7 +1838,28 @@ class Buffer { } }; -class Stuff { +class NoSigner : public ldid::Signer { + public: + NoSigner() {}; + + operator EVP_PKEY *() const { + return NULL; + } + + operator X509 *() const { + return NULL; + } + + operator STACK_OF(X509) *() const { + return NULL; + } + + operator bool () const { + return false; + } +}; + +class P12Signer : public ldid::Signer { private: PKCS12 *value_; EVP_PKEY *key_; @@ -1843,8 +1867,10 @@ class Stuff { STACK_OF(X509) *ca_; public: - Stuff(BIO *bio) : + P12Signer(BIO *bio) : value_(d2i_PKCS12_bio(bio, NULL)), + key_(NULL), + cert_(NULL), ca_(NULL) { if (value_ == NULL){ @@ -1862,33 +1888,120 @@ class Stuff { fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } - if (key_ == NULL || cert_ == NULL){ + if (key_ == NULL || cert_ == NULL) { fprintf(stderr, "ldid: An error occured while parsing: %s\nYour p12 cert might not be valid\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } if (ca_ == NULL) ca_ = sk_X509_new_null(); - if (ca_ == NULL){ + if (ca_ == NULL) { fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } } - Stuff(const std::string &data) : - Stuff(Buffer(data)) - { - } - - ~Stuff() { + ~P12Signer() { sk_X509_pop_free(ca_, X509_free); X509_free(cert_); EVP_PKEY_free(key_); PKCS12_free(value_); } - operator PKCS12 *() const { - return value_; + operator EVP_PKEY *() const { + return key_; + } + + operator X509 *() const { + return cert_; + } + + operator STACK_OF(X509) *() const { + return ca_; + } + + operator bool () const { + return true; + } +}; + +#if SMARTCARD +class P11Signer : public ldid::Signer { + private: + ENGINE *e_; + EVP_PKEY *key_; + X509 *cert_; + STACK_OF(X509) *ca_; + + public: + P11Signer(std::string uri) + : P11Signer(uri, uri) {} + + P11Signer(std::string keyuri, std::string certuri) : + key_(NULL), + cert_(NULL), + ca_(NULL) + { + e_ = ENGINE_by_id("pkcs11"); + if (!e_) { + fprintf(stderr, "ldid: An error occured while loading pkcs11 engine: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + if (!ENGINE_init(e_)) { + fprintf(stderr, "ldid: Failed to initialize pkcs11 engine: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + ENGINE_free(e_); + + key_ = ENGINE_load_private_key(e_, keyuri.c_str(), NULL, NULL); + if (key_ == NULL){ + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + const char *cmd_name = "LOAD_CERT_CTRL"; + struct { + const char *cert_id; + X509 *cert; + } params; + + params.cert_id = certuri.c_str(); + params.cert = NULL; + + if (ENGINE_ctrl(e_, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, (void *)cmd_name, NULL) == 0) { + fprintf(stderr, "ldid: Engine does not support loading certificate: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + if (ENGINE_ctrl_cmd(e_, cmd_name, 0, ¶ms, NULL, 1) == 0) { + fprintf(stderr, "ldid: Engine cannot load certificate: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + cert_ = params.cert; + + if (cert_ == NULL) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + if (ca_ == NULL) + ca_ = sk_X509_new_null(); + if (ca_ == NULL) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + } + + ~P11Signer() { + sk_X509_pop_free(ca_, X509_free); + X509_free(cert_); + EVP_PKEY_free(key_); + if (e_) { + ENGINE_finish(e_); + ENGINE_free(e_); + } } operator EVP_PKEY *() const { @@ -1902,7 +2015,12 @@ class Stuff { operator STACK_OF(X509) *() const { return ca_; } + + operator bool () const { + return true; + } }; +#endif class Octet { private: @@ -1976,7 +2094,7 @@ class Signature { PKCS7 *value_; public: - Signature(const Stuff &stuff, const Buffer &data, const std::string &xml, const ldid::Hash &hash) { + Signature(const ldid::Signer &signer, const Buffer &data, const std::string &xml, const ldid::Hash &hash) { value_ = PKCS7_new(); if (value_ == NULL){ fprintf(stderr, "ldid: An error occured while getting creating PKCS7 file: %s\n", ERR_error_string(ERR_get_error(), NULL)); @@ -1989,7 +2107,7 @@ class Signature { exit(1); } - STACK_OF(X509) *certs(stuff); + STACK_OF(X509) *certs(signer); for (unsigned i(0), e(sk_X509_num(certs)); i != e; i++) { if (PKCS7_add_certificate(value_, sk_X509_value(certs, e - i - 1)) == 0) { fprintf(stderr, "ldid: An error occured while signing: %s\n", ERR_error_string(ERR_get_error(), NULL)); @@ -2001,7 +2119,7 @@ class Signature { _assert(attributes != NULL); _scope({ sk_X509_ATTRIBUTE_pop_free(attributes, X509_ATTRIBUTE_free); }); - auto info(PKCS7_sign_add_signer(value_, stuff, stuff, NULL, PKCS7_NOSMIMECAP)); + auto info(PKCS7_sign_add_signer(value_, signer, signer, NULL, PKCS7_NOSMIMECAP)); if (info == NULL){ fprintf(stderr, "ldid: An error occured while signing: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); @@ -2270,16 +2388,15 @@ static void req(std::streambuf &buffer, uint8_t (&&data)[Size_]) { put(buffer, zeros, 3 - (Size_ + 3) % 4); } -Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, uint32_t flags, uint8_t platform, const Progress &progress) { +Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const ldid::Signer &signer, const Slots &slots, uint32_t flags, uint8_t platform, const Progress &progress) { Hash hash; std::string team; std::string common; - if (!key.empty()) { - Stuff stuff(key); - auto name(X509_get_subject_name(stuff)); + if (signer) { + auto name(X509_get_subject_name(signer)); if (name == NULL){ fprintf(stderr, "ldid: Your certificate might not be valid: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); @@ -2412,7 +2529,7 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st for (Algorithm *algorithm : GetAlgorithms()) alloc = Align(alloc + directory + (special + normal) * algorithm->size_, 16); - if (!key.empty()) { + if (signer) { alloc += sizeof(struct BlobIndex); alloc += sizeof(struct Blob); alloc += certificate; @@ -2567,7 +2684,7 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st ++total; } - if (!key.empty()) { + if (signer) { auto plist(plist_new_dict()); _scope({ plist_free(plist); }); @@ -2600,10 +2717,9 @@ Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::st std::stringbuf data; const std::string &sign(blobs[CSSLOT_CODEDIRECTORY]); - Stuff stuff(key); Buffer bio(sign); - Signature signature(stuff, sign, std::string(xml, size), hash); + Signature signature(signer, sign, std::string(xml, size), hash); Buffer result(signature); std::string value(result); put(data, value.data(), value.size()); @@ -2970,7 +3086,7 @@ struct RuleCode { } }; -static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Hash &hash, std::streambuf &save, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, size_t length, uint32_t flags, uint8_t platform, const Progress &progress) { +static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Hash &hash, std::streambuf &save, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const ldid::Signer &signer, const Slots &slots, size_t length, uint32_t flags, uint8_t platform, const Progress &progress) { // XXX: this is a miserable fail std::stringbuf temp; put(temp, prefix, size); @@ -2980,7 +3096,7 @@ static Hash Sign(const uint8_t *prefix, size_t size, std::streambuf &buffer, Has auto data(temp.str()); HashProxy proxy(hash, save); - return Sign(data.data(), data.size(), proxy, identifier, entitlements, merge, requirements, key, slots, flags, platform, progress); + return Sign(data.data(), data.size(), proxy, identifier, entitlements, merge, requirements, signer, slots, flags, platform, progress); } struct State { @@ -2995,7 +3111,7 @@ struct State { } }; -Bundle Sign(const std::string &root, Folder &parent, const std::string &key, State &local, const std::string &requirements, const Functor &alter, bool merge, uint8_t platform, const Progress &progress) { +Bundle Sign(const std::string &root, Folder &parent, const ldid::Signer &signer, State &local, const std::string &requirements, const Functor &alter, bool merge, uint8_t platform, const Progress &progress) { std::string executable; std::string identifier; @@ -3102,7 +3218,7 @@ Bundle Sign(const std::string &root, Folder &parent, const std::string &key, Sta SubFolder subfolder(folder, bundle); State remote; - bundles[nested[1]] = Sign(root + bundle, subfolder, key, remote, requirements, Starts(name, "PlugIns/") ? alter : + bundles[nested[1]] = Sign(root + bundle, subfolder, signer, remote, requirements, Starts(name, "PlugIns/") ? alter : static_cast &>(fun([&](const std::string &, const std::string &) -> std::string { return entitlements; })) , merge, platform, progress); local.Merge(bundle, remote); @@ -3160,7 +3276,7 @@ Bundle Sign(const std::string &root, Folder &parent, const std::string &key, Sta case MH_CIGAM: case MH_CIGAM_64: folder.Save(name, true, flag, fun([&](std::streambuf &save) { Slots slots; - Sign(header.bytes, size, data, hash, save, identifier, entitlements, merge, requirements, key, slots, length, 0, platform, Progression(progress, root + name)); + Sign(header.bytes, size, data, hash, save, identifier, entitlements, merge, requirements, signer, slots, length, 0, platform, Progression(progress, root + name)); })); return; } @@ -3291,16 +3407,16 @@ Bundle Sign(const std::string &root, Folder &parent, const std::string &key, Sta Slots slots; slots[1] = local.files.at(info); slots[3] = local.files.at(signature); - bundle.hash = Sign(NULL, 0, buffer, local.files[executable], save, identifier, entitlements, merge, requirements, key, slots, length, 0, platform, Progression(progress, root + executable)); + bundle.hash = Sign(NULL, 0, buffer, local.files[executable], save, identifier, entitlements, merge, requirements, signer, slots, length, 0, platform, Progression(progress, root + executable)); })); })); return bundle; } -Bundle Sign(const std::string &root, Folder &folder, const std::string &key, const std::string &requirements, const Functor &alter, bool merge, uint8_t platform, const Progress &progress) { +Bundle Sign(const std::string &root, Folder &folder, const ldid::Signer &signer, const std::string &requirements, const Functor &alter, bool merge, uint8_t platform, const Progress &progress) { State local; - return Sign(root, folder, key, local, requirements, alter, merge, platform, progress); + return Sign(root, folder, signer, local, requirements, alter, merge, platform, progress); } #endif @@ -3347,6 +3463,10 @@ int main(int argc, char *argv[]) { OSSL_PROVIDER *deflt = OSSL_PROVIDER_load(NULL, "default"); # endif +# if SMARTCARD + ENGINE_load_builtin_engines(); +# endif + union { uint16_t word; uint8_t byte[2]; @@ -3385,7 +3505,8 @@ int main(int argc, char *argv[]) { Map entitlements; Map requirements; - Map key; + std::string key; + ldid::Signer *signer = new NoSigner(); ldid::Slots slots; std::vector files; @@ -3590,7 +3711,7 @@ int main(int argc, char *argv[]) { case 'K': if (argv[argi][2] != '\0') - key.open(argv[argi] + 2, O_RDONLY, PROT_READ, MAP_PRIVATE); + key = argv[argi] + 2; break; case 'T': break; @@ -3617,6 +3738,17 @@ int main(int argc, char *argv[]) { if (files.empty()) return 0; + if (!key.empty()) { +#if SMARTCARD + if (key.compare(0, 7, "pkcs11:") == 0) { + signer = new P11Signer(key); + } else +#endif + { + signer = new P12Signer(Buffer(Map(key, O_RDONLY))); + } + } + size_t filei(0), filee(0); _foreach (file, files) try { std::string path(file); @@ -3633,7 +3765,7 @@ int main(int argc, char *argv[]) { exit(1); } ldid::DiskFolder folder(path + "/"); - path += "/" + Sign("", folder, key, requirements, ldid::fun([&](const std::string &, const std::string &) -> std::string { return entitlements; }), flag_M, platform, dummy_).path; + path += "/" + Sign("", folder, *signer, requirements, ldid::fun([&](const std::string &, const std::string &) -> std::string { return entitlements; }), flag_M, platform, dummy_).path; } else if (flag_S || flag_r || flag_s) { Map input(path, O_RDONLY, PROT_READ, MAP_PRIVATE); @@ -3668,7 +3800,7 @@ int main(int argc, char *argv[]) { } } } - ldid::Sign(input.data(), input.size(), output, identifier, entitlements, flag_M, requirements, key, slots, flags, platform, dummy_); + ldid::Sign(input.data(), input.size(), output, identifier, entitlements, flag_M, requirements, *signer, slots, flags, platform, dummy_); } input.clear(); @@ -3922,6 +4054,10 @@ int main(int argc, char *argv[]) { OSSL_PROVIDER_unload(deflt); # endif +# if SMARTCARD + ENGINE_cleanup(); +# endif + return filee; } #endif // LDID_NOTOOLS diff --git a/ldid.hpp b/ldid.hpp index 688b14c..af425e2 100644 --- a/ldid.hpp +++ b/ldid.hpp @@ -11,6 +11,8 @@ #include #include +#include + namespace ldid { // I wish Apple cared about providing quality toolchains :/ @@ -157,11 +159,19 @@ struct Bundle { Hash hash; }; -Bundle Sign(const std::string &root, Folder &folder, const std::string &key, const std::string &requirements, const Functor &alter, bool merge, uint8_t platform, const Progress &progress); +class Signer { + public: + virtual operator EVP_PKEY *() const = 0; + virtual operator X509 *() const = 0; + virtual operator STACK_OF(X509) *() const = 0; + virtual operator bool () const = 0; +}; + +Bundle Sign(const std::string &root, Folder &folder, const Signer &signer, const std::string &requirements, const Functor &alter, bool merge, uint8_t platform, const Progress &progress); typedef std::map Slots; -Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const std::string &key, const Slots &slots, uint32_t flags, uint8_t platform, const Progress &progress); +Hash Sign(const void *idata, size_t isize, std::streambuf &output, const std::string &identifier, const std::string &entitlements, bool merge, const std::string &requirements, const Signer &signer, const Slots &slots, uint32_t flags, uint8_t platform, const Progress &progress); } From 798f55bab61c6a3cf45f81014527bbe2b473958b Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Tue, 19 Mar 2024 10:28:21 -0700 Subject: [PATCH 2/2] Add -X flag for seperate pkcs11 uri for Certificate Ex: ldid -K'pkcs11:object=MyKey;type=private' -X'pkcs11:object=MyKey;type=cert' ldid -KCertificates.p12 -XExtractedFromP12.cer -XAppleWWDRCAG3.cer -XAppleIncRootCertificate.cer --- PKCS11.md | 4 +- docs/ldid.1 | 16 +++++- ldid.cpp | 157 +++++++++++++++++++++++++++++++++++----------------- ldid.hpp | 1 + 4 files changed, 124 insertions(+), 54 deletions(-) diff --git a/PKCS11.md b/PKCS11.md index e5582d0..84ddebe 100644 --- a/PKCS11.md +++ b/PKCS11.md @@ -17,8 +17,8 @@ yubico-piv-tool -s 9c -a import-certificate -i cert.crt.pem yubico-piv-tool -s 9c -a import-key -i key.pem yubico-piv-tool -s 9c -a set-chuid ``` -3. You can use `p11tool --list-privkeys --login` to identify the URI for the slot (make sure that `type` is not in the URI, as seperate URIs for the cert and private key are not currently supported from the command line) +3. You can use `p11tool --list-privkeys --login` and `p11tool --list-certs --login` to help identify the URIs for the private key and certificate ## Sign -1. `ldid -K'pkcs11:model=YubiKey%20YK5;id=%02' -Sents.xml ls.bin` +1. `ldid -K'pkcs11:object=Private%20key%20for%20Digital%20Signature;type=private' -X'pkcs11:object=X.509%20Certificate%20for%20Digital%20Signature;type=cert' -Sents.xml ls.bin` 2. If the correct PKCS#11 module is not being loaded, try setting `PKCS11_MODULE_PATH` in your environment (ex. `export PKCS11_MODULE_PATH="/usr/local/lib/p11-kit-proxy.so"` or `PKCS11_MODULE_PATH="/usr/local/lib/libykcs11.so"`) diff --git a/docs/ldid.1 b/docs/ldid.1 index b6a447a..6ce4e95 100644 --- a/docs/ldid.1 +++ b/docs/ldid.1 @@ -20,7 +20,7 @@ .Op Fl H Ns Op Ar sha1 | Ar sha256 .Op Fl h .Op Fl I Ns Ar name -.Op Fl K Ns Ar file Op Fl U Ns Ar password +.Op Fl K Ns Ar file Oo Fl U Ns Ar password Oc Op Fl X Ns Ar file .Op Fl M .Op Fl P Ns Op Ar num .Op Fl Q Ns Ar requirements @@ -108,6 +108,8 @@ If the has a password, you will be prompted for it, or you can specify from the command line with .Fl U . +To specify the certificate separate from the private key, use +.Fl X . .It Fl M When used with .Fl S , @@ -160,6 +162,18 @@ target is a bundle directory, and not a specific Mach-O file. .Fl w can be used on any bundle, not just the root .app, including frameworks, appexes, and more. +.It Fl X Ns Ar file +Adds +.Ar file +as a certificate to be used when signing. +The first +.Ar file +must be the certificate for the signing key, each additional will be added as part of the chain. +Must be either +.Ar DER +encoded certificate or +.Ar pkcs11: +URI. .El .Sh EXAMPLES To fakesign diff --git a/ldid.cpp b/ldid.cpp index ae46ad8..72d19cd 100644 --- a/ldid.cpp +++ b/ldid.cpp @@ -44,6 +44,11 @@ #include #include +# if SMARTCARD +# define OPENSSL_SUPPRESS_DEPRECATED +/* We need to use engines, which are deprecated */ +# endif + #include # if OPENSSL_VERSION_MAJOR >= 3 # include @@ -1867,7 +1872,7 @@ class P12Signer : public ldid::Signer { STACK_OF(X509) *ca_; public: - P12Signer(BIO *bio) : + P12Signer(BIO *bio, std::vector certs) : value_(d2i_PKCS12_bio(bio, NULL)), key_(NULL), cert_(NULL), @@ -1888,7 +1893,7 @@ class P12Signer : public ldid::Signer { fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } - if (key_ == NULL || cert_ == NULL) { + if (key_ == NULL || (cert_ == NULL && certs.empty())) { fprintf(stderr, "ldid: An error occured while parsing: %s\nYour p12 cert might not be valid\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } @@ -1899,6 +1904,22 @@ class P12Signer : public ldid::Signer { fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } + + if (!certs.empty()) { + cert_ = d2i_X509_bio(Buffer(Map(certs[0], false)), NULL); + certs.erase(certs.begin()); + _foreach (certid, certs) { + X509 *cert = d2i_X509_bio(Buffer(Map(certid, false)), NULL); + if (cert == NULL) { + fprintf(stderr, "ldid: An error occured while parsing: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + if (sk_X509_push(ca_, cert) == 0) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + } + } } ~P12Signer() { @@ -1932,16 +1953,63 @@ class P11Signer : public ldid::Signer { EVP_PKEY *key_; X509 *cert_; STACK_OF(X509) *ca_; + bool supportLoadCert = false; + + X509 *loadcert(std::string cert_id) { + if (cert_id.compare(0, 7, "pkcs11:") == 0) { + const char *cmd_name = "LOAD_CERT_CTRL"; + struct { + const char *cert_id; + X509 *cert; + } params; + + params.cert_id = cert_id.c_str(); + params.cert = NULL; + + if (!supportLoadCert) { + if (ENGINE_ctrl(e_, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, (void *)cmd_name, NULL) == 0) { + fprintf(stderr, "ldid: Engine does not support loading certificate: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + supportLoadCert = true; + } + + if (ENGINE_ctrl_cmd(e_, cmd_name, 0, ¶ms, NULL, 1) == 0) { + fprintf(stderr, "ldid: Engine cannot load certificate: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + if (params.cert == NULL) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + return params.cert; + } else { + X509 *cert = d2i_X509_bio(Buffer(Map(cert_id, false)), NULL); + if (cert == NULL) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + + return cert; + } + } public: P11Signer(std::string uri) - : P11Signer(uri, uri) {} + : P11Signer(uri, {}) {} - P11Signer(std::string keyuri, std::string certuri) : + P11Signer(std::string keyuri, std::vector certs) : key_(NULL), cert_(NULL), - ca_(NULL) + ca_(sk_X509_new_null()) { + if (ca_ == NULL) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + e_ = ENGINE_by_id("pkcs11"); if (!e_) { fprintf(stderr, "ldid: An error occured while loading pkcs11 engine: %s\n", ERR_error_string(ERR_get_error(), NULL)); @@ -1952,7 +2020,7 @@ class P11Signer : public ldid::Signer { fprintf(stderr, "ldid: Failed to initialize pkcs11 engine: %s\n", ERR_error_string(ERR_get_error(), NULL)); exit(1); } - ENGINE_free(e_); + ENGINE_free(e_); // for ENGINE_by_id() key_ = ENGINE_load_private_key(e_, keyuri.c_str(), NULL, NULL); if (key_ == NULL){ @@ -1960,37 +2028,17 @@ class P11Signer : public ldid::Signer { exit(1); } - const char *cmd_name = "LOAD_CERT_CTRL"; - struct { - const char *cert_id; - X509 *cert; - } params; - - params.cert_id = certuri.c_str(); - params.cert = NULL; - - if (ENGINE_ctrl(e_, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, (void *)cmd_name, NULL) == 0) { - fprintf(stderr, "ldid: Engine does not support loading certificate: %s\n", ERR_error_string(ERR_get_error(), NULL)); - exit(1); - } - - if (ENGINE_ctrl_cmd(e_, cmd_name, 0, ¶ms, NULL, 1) == 0) { - fprintf(stderr, "ldid: Engine cannot load certificate: %s\n", ERR_error_string(ERR_get_error(), NULL)); - exit(1); - } - - cert_ = params.cert; - - if (cert_ == NULL) { - fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); - exit(1); - } - - if (ca_ == NULL) - ca_ = sk_X509_new_null(); - if (ca_ == NULL) { - fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); - exit(1); + if (certs.empty()) { + cert_ = loadcert(keyuri); + } else { + cert_ = loadcert(certs[0]); + certs.erase(certs.begin()); + _foreach (certid, certs) { + if (sk_X509_push(ca_, loadcert(certid)) == 0) { + fprintf(stderr, "ldid: An error occured while loading: %s\n", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + } } } @@ -1998,10 +2046,7 @@ class P11Signer : public ldid::Signer { sk_X509_pop_free(ca_, X509_free); X509_free(cert_); EVP_PKEY_free(key_); - if (e_) { - ENGINE_finish(e_); - ENGINE_free(e_); - } + ENGINE_finish(e_); // for ENGINE_init() } operator EVP_PKEY *() const { @@ -3506,6 +3551,7 @@ int main(int argc, char *argv[]) { Map entitlements; Map requirements; std::string key; + std::vector certs; ldid::Signer *signer = new NoSigner(); ldid::Slots slots; @@ -3714,6 +3760,11 @@ int main(int argc, char *argv[]) { key = argv[argi] + 2; break; + case 'X': + if (argv[argi][2] != '\0') + certs.push_back(argv[argi] + 2); + break; + case 'T': break; case 'u': { @@ -3739,14 +3790,16 @@ int main(int argc, char *argv[]) { return 0; if (!key.empty()) { -#if SMARTCARD + delete signer; if (key.compare(0, 7, "pkcs11:") == 0) { - signer = new P11Signer(key); - } else +#if SMARTCARD + signer = new P11Signer(key, certs); +#else + fprintf(stderr, "ldid: Smartcard support is not enabled\n"); + exit(1); #endif - { - signer = new P12Signer(Buffer(Map(key, O_RDONLY))); - } + } else + signer = new P12Signer(Buffer(Map(key, O_RDONLY, PROT_READ, MAP_PRIVATE)), certs); } size_t filei(0), filee(0); @@ -4049,15 +4102,17 @@ int main(int argc, char *argv[]) { ++filei; } -# if OPENSSL_VERSION_MAJOR >= 3 - OSSL_PROVIDER_unload(legacy); - OSSL_PROVIDER_unload(deflt); -# endif + delete signer; # if SMARTCARD ENGINE_cleanup(); # endif +# if OPENSSL_VERSION_MAJOR >= 3 + OSSL_PROVIDER_unload(legacy); + OSSL_PROVIDER_unload(deflt); +# endif + return filee; } #endif // LDID_NOTOOLS diff --git a/ldid.hpp b/ldid.hpp index af425e2..740364f 100644 --- a/ldid.hpp +++ b/ldid.hpp @@ -161,6 +161,7 @@ struct Bundle { class Signer { public: + virtual ~Signer() {}; virtual operator EVP_PKEY *() const = 0; virtual operator X509 *() const = 0; virtual operator STACK_OF(X509) *() const = 0;