Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,15 @@ removal and extraction.

== INSTALLATION

The usual way:
Make sure dependent libraries are installed:

* On Ubuntu-18.04 to 22.10:
sudo apt install libssl-dev autoconf build-essential automake libtool libcurl4-openssl-dev libengine-pkcs11-openssl

Then download, build and install:

git clone https://github.com/larskanis/osslsigncode
sh autogen.sh
./configure
make
make install
Expand All @@ -41,7 +48,8 @@ The usual way:
== USAGE

Before you can sign a file you need a Software Publishing
Certificate (spc) and a corresponding private key.
Certificate (spc) and a corresponding private key as file, on
smart card or a HSM.

This article provides a good starting point as to how
to do the signing with the Microsoft signtool.exe:
Expand All @@ -50,8 +58,10 @@ to do the signing with the Microsoft signtool.exe:

To sign with osslsigncode you need the certificate file mentioned in the
article above, in SPC or PEM format, and you will also need the private
key which must be a key file in DER or PEM format, or if osslsigncode was
compiled against OpenSSL 1.0.0 or later, in PVK format.
key which must be a key file in DER, PEM format or PVK format.
Alternatively this version of osslsigncode supports private keys stored
on high security modules (HSM) through OpenSSL engines.


To sign a PE or MSI file you can now do:

Expand Down Expand Up @@ -80,6 +90,34 @@ You can use a certificate and key stored in a PKCS#12 container:
-n "Your Application" -i http://www.yourwebsite.com/ \
-in yourapp.exe -out yourapp-signed.exe

To use a key stored on a HSM or a smart card, you need a OpenSSL engine
compatible shared library for operations on the private key.
Since most HSMs don't support the OpenSSL engine interface directly
but the more common standard PKCS#11, it's necessary to use a bridge
called engine-pkcs11. It can be installed like:

sudo apt install libengine-pkcs11-openssl

Furthermore you can make use the pkcs11 library of the OpenSC project,
which is compatible to many smart cards. It can be installed like:

sudo apt install opensc

To sign a PE file with private key stored on smart card and
interactive prompt for the PIN code:

osslsigncode sign -certs <cert-file> -askpass \
-pkcs11engine /usr/lib/x86_64-linux-gnu/engines-3/libpkcs11.so \
-pkcs11module /usr/lib/x86_64-linux-gnu/opensc-pkcs11.so \
-in yourapp.exe -out yourapp-signed.exe

Use `/usr/lib/x86_64-linux-gnu/engines-1.1/libpkcs11.so` on a OpenSSL-1.1
based distribution instead.
The certificate has to be provided as a file. It is currently not
supported to read the certificate from the smard card directly.
Some smart card vendors provide their own PKCS#11 library, which can be
used alternatively instead of opensc-pkcs11.so.

To sign a CAB file containing java class files:

osslsigncode sign -certs <cert-file> -key <key-file> \
Expand Down
176 changes: 48 additions & 128 deletions osslsigncode.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,16 +450,16 @@ static SpcSpOpusInfo* createOpus(const char *desc, const char *url)
if (desc) {
info->programName = SpcString_new();
info->programName->type = 1;
info->programName->value.ascii = M_ASN1_IA5STRING_new();
ASN1_STRING_set((ASN1_STRING *)info->programName->value.ascii,
info->programName->value.ascii = ASN1_IA5STRING_new();
ASN1_STRING_set(info->programName->value.ascii,
(const unsigned char*)desc, strlen(desc));
}

if (url) {
info->moreInfo = SpcLink_new();
info->moreInfo->type = 0;
info->moreInfo->value.url = M_ASN1_IA5STRING_new();
ASN1_STRING_set((ASN1_STRING *)info->moreInfo->value.url,
info->moreInfo->value.url = ASN1_IA5STRING_new();
ASN1_STRING_set(info->moreInfo->value.url,
(const unsigned char*)url, strlen(url));
}

Expand Down Expand Up @@ -609,19 +609,20 @@ static int add_timestamp(PKCS7 *sig, char *url, char *proxy, int rfc3161, const

if (rfc3161) {
unsigned char mdbuf[EVP_MAX_MD_SIZE];
EVP_MD_CTX mdctx;
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();

EVP_MD_CTX_init(&mdctx);
EVP_DigestInit(&mdctx, md);
EVP_DigestUpdate(&mdctx, si->enc_digest->data, si->enc_digest->length);
EVP_DigestFinal(&mdctx, mdbuf, NULL);
EVP_DigestInit(mdctx, md);
EVP_DigestUpdate(mdctx, si->enc_digest->data, si->enc_digest->length);
EVP_DigestFinal(mdctx, mdbuf, NULL);
EVP_MD_CTX_free(mdctx);
mdctx = NULL;

TimeStampReq *req = TimeStampReq_new();
ASN1_INTEGER_set(req->version, 1);
req->messageImprint->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(md));
req->messageImprint->digestAlgorithm->parameters = ASN1_TYPE_new();
req->messageImprint->digestAlgorithm->parameters->type = V_ASN1_NULL;
M_ASN1_OCTET_STRING_set(req->messageImprint->digest, mdbuf, EVP_MD_size(md));
ASN1_OCTET_STRING_set(req->messageImprint->digest, mdbuf, EVP_MD_size(md));
req->certReq = (void*)0x1;

len = i2d_TimeStampReq(req, NULL);
Expand Down Expand Up @@ -815,7 +816,7 @@ static void cleanup_lib_state(void)
EVP_cleanup();
CONF_modules_free();
CRYPTO_cleanup_all_ex_data();
#if OPENSSL_VERSION_NUMBER > 0x10000000
#if OPENSSL_VERSION_NUMBER > 0x10000000 && OPENSSL_VERSION_NUMBER < 0x10100000
ERR_remove_thread_state(NULL);
#endif
ERR_free_strings();
Expand Down Expand Up @@ -921,83 +922,8 @@ static const unsigned char classid_page_hash[] = {
0xAE, 0x05, 0xA2, 0x17, 0xDA, 0x8E, 0x60, 0xD6
};

static unsigned char *calc_page_hash(char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos, int phtype, unsigned int *phlen);

DECLARE_STACK_OF(ASN1_OCTET_STRING)
#ifndef sk_ASN1_OCTET_STRING_new_null
#define sk_ASN1_OCTET_STRING_new_null() SKM_sk_new_null(ASN1_OCTET_STRING)
#define sk_ASN1_OCTET_STRING_free(st) SKM_sk_free(ASN1_OCTET_STRING, (st))
#define sk_ASN1_OCTET_STRING_push(st, val) SKM_sk_push(ASN1_OCTET_STRING, (st), (val))
#define i2d_ASN1_SET_OF_ASN1_OCTET_STRING(st, pp, i2d_func, ex_tag, ex_class, is_set) \
SKM_ASN1_SET_OF_i2d(ASN1_OCTET_STRING, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set))
#endif

DECLARE_STACK_OF(SpcAttributeTypeAndOptionalValue)
#ifndef sk_SpcAttributeTypeAndOptionalValue_new_null
#define sk_SpcAttributeTypeAndOptionalValue_new_null() SKM_sk_new_null(SpcAttributeTypeAndOptionalValue)
#define sk_SpcAttributeTypeAndOptionalValue_free(st) SKM_sk_free(SpcAttributeTypeAndOptionalValue, (st))
#define sk_SpcAttributeTypeAndOptionalValue_push(st, val) SKM_sk_push(SpcAttributeTypeAndOptionalValue, (st), (val))
#define i2d_SpcAttributeTypeAndOptionalValue(st, pp, i2d_func, ex_tag, ex_class, is_set) \
SKM_ASN1_SET_OF_i2d(SpcAttributeTypeAndOptionalValue, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set))
#endif

static SpcLink *get_page_hash_link(int phtype, char *indata, unsigned int peheader, int pe32plus, unsigned int sigpos)
{
unsigned int phlen;
unsigned char *ph = calc_page_hash(indata, peheader, pe32plus, sigpos, phtype, &phlen);
if (!ph) {
fprintf(stderr, "Failed to calculate page hash\n");
exit(-1);
}

ASN1_OCTET_STRING *ostr = M_ASN1_OCTET_STRING_new();
M_ASN1_OCTET_STRING_set(ostr, ph, phlen);
free(ph);

STACK_OF(ASN1_OCTET_STRING) *oset = sk_ASN1_OCTET_STRING_new_null();
sk_ASN1_OCTET_STRING_push(oset, ostr);
unsigned char *p, *tmp;
unsigned int l;
l = i2d_ASN1_SET_OF_ASN1_OCTET_STRING(oset, NULL, i2d_ASN1_OCTET_STRING,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
tmp = p = OPENSSL_malloc(l);
i2d_ASN1_SET_OF_ASN1_OCTET_STRING(oset, &tmp, i2d_ASN1_OCTET_STRING,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
ASN1_OCTET_STRING_free(ostr);
sk_ASN1_OCTET_STRING_free(oset);

SpcAttributeTypeAndOptionalValue *aval = SpcAttributeTypeAndOptionalValue_new();
aval->type = OBJ_txt2obj((phtype == NID_sha1) ? SPC_PE_IMAGE_PAGE_HASHES_V1 : SPC_PE_IMAGE_PAGE_HASHES_V2, 1);
aval->value = ASN1_TYPE_new();
aval->value->type = V_ASN1_SET;
aval->value->value.set = ASN1_STRING_new();
ASN1_STRING_set(aval->value->value.set, p, l);
OPENSSL_free(p);

STACK_OF(SpcAttributeTypeAndOptionalValue) *aset = sk_SpcAttributeTypeAndOptionalValue_new_null();
sk_SpcAttributeTypeAndOptionalValue_push(aset, aval);
l = i2d_SpcAttributeTypeAndOptionalValue(aset, NULL, i2d_SpcAttributeTypeAndOptionalValue,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
tmp = p = OPENSSL_malloc(l);
l = i2d_SpcAttributeTypeAndOptionalValue(aset, &tmp, i2d_SpcAttributeTypeAndOptionalValue,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
sk_SpcAttributeTypeAndOptionalValue_free(aset);
SpcAttributeTypeAndOptionalValue_free(aval);

SpcSerializedObject *so = SpcSerializedObject_new();
M_ASN1_OCTET_STRING_set(so->classId, classid_page_hash, sizeof(classid_page_hash));
M_ASN1_OCTET_STRING_set(so->serializedData, p, l);
OPENSSL_free(p);

SpcLink *link = SpcLink_new();
link->type = 1;
link->value.moniker = so;
return link;
}

static void get_indirect_data_blob(u_char **blob, int *len, const EVP_MD *md, file_type_t type,
int pagehash, char *indata, unsigned int peheader, int pe32plus,
char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos)
{
static const unsigned char msistr[] = {
Expand All @@ -1024,14 +950,7 @@ static void get_indirect_data_blob(u_char **blob, int *len, const EVP_MD *md, fi
} else if (type == FILE_TYPE_PE) {
SpcPeImageData *pid = SpcPeImageData_new();
ASN1_BIT_STRING_set(pid->flags, (unsigned char*)"0", 0);
if (pagehash) {
int phtype = NID_sha1;
if (EVP_MD_size(md) > EVP_MD_size(EVP_sha1()))
phtype = NID_sha256;
pid->file = get_page_hash_link(phtype, indata, peheader, pe32plus, sigpos);
} else {
pid->file = get_obsolete_link();
}
pid->file = get_obsolete_link();
l = i2d_SpcPeImageData(pid, NULL);
p = OPENSSL_malloc(l);
i2d_SpcPeImageData(pid, &p);
Expand All @@ -1046,7 +965,7 @@ static void get_indirect_data_blob(u_char **blob, int *len, const EVP_MD *md, fi
ASN1_INTEGER_set(si->d, 0);
ASN1_INTEGER_set(si->e, 0);
ASN1_INTEGER_set(si->f, 0);
M_ASN1_OCTET_STRING_set(si->string, msistr, sizeof(msistr));
ASN1_OCTET_STRING_set(si->string, msistr, sizeof(msistr));
l = i2d_SpcSipInfo(si, NULL);
p = OPENSSL_malloc(l);
i2d_SpcSipInfo(si, &p);
Expand All @@ -1068,7 +987,7 @@ static void get_indirect_data_blob(u_char **blob, int *len, const EVP_MD *md, fi
hashlen = EVP_MD_size(md);
hash = OPENSSL_malloc(hashlen);
memset(hash, 0, hashlen);
M_ASN1_OCTET_STRING_set(idc->messageDigest->digest, hash, hashlen);
ASN1_OCTET_STRING_set(idc->messageDigest->digest, hash, hashlen);
OPENSSL_free(hash);

*len = i2d_SpcIndirectDataContent(idc, NULL);
Expand Down Expand Up @@ -1923,19 +1842,18 @@ static void calc_pe_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf,
unsigned int peheader, int pe32plus, unsigned int fileend)
{
static unsigned char bfb[16*1024*1024];
EVP_MD_CTX mdctx;
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();

EVP_MD_CTX_init(&mdctx);
EVP_DigestInit(&mdctx, md);
EVP_DigestInit(mdctx, md);

memset(mdbuf, 0, EVP_MAX_MD_SIZE);

(void)BIO_seek(bio, 0);
BIO_read(bio, bfb, peheader + 88);
EVP_DigestUpdate(&mdctx, bfb, peheader + 88);
EVP_DigestUpdate(mdctx, bfb, peheader + 88);
BIO_read(bio, bfb, 4);
BIO_read(bio, bfb, 60+pe32plus*16);
EVP_DigestUpdate(&mdctx, bfb, 60+pe32plus*16);
EVP_DigestUpdate(mdctx, bfb, 60+pe32plus*16);
BIO_read(bio, bfb, 8);

unsigned int n = peheader + 88 + 4 + 60+pe32plus*16 + 8;
Expand All @@ -1946,11 +1864,12 @@ static void calc_pe_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf,
int l = BIO_read(bio, bfb, want);
if (l <= 0)
break;
EVP_DigestUpdate(&mdctx, bfb, l);
EVP_DigestUpdate(mdctx, bfb, l);
n += l;
}

EVP_DigestFinal(&mdctx, mdbuf, NULL);
EVP_DigestFinal(mdctx, mdbuf, NULL);
EVP_MD_CTX_free(mdctx);
}


Expand Down Expand Up @@ -2019,16 +1938,15 @@ static unsigned char *calc_page_hash(char *indata, unsigned int peheader, int pe
int phlen = pphlen * (3 + nsections + sigpos / pagesize);
unsigned char *res = malloc(phlen);
unsigned char *zeroes = calloc(pagesize, 1);
EVP_MD_CTX mdctx;

EVP_MD_CTX_init(&mdctx);
EVP_DigestInit(&mdctx, md);
EVP_DigestUpdate(&mdctx, indata, peheader + 88);
EVP_DigestUpdate(&mdctx, indata + peheader + 92, 60 + pe32plus*16);
EVP_DigestUpdate(&mdctx, indata + peheader + 160 + pe32plus*16, hdrsize - (peheader + 160 + pe32plus*16));
EVP_DigestUpdate(&mdctx, zeroes, pagesize - hdrsize);
EVP_MD_CTX *mdctx = EVP_MD_CTX_new();

EVP_DigestInit(mdctx, md);
EVP_DigestUpdate(mdctx, indata, peheader + 88);
EVP_DigestUpdate(mdctx, indata + peheader + 92, 60 + pe32plus*16);
EVP_DigestUpdate(mdctx, indata + peheader + 160 + pe32plus*16, hdrsize - (peheader + 160 + pe32plus*16));
EVP_DigestUpdate(mdctx, zeroes, pagesize - hdrsize);
memset(res, 0, 4);
EVP_DigestFinal(&mdctx, res + 4, NULL);
EVP_DigestFinal(mdctx, res + 4, NULL);

unsigned short sizeofopthdr = GET_UINT16_LE(indata + peheader + 20);
char *sections = indata + peheader + 24 + sizeofopthdr;
Expand All @@ -2040,18 +1958,20 @@ static unsigned char *calc_page_hash(char *indata, unsigned int peheader, int pe
unsigned int l;
for (l=0; l < rs; l+=pagesize, pi++) {
PUT_UINT32_LE(ro + l, res + pi*pphlen);
EVP_DigestInit(&mdctx, md);
EVP_DigestInit(mdctx, md);
if (rs - l < pagesize) {
EVP_DigestUpdate(&mdctx, indata + ro + l, rs - l);
EVP_DigestUpdate(&mdctx, zeroes, pagesize - (rs - l));
EVP_DigestUpdate(mdctx, indata + ro + l, rs - l);
EVP_DigestUpdate(mdctx, zeroes, pagesize - (rs - l));
} else {
EVP_DigestUpdate(&mdctx, indata + ro + l, pagesize);
EVP_DigestUpdate(mdctx, indata + ro + l, pagesize);
}
EVP_DigestFinal(&mdctx, res + pi*pphlen + 4, NULL);
EVP_DigestFinal(mdctx, res + pi*pphlen + 4, NULL);
}
lastpos = ro + rs;
sections += 40;
}
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
PUT_UINT32_LE(lastpos, res + pi*pphlen);
memset(res + pi*pphlen + 4, 0, EVP_MD_size(md));
pi++;
Expand Down Expand Up @@ -2413,7 +2333,7 @@ int main(int argc, char **argv)
int nturl = 0, ntsurl = 0;
int addBlob = 0;
u_char *p = NULL;
int ret = 0, i, len = 0, jp = -1, pe32plus = 0, comm = 0, pagehash = 0;
int ret = 0, i, len = 0, jp = -1, pe32plus = 0, comm = 0;
unsigned int tmp, peheader = 0, padlen = 0;
off_t filesize, fileend, sigfilesize, sigfileend, outdatasize;
file_type_t type;
Expand Down Expand Up @@ -2448,12 +2368,14 @@ int main(int argc, char **argv)
ERR_load_crypto_strings();
OPENSSL_add_all_algorithms_conf();

/* create some MS Authenticode OIDS we need later on */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL) ||
!OBJ_create(SPC_MS_JAVA_SOMETHING, NULL, NULL) ||
!OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL) ||
!OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL))
DO_EXIT_0("Failed to add objects\n");
/* create some MS Authenticode OIDS we have to use via PKCS7 functions later on and thus
* need NIDs. Short and long names are fake, since I do not know the
* proper values but OpenSSL no longer supports NULL for them. */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, "SPC_STATEMENT_TYPE_OBJID", "SPC_STATEMENT_TYPE_OBJID") ||
!OBJ_create(SPC_MS_JAVA_SOMETHING, "SPC_MS_JAVA_SOMETHING", "SPC_MS_JAVA_SOMETHING") ||
!OBJ_create(SPC_SP_OPUS_INFO_OBJID, "SPC_SP_OPUS_INFO_OBJID", "SPC_SP_OPUS_INFO_OBJID") ||
!OBJ_create(SPC_NESTED_SIGNATURE_OBJID, "SPC_NESTED_SIGNATURE_OBJID", "SPC_NESTED_SIGNATURE_OBJID"))
DO_EXIT_0("Failed to add objects\n");

md = EVP_sha1();

Expand Down Expand Up @@ -2531,8 +2453,6 @@ int main(int argc, char **argv)
readpass = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-comm")) {
comm = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-ph")) {
pagehash = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-n")) {
if (--argc < 1) usage(argv0);
desc = *(++argv);
Expand Down Expand Up @@ -3243,7 +3163,7 @@ int main(int argc, char **argv)
p7x = NULL;
}

get_indirect_data_blob(&p, &len, md, type, pagehash, indata, peheader, pe32plus, fileend);
get_indirect_data_blob(&p, &len, md, type, indata, peheader, pe32plus, fileend);
len -= EVP_MD_size(md);
memcpy(buf, p, len);
OPENSSL_free(p);
Expand Down