10 Commits

Author SHA1 Message Date
olszomal bf209e0fc8 Refactor SpcIndirectDataContent parsing helpers 2026-05-26 17:27:46 +02:00
olszomal 68a6826cd1 Use ASN1_STRING and ASN1_INTEGER APIs instead of direct struct access 2026-05-26 17:27:46 +02:00
olszomal ea5d15862d cmake: add documentation generation, GNU install dirs, and uninstall target 2026-05-26 17:27:46 +02:00
olszomal bdde95635f attach-signature flow moved to digest-only verification semantics
attach-signature now uses digest-only verification instead of full
signature validation; output file is not kept if digest verification fails
2026-04-02 17:43:43 +02:00
olszomal d0ef178a9a Remove redundant NULL assignments, CID 1669015 2026-02-25 14:07:58 +01:00
olszomal e126ab3e4a Fix memory leak of compressedData on error paths, CID 1669014 2026-02-25 14:07:58 +01:00
Michał Trojnara a1fb6600fb Fix handling OpenSSL initialization errors 2026-02-11 16:23:57 +01:00
Michał Trojnara 8227c68ceb Do not export osslsigncode.c consts 2026-02-11 15:50:25 +01:00
Michał Trojnara c988b48063 Refactor UI method cleanup 2026-02-11 15:48:17 +01:00
Michał Trojnara 6cf70b4af2 Initial 2.14-dev commit 2026-02-10 23:17:56 +01:00
16 changed files with 1163 additions and 328 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ on:
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
version: osslsigncode-2.13
version: osslsigncode-2.14-dev
jobs:
build:
+25 -5
View File
@@ -10,13 +10,13 @@ set(BUILTIN_SOCKET ON CACHE BOOL "") # for static Python
# configure basic project information
project(osslsigncode
VERSION 2.13
VERSION 2.14
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files"
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
LANGUAGES C)
# force nonstandard version format for development packages
set(DEV "")
set(DEV "-dev")
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}")
# version and contact information
@@ -97,14 +97,25 @@ set_target_properties(osslsigncode PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
# testing with CTest
include(CMakeTest)
# documentation with Pandoc
include(CMakeDoc)
# installation rules for a project
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
install(TARGETS osslsigncode RUNTIME DESTINATION ${BINDIR})
include(GNUInstallDirs)
install(TARGETS osslsigncode RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES
"${PROJECT_SOURCE_DIR}/README.md"
"${PROJECT_SOURCE_DIR}/NEWS.md"
DESTINATION "${CMAKE_INSTALL_DOCDIR}")
if(UNIX)
include(CMakeDist)
else(UNIX)
install(
DIRECTORY ${PROJECT_BINARY_DIR}/ DESTINATION ${BINDIR}
DIRECTORY ${PROJECT_BINARY_DIR}/
DESTINATION ${CMAKE_INSTALL_BINDIR}
FILES_MATCHING
PATTERN "*.dll"
PATTERN "vcpkg_installed" EXCLUDE
@@ -112,6 +123,15 @@ else(UNIX)
PATTERN "Testing" EXCLUDE)
endif(UNIX)
# uninstall target
configure_file(
"${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${PROJECT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake")
#[[
Local Variables:
c-basic-offset: 4
+5
View File
@@ -1,5 +1,10 @@
# osslsigncode change log
### 2.14 (unreleased)
- attach-signature now uses digest-only verification instead of full signature
validation; output file is not kept if digest verification fails
### 2.13 (2026.02.10)
**MULTIPLE SECURITY VULNERABILITIES**
+31 -28
View File
@@ -470,32 +470,33 @@ static int appx_hash_length_get(FILE_FORMAT_CTX *ctx)
*/
static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
{
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
SpcIndirectDataContent *idc;
BIO *hashes;
if (idc) {
BIO *hashes;
if (!appx_extract_hashes(ctx, idc)) {
fprintf(stderr, "Failed to extract hashes from the signature\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
hashes = appx_calculate_hashes(ctx);
if (!hashes) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
BIO_free_all(hashes);
if (!appx_compare_hashes(ctx)) {
fprintf(stderr, "Signature hash verification failed\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
idc = pkcs7_get_indirect_data_content(p7);
if (!idc)
return 1; /* OK - no SpcIndirectDataContent */
if (!appx_extract_hashes(ctx, idc)) {
fprintf(stderr, "Failed to extract hashes from the signature\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
hashes = appx_calculate_hashes(ctx);
if (!hashes) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
BIO_free_all(hashes);
if (!appx_compare_hashes(ctx)) {
fprintf(stderr, "Signature hash verification failed\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
return 1; /* OK */
}
@@ -1077,13 +1078,13 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
AppxSpcSipInfo_free(si);
BIO_free_all(stdbio);
#endif
int length = content->messageDigest->digest->length;
uint8_t *data = content->messageDigest->digest->data;
int len = ASN1_STRING_length(content->messageDigest->digest);
const uint8_t *data = ASN1_STRING_get0_data(content->messageDigest->digest);
int mdlen = EVP_MD_size(ctx->appx_ctx->md);
int pos = 4;
/* we are expecting at least 4 hashes + 4 byte header */
if (length < 4 * mdlen + 4) {
if (len < 4 * mdlen + 4) {
fprintf(stderr, "Hash too short\n");
return 0; /* FAILED */
}
@@ -1091,7 +1092,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
fprintf(stderr, "Hash signature does not match\n");
return 0; /* FAILED */
}
while (pos + mdlen + 4 <= length) {
while (pos + mdlen + 4 <= len) {
if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) {
ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen);
memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen);
@@ -1923,11 +1924,13 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
/* Validate sizes for safe allocation */
if (uncompressedSize > (uint64_t)(SIZE_MAX - 1)) {
fprintf(stderr, "Corrupted uncompressedSize : %" PRIu64"\n", uncompressedSize);
OPENSSL_free(compressedData);
return 0; /* FAILED */
}
/* Detect suspicious compression ratio (zip bomb protection) */
if (uncompressedSize > 1024 * 1024 && uncompressedSize / 100 >= compressedSize) {
fprintf(stderr, "Error: suspicious compression ratio\n");
OPENSSL_free(compressedData);
return 0; /* FAILED */
}
uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
+1 -14
View File
@@ -337,20 +337,7 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
u_char mdbuf[EVP_MAX_MD_SIZE];
u_char *cmdbuf;
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}
+24 -13
View File
@@ -297,27 +297,39 @@ static int cat_add_content_type(PKCS7 *p7, PKCS7 *cursig)
*/
static int cat_sign_content(PKCS7 *p7, PKCS7 *contents)
{
u_char *content;
int seqhdrlen, content_length;
const unsigned char *sequence_data;
const unsigned char *content;
ASN1_STRING *sequence;
int seqhdrlen, sequence_len, content_length;
if (!contents->d.other || !contents->d.other->value.sequence
|| !contents->d.other->value.sequence->data) {
if (!contents->d.other || !contents->d.other->value.sequence) {
fprintf(stderr, "Failed to get content value\n");
return 0; /* FAILED */
}
seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data,
contents->d.other->value.sequence->length);
content = contents->d.other->value.sequence->data + seqhdrlen;
content_length = contents->d.other->value.sequence->length - seqhdrlen;
sequence = contents->d.other->value.sequence;
sequence_data = ASN1_STRING_get0_data(sequence);
sequence_len = ASN1_STRING_length(sequence);
if (!sequence_data) {
fprintf(stderr, "Failed to get content value\n");
return 0; /* FAILED */
}
seqhdrlen = asn1_simple_hdr_len(sequence_data, sequence_len);
content = (const unsigned char *)sequence_data + seqhdrlen;
content_length = sequence_len - seqhdrlen;
if (!pkcs7_sign_content(p7, content, content_length)) {
fprintf(stderr, "Failed to sign content\n");
return 0; /* FAILED */
}
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
fprintf(stderr, "PKCS7_set_content failed\n");
return 0; /* FAILED */
}
return 1; /* OK */
}
@@ -381,23 +393,22 @@ static int cat_print_content_member_digest(ASN1_TYPE *content)
{
SpcIndirectDataContent *idc;
u_char mdbuf[EVP_MAX_MD_SIZE];
const u_char *data ;
int mdtype = -1;
ASN1_STRING *value;
value = content->value.sequence;
data = ASN1_STRING_get0_data(value);
idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value));
idc = asn1_type_get_indirect_data_content(content);
if (!idc)
return 0; /* FAILED */
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
return 1; /* OK */
}
+44
View File
@@ -0,0 +1,44 @@
# documentation with Pandoc
# cmake --build .
find_program(PANDOC pandoc)
if(NOT PANDOC)
message(WARNING "CMakeDoc: pandoc not found, documentation disabled")
return()
endif(NOT PANDOC)
set(DOC_MD "${PROJECT_SOURCE_DIR}/osslsigncode.md")
if(NOT EXISTS "${DOC_MD}")
message(WARNING "CMakeDoc: markdown source not found: ${DOC_MD}")
return()
endif(NOT EXISTS "${DOC_MD}")
set(MAN_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.1")
set(HTML_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.html")
add_custom_command(
OUTPUT "${MAN_PAGE}"
COMMAND "${PANDOC}" -s "${DOC_MD}" -t man -o "${MAN_PAGE}"
DEPENDS "${DOC_MD}"
COMMENT "CMakeDoc: generating man page"
VERBATIM)
add_custom_command(
OUTPUT "${HTML_PAGE}"
COMMAND "${PANDOC}" -s --toc --toc-depth=2 "${DOC_MD}" -t html -o "${HTML_PAGE}"
DEPENDS "${DOC_MD}"
COMMENT "CMakeDoc: generating HTML documentation"
VERBATIM)
add_custom_target(docs ALL DEPENDS "${MAN_PAGE}" "${HTML_PAGE}")
#[[
Local Variables:
c-basic-offset: 4
tab-width: 4
indent-tabs-mode: nil
End:
vim: set ts=4 expandtab:
]]
+2 -14
View File
@@ -196,18 +196,12 @@ if(Python3_FOUND AND NOT cryptography_error)
foreach(format ${formats})
add_test(NAME "attached_${format}_${ext}"
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
# sign options
"-add-msi-dse"
"-h" "sha512"
"-nest"
"-sigin" "${FILES}/${ext}.${format}"
"-in" "${FILES}/signed.${ext}"
"-out" "${FILES}/attached_${format}.${ext}"
# verify options
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
"-CAfile" "${CERTS}/CACert.pem"
"-CRLfile" "${CERTS}/CACertCRL.pem")
"-out" "${FILES}/attached_${format}.${ext}")
set_tests_properties("attached_${format}_${ext}" PROPERTIES
DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}")
list(APPEND ALL_TESTS "attached_${format}_${ext}")
@@ -413,17 +407,11 @@ if(Python3_FOUND AND NOT cryptography_error)
foreach(format ${formats})
add_test(NAME "attached_data_${ext}_${data_format}_${format}"
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
# sign options
"-add-msi-dse"
"-h" "sha384"
"-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}"
"-in" "${FILES}/unsigned.${ext}"
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}"
# verify options
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
"-CAfile" "${CERTS}/CACert.pem"
"-CRLfile" "${CERTS}/CACertCRL.pem")
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}")
set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES
DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}")
list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}")
+24
View File
@@ -0,0 +1,24 @@
# uninstall target
#
# CMake does not provide a built-in uninstall target.
# This target removes files listed in install_manifest.txt,
# generated by the install step.
#
# cmake --build . --target uninstall
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest")
endif()
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
string(REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Removing ${file}")
if(EXISTS "${file}" OR IS_SYMLINK "${file}")
file(REMOVE "${file}")
else()
message(STATUS "File does not exist: ${file}")
endif()
endforeach()
+99 -5
View File
@@ -341,6 +341,91 @@ PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content)
return p7;
}
/*
* Retrieve the message digest and digest algorithm from PKCS7
* SpcIndirectDataContent.
*
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
* [out] mdbuf: message digest buffer, at least EVP_MAX_MD_SIZE bytes
* [out] mdtype: OpenSSL NID of the digest algorithm
* [returns] 0 on error or 1 on success
*/
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype)
{
SpcIndirectDataContent *idc;
if (!mdbuf || !mdtype)
return 0; /* FAILED */
*mdtype = -1;
idc = pkcs7_get_indirect_data_content(p7);
if (!idc) {
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
return 0; /* FAILED */
}
if (spc_indirect_data_content_get_digest(idc, mdbuf, mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
if (*mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}
return 1; /* OK */
}
/*
* Decode SpcIndirectDataContent from a PKCS7 signedData content.
*
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
*
* The caller is responsible for freeing the returned object with
* SpcIndirectDataContent_free().
*/
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7)
{
if (!is_content_type(p7, SPC_INDIRECT_DATA_OBJID))
return NULL;
if (!p7->d.sign || !p7->d.sign->contents || !p7->d.sign->contents->d.other)
return NULL;
return asn1_type_get_indirect_data_content(p7->d.sign->contents->d.other);
}
/*
* Decode SpcIndirectDataContent from an ASN1_TYPE object.
* The ASN1_TYPE is expected to contain a V_ASN1_SEQUENCE value.
*
* [in] content: ASN1_TYPE containing DER-encoded SpcIndirectDataContent
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
*
* The caller is responsible for freeing the returned object with
* SpcIndirectDataContent_free().
*/
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content)
{
ASN1_STRING *value;
const unsigned char *data;
int len;
if (!content || content->type != V_ASN1_SEQUENCE)
return NULL;
value = content->value.sequence;
if (!value)
return NULL;
data = ASN1_STRING_get0_data(value);
len = ASN1_STRING_length(value);
return d2i_SpcIndirectDataContent(NULL, &data, len);
}
/*
* Return spcIndirectDataContent.
* [in] hash: message digest BIO
@@ -575,22 +660,27 @@ int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype)
*/
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype)
{
ASN1_OCTET_STRING *digest_asn1;
const unsigned char *digest_data;
int digest_len;
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
!idc->messageDigest->digestAlgorithm) {
return -1; /* FAILED */
}
digest_len = idc->messageDigest->digest->length;
digest_asn1 = idc->messageDigest->digest;
digest_len = ASN1_STRING_length((ASN1_STRING *)digest_asn1);
/* Validate digest length to prevent buffer overflow */
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
fprintf(stderr, "Invalid digest length in signature: %d (expected 1-%d)\n",
digest_len, EVP_MAX_MD_SIZE);
digest_len, EVP_MAX_MD_SIZE);
return -1; /* FAILED */
}
digest_data = ASN1_STRING_get0_data((ASN1_STRING *)digest_asn1);
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)digest_len);
memcpy(mdbuf, digest_data, (size_t)digest_len);
return digest_len; /* OK */
}
@@ -653,8 +743,12 @@ static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
idc->data->value->value.sequence->data = p;
idc->data->value->value.sequence->length = l;
if (!ASN1_STRING_set(idc->data->value->value.sequence, p, l)) {
OPENSSL_free(p);
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
OPENSSL_free(p);
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
+3
View File
@@ -15,6 +15,9 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx);
int add_indirect_data_object(PKCS7 *p7);
int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content);
PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content);
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype);
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7);
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content);
ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx);
int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len);
int asn1_simple_hdr_len(const u_char *p, int len);
+1 -14
View File
@@ -414,20 +414,7 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const EVP_MD *md;
BIO *hash;
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}
+246 -172
View File
@@ -67,7 +67,7 @@
* 2:d=1 hl=2 l= 2 prim: BIT STRING
* 6:d=1 hl=2 l= 0 cons: SEQUENCE
*/
const u_char java_attrs_low[] = {
static const u_char java_attrs_low[] = {
0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00
};
@@ -76,7 +76,7 @@ const u_char java_attrs_low[] = {
* 0:d=0 hl=2 l= 12 cons: SEQUENCE
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Individual Code Signing
*/
const u_char purpose_ind[] = {
static const u_char purpose_ind[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x15
};
@@ -86,11 +86,13 @@ const u_char purpose_ind[] = {
* 0:d=0 hl=2 l= 12 cons: SEQUENCE
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Commercial Code Signing
*/
const u_char purpose_comm[] = {
static const u_char purpose_comm[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x16
};
static UI_METHOD *ui_method;
/*
* ASN.1 definitions (more or less from official MS Authenticode docs)
*/
@@ -230,7 +232,7 @@ static ASN1_INTEGER *create_nonce(int bits);
static char *clrdp_url_get_x509(X509 *cert);
static time_t time_t_get_asn1_time(const ASN1_TIME *s);
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si);
static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si);
static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si);
static time_t time_t_get_cms_time(CMS_ContentInfo *cms);
static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed,
PKCS7_SIGNER_INFO *countersignature);
@@ -241,6 +243,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7);
static int PKCS7_compare(const PKCS7 *const *a, const PKCS7 *const *b);
static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx);
static STACK_OF(PKCS7) *get_signature_list(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options);
static void print_cert(X509 *cert, int i);
static int x509_store_load_crlfile(X509_STORE *store, char *cafile, char *crlfile);
static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey, STACK_OF(X509) *certs, STACK_OF(X509_CRL) *crls);
@@ -293,7 +296,8 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
TS_REQ *req = NULL;
BIO *bout = NULL, *bhash = NULL;
u_char *p;
int len;
const u_char *digest;
int digest_len, len;
signer_info = PKCS7_get_signer_info(p7);
if (!signer_info)
@@ -303,6 +307,9 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
if (!si)
goto out;
digest = ASN1_STRING_get0_data(si->enc_digest);
digest_len = ASN1_STRING_length(si->enc_digest);
bhash = BIO_new(BIO_f_md());
#if defined(__GNUC__)
#pragma GCC diagnostic push
@@ -316,7 +323,7 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
#pragma GCC diagnostic pop
#endif
BIO_push(bhash, BIO_new(BIO_s_null()));
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length);
BIO_write(bhash, digest, digest_len);
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
req = TS_REQ_new();
@@ -374,6 +381,7 @@ static ASN1_INTEGER *create_nonce(int bits)
{
unsigned char buf[20];
ASN1_INTEGER *nonce = NULL;
BIGNUM *bn = NULL;
int len = (bits - 1) / 8 + 1;
int i;
@@ -388,15 +396,21 @@ static ASN1_INTEGER *create_nonce(int bits)
/* Find the first non-zero byte and creating ASN1_INTEGER object. */
for (i = 0; i < len && !buf[i]; ++i) {
}
nonce = ASN1_INTEGER_new();
bn = BN_bin2bn(buf + i, len - i, NULL);
if (!bn) {
fprintf(stderr, "Could not create nonce BIGNUM\n");
return NULL;
}
nonce = BN_to_ASN1_INTEGER(bn, NULL);
BN_free(bn);
if (!nonce) {
fprintf(stderr, "Could not create nonce\n");
return NULL;
}
OPENSSL_free(nonce->data);
nonce->length = len - i;
nonce->data = OPENSSL_malloc((size_t)nonce->length + 1);
memcpy(nonce->data, buf + i, (size_t)nonce->length);
return nonce;
}
@@ -1621,7 +1635,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
u_char *p, int len, const char *oid)
{
X509_ATTRIBUTE *attr = NULL;
ASN1_OBJECT *object;
const ASN1_OBJECT *object;
char object_txt[128];
if (*unauth_attr == NULL) {
@@ -1632,7 +1646,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
int i;
for (i = 0; i < X509at_get_attr_count(*unauth_attr); i++) {
attr = X509at_get_attr(*unauth_attr, i);
object = X509_ATTRIBUTE_get0_object(attr);
object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
if (object == NULL)
continue;
object_txt[0] = 0x00;
@@ -2196,18 +2210,22 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
/* get the embedded content */
pos = CMS_get0_content(timestamp);
if (pos != NULL && *pos != NULL) {
const u_char *p = (*pos)->data;
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length);
const u_char *p = ASN1_STRING_get0_data(*pos);
int len = ASN1_STRING_length(*pos);
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len);
if (token) {
BIO *bhash;
u_char mdbuf[EVP_MAX_MD_SIZE];
ASN1_OCTET_STRING *hash;
const ASN1_OBJECT *aoid;
int md_nid;
const u_char *hash_data;
int hash_len, md_nid;
const EVP_MD *md;
TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token);
const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint);
const u_char *digest = ASN1_STRING_get0_data(si->enc_digest);
int digest_len = ASN1_STRING_length(si->enc_digest);
X509_ALGOR_get0(&aoid, NULL, NULL, alg);
md_nid = OBJ_obj2nid(aoid);
@@ -2229,17 +2247,19 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
#pragma GCC diagnostic pop
#endif
BIO_push(bhash, BIO_new(BIO_s_null()));
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length);
BIO_write(bhash, digest, digest_len);
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
BIO_free_all(bhash);
/* compare the provided hash against the computed hash */
hash =TS_MSG_IMPRINT_get_msg(msg_imprint);
if (memcmp(mdbuf, hash->data, (size_t)hash->length)) {
hash_data = ASN1_STRING_get0_data(hash);
hash_len = ASN1_STRING_length(hash);
if (memcmp(mdbuf, hash_data, (size_t)hash_len)) {
printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n",
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid));
print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md));
print_hash("\tReceived message digest", "", hash->data, hash->length);
print_hash("\tReceived message digest", "", hash_data, hash_len);
printf("\nFile's message digest verification: failed\n");
TS_TST_INFO_free(token);
return 0; /* FAILED */
@@ -2413,7 +2433,7 @@ static int verify_pkcs7_data(PKCS7 *p7, X509_STORE *store)
&& (contents->d.other->value.sequence->length > 0)) {
if (contents->d.other->type == V_ASN1_SEQUENCE) {
/* only verify the content of the sequence */
const unsigned char *data = contents->d.other->value.sequence->data;
const u_char *data = contents->d.other->value.sequence->data;
long len;
int inf, tag, class;
@@ -2685,11 +2705,12 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
{
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si;
int md_nid, i;
int md_nid, i, len;
STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr;
X509_ATTRIBUTE *attr;
ASN1_OBJECT *object;
ASN1_STRING *value;
const ASN1_OBJECT *object;
const ASN1_STRING *value;
const u_char *data;
char object_txt[128];
time_t time = INVALID_TIME;
@@ -2708,24 +2729,24 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
printf("\nAuthenticated attributes:\n");
for (i=0; i<X509at_get_attr_count(auth_attr); i++) {
attr = X509at_get_attr(auth_attr, i);
object = X509_ATTRIBUTE_get0_object(attr);
object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
if (object == NULL)
continue;
object_txt[0] = 0x00;
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
if (!strcmp(object_txt, PKCS9_MESSAGE_DIGEST)) {
/* PKCS#9 message digest - Policy OID: 1.2.840.113549.1.9.4 */
const u_char *mdbuf;
int len;
ASN1_STRING *digest = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL);
if (digest == NULL)
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL);
if (value == NULL)
continue;
mdbuf = ASN1_STRING_get0_data(digest);
len = ASN1_STRING_length(digest);
print_hash("\tMessage digest", "", mdbuf, len);
data = ASN1_STRING_get0_data(value);
len = ASN1_STRING_length(value);
print_hash("\tMessage digest", "", data, len);
} else if (!strcmp(object_txt, PKCS9_SIGNING_TIME)) {
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
ASN1_UTCTIME *signtime = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
const ASN1_UTCTIME *signtime;
signtime = (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
if (signtime == NULL)
continue;
printf("\tSigning time: ");
@@ -2733,30 +2754,39 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
} else if (!strcmp(object_txt, SPC_SP_OPUS_INFO_OBJID)) {
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.12 */
SpcSpOpusInfo *opus;
const u_char *data;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL)
continue;
data = ASN1_STRING_get0_data(value);
opus = d2i_SpcSpOpusInfo(NULL, &data, ASN1_STRING_length(value));
len = ASN1_STRING_length(value);
opus = d2i_SpcSpOpusInfo(NULL, &data, len);
if (opus == NULL)
continue;
if (opus->moreInfo && opus->moreInfo->type == 0) {
char *url = OPENSSL_strdup((char *)opus->moreInfo->value.url->data);
printf("\tURL description: %s\n", url);
OPENSSL_free(url);
ASN1_IA5STRING *url_asn1 = opus->moreInfo->value.url;
const u_char *url_data = ASN1_STRING_get0_data((ASN1_STRING *)url_asn1);
int url_length = ASN1_STRING_length((ASN1_STRING *)url_asn1);
printf("\tURL description: %.*s\n", url_length, url_data);
}
if (opus->programName) {
char *desc = NULL;
if (opus->programName->type == 0) {
u_char *opusdata;
int len = ASN1_STRING_to_UTF8(&opusdata, opus->programName->value.unicode);
if (len >= 0) {
desc = OPENSSL_strndup((char *)opusdata, (size_t)len);
OPENSSL_free(opusdata);
u_char *opus_data;
int opus_len = ASN1_STRING_to_UTF8(&opus_data, opus->programName->value.unicode);
if (opus_len >= 0) {
desc = OPENSSL_strndup((char *)opus_data, (size_t)opus_len);
OPENSSL_free(opus_data);
}
} else {
desc = OPENSSL_strdup((char *)opus->programName->value.ascii->data);
ASN1_IA5STRING *desc_asn1 = opus->programName->value.ascii;
const u_char *desc_data = ASN1_STRING_get0_data((ASN1_STRING *)desc_asn1);
int desc_len = ASN1_STRING_length((ASN1_STRING *)desc_asn1);
desc = OPENSSL_strndup((const char *)desc_data, (size_t)desc_len);
}
if (desc) {
printf("\tText description: %s\n", desc);
@@ -2766,31 +2796,31 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
SpcSpOpusInfo_free(opus);
} else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) {
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */
const u_char *purpose;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL)
continue;
purpose = ASN1_STRING_get0_data(value);
if (!memcmp(purpose, purpose_comm, sizeof purpose_comm))
data = ASN1_STRING_get0_data(value);
if (!memcmp(data, purpose_comm, sizeof purpose_comm))
printf("\tMicrosoft Commercial Code Signing purpose\n");
else if (!memcmp(purpose, purpose_ind, sizeof purpose_ind))
else if (!memcmp(data, purpose_ind, sizeof purpose_ind))
printf("\tMicrosoft Individual Code Signing purpose\n");
else
printf("\tUnrecognized Code Signing purpose\n");
} else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) {
/* Microsoft OID: 1.3.6.1.4.1.311.15.1 */
const u_char *level;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL)
continue;
level = ASN1_STRING_get0_data(value);
if (!memcmp(level, java_attrs_low, sizeof java_attrs_low))
data = ASN1_STRING_get0_data(value);
if (!memcmp(data, java_attrs_low, sizeof java_attrs_low))
printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
else
printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
} else if (!strcmp(object_txt, PKCS9_SEQUENCE_NUMBER)) {
/* PKCS#9 sequence number - Policy OID: 1.2.840.113549.1.9.25.4 */
ASN1_INTEGER *number = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL);
const ASN1_INTEGER *number;
number = (const ASN1_INTEGER *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL);
if (number == NULL)
continue;
printf("\tSequence number: %ld\n", ASN1_INTEGER_get(number));
@@ -2801,17 +2831,17 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
unauth_attr = PKCS7_get_attributes(si); /* cont[1] */
for (i=0; i<X509at_get_attr_count(unauth_attr); i++) {
attr = X509at_get_attr(unauth_attr, i);
object = X509_ATTRIBUTE_get0_object(attr);
object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
if (object == NULL)
continue;
object_txt[0] = 0x00;
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
if (!strcmp(object_txt, PKCS9_COUNTER_SIGNATURE)) {
/* Authenticode Timestamp - Policy OID: 1.2.840.113549.1.9.6 */
const u_char *data;
CMS_ContentInfo *cms;
PKCS7_SIGNER_INFO *countersi;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL)
continue;
data = ASN1_STRING_get0_data(value);
@@ -2841,9 +2871,9 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
}
} else if (!strcmp(object_txt, SPC_RFC3161_OBJID)) {
/* RFC3161 Timestamp - Policy OID: 1.3.6.1.4.1.311.3.3.1 */
const u_char *data;
CMS_ContentInfo *cms;
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
if (value == NULL)
continue;
data = ASN1_STRING_get0_data(value);
@@ -2869,27 +2899,35 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
}
} else if (!strcmp(object_txt, SPC_UNAUTHENTICATED_DATA_BLOB_OBJID)) {
/* Unauthenticated Data Blob - Policy OID: 1.3.6.1.4.1.42921.1.2.1 */
ASN1_STRING *blob = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL);
if (blob == NULL) {
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL);
if (value == NULL) {
printf("Warning: Unauthenticated Data Blob could not be decoded correctly\n");
continue;
}
len = ASN1_STRING_length(value);
if (verbose) {
char *data_blob = OPENSSL_buf2hexstr(blob->data, blob->length);
char *data_blob;
data = ASN1_STRING_get0_data(value);
data_blob = OPENSSL_buf2hexstr(data, len);
printf("\nUnauthenticated Data Blob:\n%s\n", data_blob);
OPENSSL_free(data_blob);
} else {
printf("\nUnauthenticated Data Blob length: %d bytes\n", blob->length);
printf("\nUnauthenticated Data Blob length: %d bytes\n", len);
}
}
}
/* Signature */
if (verbose) {
data = ASN1_STRING_get0_data(si->enc_digest);
len = ASN1_STRING_length(si->enc_digest);
md_nid = OBJ_obj2nid(si->digest_enc_alg->algorithm);
printf("\nDigest encryption algorithm: %s\n",
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid));
print_hash("Signature", "", ASN1_STRING_get0_data(si->enc_digest), ASN1_STRING_length(si->enc_digest));
print_hash("Signature", "", data, len);
}
return time;
@@ -2925,7 +2963,7 @@ static time_t time_t_get_asn1_time(const ASN1_TIME *s)
*/
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
{
ASN1_UTCTIME *time = asn1_time_get_si_time(si);
const ASN1_UTCTIME *time = asn1_time_get_si_time(si);
if (time == NULL)
return INVALID_TIME; /* FAILED */
@@ -2937,7 +2975,7 @@ static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
* [in] si: PKCS7_SIGNER_INFO structure
* [returns] NULL on error or ASN1_UTCTIME on success
*/
static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
{
STACK_OF(X509_ATTRIBUTE) *auth_attr = PKCS7_get_signed_attributes(si);
if (auth_attr) {
@@ -2947,7 +2985,7 @@ static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
X509_ATTRIBUTE *attr = X509at_get_attr(auth_attr, i);
if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) {
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
return X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
return (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
}
}
}
@@ -2987,10 +3025,13 @@ static time_t time_t_get_cms_time(CMS_ContentInfo *cms)
ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
if (pos != NULL && *pos != NULL) {
const u_char *p = (*pos)->data;
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length);
const u_char *p = ASN1_STRING_get0_data(*pos);
int len = ASN1_STRING_length(*pos);
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len);
if (token) {
const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token);
posix_time = time_t_get_asn1_time(asn1_time);
TS_TST_INFO_free(token);
}
@@ -3237,91 +3278,67 @@ static int verify_signature(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
}
/*
* [in] ctx: structure holds input and output data
* Verifies signatures in a file.
* [in] ctx: file format context (input/output data)
* [in] options: verification options
* [in] verify_signature_flag:
* - non-zero: verify both digest and cryptographic signature
* - zero: verify only digest/content consistency (no signature validation)
* [returns] 1 on error or 0 on success
*/
static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options,
int verify_signature_flag)
{
int i, ret = 1, verified = 0;
PKCS7 *p7;
STACK_OF(PKCS7) *signatures = NULL;
int detached = options->catalog ? 1 : 0;
STACK_OF(PKCS7) *signatures = NULL;
if (detached) {
GLOBAL_OPTIONS *cat_options;
FILE_FORMAT_CTX *cat_ctx;
if (!ctx->format->is_detaching_supported || !ctx->format->is_detaching_supported()) {
fprintf(stderr, "This format does not support detached PKCS#7 signature\n");
return 1; /* FAILED */
}
printf("Checking the specified catalog file\n\n");
cat_options = OPENSSL_memdup(options, sizeof(GLOBAL_OPTIONS));
if (!cat_options) {
fprintf(stderr, "OPENSSL_memdup error.\n");
return 1; /* FAILED */
}
cat_options->infile = options->catalog;
cat_options->cmd = CMD_EXTRACT;
cat_ctx = file_format_cat.ctx_new(cat_options, NULL, NULL);
if (!cat_ctx) {
fprintf(stderr, "CAT file initialization error\n");
return 1; /* FAILED */
}
if (!cat_ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
return 1; /* FAILED */
}
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
cat_ctx->format->ctx_cleanup(cat_ctx);
OPENSSL_free(cat_options);
} else {
if (!ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
return 1; /* FAILED */
}
p7 = ctx->format->pkcs7_extract(ctx);
}
if (!p7) {
fprintf(stderr, "Unable to extract existing signature\n");
signatures = get_signature_list(ctx, options);
if (!signatures)
return 1; /* FAILED */
}
signatures = signature_list_create(p7);
if (!signatures) {
fprintf(stderr, "Failed to create signature list\n\n");
if (!detached && !ctx->format->verify_digests) {
fprintf(stderr, "Unsupported method: verify_digests\n");
sk_PKCS7_pop_free(signatures, PKCS7_free);
return 1; /* FAILED */
}
for (i = 0; i < sk_PKCS7_num(signatures); i++) {
PKCS7 *sig;
int digest_ok = 0;
if (options->index >= 0 && options->index != i) {
printf("Warning: signature verification at index %d was skipped\n", i);
continue;
}
sig = sk_PKCS7_value(signatures, i);
printf("\nSignature Index: %d %s\n\n", i, i == 0 ? " (Primary Signature)" : "");
if (detached) {
if (!verify_content(ctx, sig)) {
ret &= verify_signature(ctx, sig);
} else {
digest_ok = (verify_content(ctx, sig) == 0);
if (!digest_ok)
printf("Catalog verification: failed\n\n");
}
verified++;
} else if (ctx->format->verify_digests) {
printf("\nSignature Index: %d %s\n\n", i, i==0 ? " (Primary Signature)" : "");
if (ctx->format->verify_digests(ctx, sig)) {
} else {
digest_ok = (ctx->format->verify_digests(ctx, sig) != 0);
}
if (digest_ok) {
if (!verify_signature_flag) {
/* success: at least one digest matches */
ret = 0;
} else {
/* success if at least one valid signature is found */
ret &= verify_signature(ctx, sig);
}
verified++;
} else {
fprintf(stderr, "Unsupported method: verify_digests\n");
return 1; /* FAILED */
}
verified++;
}
printf("Number of verified signatures: %d\n", verified);
sk_PKCS7_pop_free(signatures, PKCS7_free);
if (ret)
ERR_print_errors_fp(stderr);
return ret;
}
@@ -3363,11 +3380,11 @@ static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7)
int j;
for (j=0; j<X509_ATTRIBUTE_count(attr); j++) {
ASN1_STRING *value;
const ASN1_STRING *value;
const u_char *data;
PKCS7 *nested;
value = X509_ATTRIBUTE_get0_data(attr, j, V_ASN1_SEQUENCE, NULL);
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, j, V_ASN1_SEQUENCE, NULL);
if (value == NULL)
continue;
data = ASN1_STRING_get0_data(value);
@@ -3498,8 +3515,72 @@ static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx)
}
/*
* Extracts PKCS#7 object and creates a list of signatures
* [in] ctx: file format context
* [in] options: structure holds input parameters
* [returns] STACK_OF(PKCS7) on success or NULL on error
*/
static STACK_OF(PKCS7) *get_signature_list(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
{
PKCS7 *p7;
STACK_OF(PKCS7) *signatures;
if (options->catalog) {
GLOBAL_OPTIONS *cat_options;
FILE_FORMAT_CTX *cat_ctx;
if (!ctx->format->is_detaching_supported || !ctx->format->is_detaching_supported()) {
fprintf(stderr, "This format does not support detached PKCS#7 signature\n");
return NULL;
}
printf("Checking the specified catalog file\n\n");
cat_options = OPENSSL_memdup(options, sizeof(GLOBAL_OPTIONS));
if (!cat_options) {
fprintf(stderr, "OPENSSL_memdup error\n");
return NULL;
}
cat_options->infile = options->catalog;
cat_options->cmd = CMD_EXTRACT;
cat_ctx = file_format_cat.ctx_new(cat_options, NULL, NULL);
if (!cat_ctx) {
fprintf(stderr, "CAT file initialization error\n");
OPENSSL_free(cat_options);
return NULL;
}
if (!cat_ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
cat_ctx->format->ctx_cleanup(cat_ctx);
OPENSSL_free(cat_options);
return NULL;
}
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
cat_ctx->format->ctx_cleanup(cat_ctx);
OPENSSL_free(cat_options);
} else {
if (!ctx->format->pkcs7_extract) {
fprintf(stderr, "Unsupported command: extract-signature\n");
return NULL;
}
p7 = ctx->format->pkcs7_extract(ctx);
}
if (!p7) {
fprintf(stderr, "Unable to extract existing signature\n");
return NULL;
}
signatures = signature_list_create(p7);
if (!signatures) {
fprintf(stderr, "Failed to create signature list\n\n");
PKCS7_free(p7);
return NULL;
}
return signatures;
}
/*
* Verifies integrity of data after attaching a signature.
* [in] options: structure holds the input data
* [returns] 1 on error or 0 on success
* [returns] 0 on success, 1 on error or mismatch
*/
static int check_attached_data(GLOBAL_OPTIONS *options)
{
@@ -3530,7 +3611,8 @@ static int check_attached_data(GLOBAL_OPTIONS *options)
OPENSSL_free(tmp_options);
return 1; /* FAILED */
}
if (verify_signed_file(ctx, tmp_options)) {
/* Check that attached data matches PKCS#7 digest (no signature validation) */
if (verify_signed_file(ctx, tmp_options, 0)) {
fprintf(stderr, "Signature mismatch\n");
ctx->format->ctx_cleanup(ctx);
OPENSSL_free(tmp_options);
@@ -3661,13 +3743,7 @@ static void usage(const char *argv0, const char *cmd)
}
if (on_list(cmd, cmds_attach)) {
printf("%1sattach-signature [ -sigin ] <file>\n", "");
printf("%12s[ -CAfile <file> ]\n", "");
printf("%12s[ -CRLfile <file> ]\n", "");
printf("%12s[ -TSA-CAfile <file> ]\n", "");
printf("%12s[ -TSA-CRLfile <file> ]\n", "");
printf("%12s[ -time <unix-time> ]\n", "");
printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", "");
printf("%12s[ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ]\n", "");
printf("%12s[ -nest ]\n", "");
printf("%12s[ -add-msi-dse ]\n", "");
printf("%12s[ -in ] <file> [ -out ] <file>\n\n", "");
@@ -3718,13 +3794,13 @@ static void help_for(const char *argv0, const char *cmd)
#ifdef PROVIDE_ASKPASS
const char *cmds_askpass[] = {"sign", NULL};
#endif /* PROVIDE_ASKPASS */
const char *cmds_CAfile[] = {"attach-signature", "verify", NULL};
const char *cmds_CAfile[] = {"verify", NULL};
const char *cmds_catalog[] = {"verify", NULL};
const char *cmds_certs[] = {"sign", NULL};
const char *cmds_comm[] = {"sign", NULL};
const char *cmds_CRLfile[] = {"attach-signature", "verify", NULL};
const char *cmds_CRLfile[] = {"verify", NULL};
const char *cmds_CRLfileHTTPS[] = {"add", "sign", "verify", NULL};
const char *cmds_CRLfileTSA[] = {"attach-signature", "verify", NULL};
const char *cmds_CRLfileTSA[] = {"verify", NULL};
const char *cmds_h[] = {"add", "attach-signature", "sign", "extract-data", NULL};
const char *cmds_i[] = {"sign", NULL};
const char *cmds_in[] = {"add", "attach-signature", "extract-signature",
@@ -3758,16 +3834,16 @@ static void help_for(const char *argv0, const char *cmd)
#endif /* OPENSSL_NO_ENGINE */
const char *cmds_pkcs12[] = {"sign", NULL};
const char *cmds_readpass[] = {"sign", NULL};
const char *cmds_require_leaf_hash[] = {"attach-signature", "verify", NULL};
const char *cmds_require_leaf_hash[] = {"verify", NULL};
const char *cmds_sigin[] = {"attach-signature", NULL};
const char *cmds_time[] = {"attach-signature", "sign", "verify", NULL};
const char *cmds_time[] = {"sign", "verify", NULL};
const char *cmds_ignore_timestamp[] = {"verify", NULL};
const char *cmds_ignore_cdp[] = {"verify", NULL};
const char *cmds_ignore_crl[] = {"verify", NULL};
const char *cmds_t[] = {"add", "sign", NULL};
const char *cmds_ts[] = {"add", "sign", NULL};
const char *cmds_CAfileHTTPS[] = {"add", "sign", "verify", NULL};
const char *cmds_CAfileTSA[] = {"attach-signature", "verify", NULL};
const char *cmds_CAfileTSA[] = {"verify", NULL};
const char *cmds_certsTSA[] = {"add", "sign", NULL};
const char *cmds_keyTSA[] = {"add", "sign", NULL};
const char *cmds_timeTSA[] = {"add", "sign", NULL};
@@ -3787,6 +3863,7 @@ static void help_for(const char *argv0, const char *cmd)
printf("%-22s = add an unauthenticated blob or a timestamp to a previously-signed file\n", "add");
printf("%-22s = sign file using a given signature\n", "attach-signature");
printf("%-22s = extract signature from a previously-signed file\n", "extract-signature");
printf("%-22s = extract a data content to be signed\n", "extract-data");
printf("%-22s = remove sections of the embedded signature on a file\n", "remove-signature");
printf("%-22s = digitally sign a file\n", "sign");
printf("%-22s = verifies the digital signature of a file\n\n", "verify");
@@ -3798,8 +3875,6 @@ static void help_for(const char *argv0, const char *cmd)
}
if (on_list(cmd, cmds_attach)) {
printf("\nUse the \"attach-signature\" command to attach the signature stored in the \"sigin\" file.\n");
printf("In order to verify this signature you should specify how to find needed CA or TSA\n");
printf("certificates, if appropriate.\n\n");
printf("Options:\n");
}
if (on_list(cmd, cmds_extract)) {
@@ -4429,19 +4504,15 @@ static int ui_read(UI *ui, UI_STRING *uis)
}
static UI_METHOD *ui_osslsigncode(void) {
static UI_METHOD *ui_method=NULL;
UI_METHOD *ui = UI_create_method("osslsigncode UI");
if (ui_method) /* already initialized */
return ui_method;
ui_method = UI_create_method("osslsigncode UI");
if (!ui_method) {
return NULL;
if (ui) {
UI_method_set_opener(ui, UI_method_get_opener(UI_OpenSSL()));
UI_method_set_writer(ui, UI_method_get_writer(UI_OpenSSL()));
UI_method_set_reader(ui, ui_read);
UI_method_set_closer(ui, UI_method_get_closer(UI_OpenSSL()));
}
UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
UI_method_set_writer(ui_method, UI_method_get_writer(UI_OpenSSL()));
UI_method_set_reader(ui_method, ui_read);
UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
return ui_method;
return ui;
}
/* store_type == 0 means here multiple types of credentials are to be loaded */
@@ -4452,7 +4523,7 @@ static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey
if (!url)
return;
store_ctx = OSSL_STORE_open(url, ui_osslsigncode(), pass, NULL, NULL);
store_ctx = OSSL_STORE_open(url, ui_method, pass, NULL, NULL);
if (!store_ctx)
return;
@@ -4553,7 +4624,6 @@ static void providers_cleanup(void)
{
sk_OSSL_PROVIDER_pop_free(providers, provider_free);
providers = NULL;
UI_destroy_method(ui_osslsigncode());
}
static int provider_load(const char *pname)
@@ -4658,7 +4728,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
if (cmd == CMD_HELP) {
return 0; /* FAILED */
}
if (cmd == CMD_SIGN || cmd == CMD_VERIFY || cmd == CMD_ATTACH) {
if (cmd == CMD_SIGN || cmd == CMD_VERIFY) {
options->cafile = get_cafile();
options->https_cafile = get_cafile();
options->tsa_cafile = get_cafile();
@@ -4823,7 +4893,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */
}
options->url = *(++argv);
} else if ((cmd == CMD_ATTACH || cmd == CMD_SIGN || cmd == CMD_VERIFY)
} else if ((cmd == CMD_SIGN || cmd == CMD_VERIFY)
&& (!strcmp(*argv, "-time") || !strcmp(*argv, "-st"))) {
if (--argc < 1) {
usage(argv0, "all");
@@ -4892,14 +4962,14 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */
}
options->catalog = *(++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-CAfile")) {
} else if (cmd == CMD_VERIFY && !strcmp(*argv, "-CAfile")) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
}
OPENSSL_free(options->cafile);
options->cafile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-CRLfile")) {
} else if (cmd == CMD_VERIFY && !strcmp(*argv, "-CRLfile")) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
@@ -4920,20 +4990,20 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */
}
options->https_crlfile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) {
} else if (cmd == CMD_VERIFY && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
}
OPENSSL_free(options->tsa_cafile);
options->tsa_cafile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) {
} else if (cmd == CMD_VERIFY && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
}
options->tsa_crlfile = OPENSSL_strdup(*++argv);
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-require-leaf-hash")) {
} else if (cmd == CMD_VERIFY && !strcmp(*argv, "-require-leaf-hash")) {
if (--argc < 1) {
usage(argv0, "all");
return 0; /* FAILED */
@@ -5050,7 +5120,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
return 0; /* FAILED */
}
#ifndef WIN32
if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && access(options->cafile, R_OK)) {
if (cmd == CMD_VERIFY && access(options->cafile, R_OK)) {
printf("Use the \"-CAfile\" option to add one or more trusted CA certificates to verify the signature.\n");
return 0; /* FAILED */
}
@@ -5144,7 +5214,7 @@ static int main_execute(int argc, char **argv)
DO_EXIT_0("Initialization error or unsupported input file type.\n");
}
if (options.cmd == CMD_VERIFY) {
ret = verify_signed_file(ctx, &options);
ret = verify_signed_file(ctx, &options, 1);
goto skip_signing;
} else if (options.cmd == CMD_EXTRACT_DATA) {
if (!ctx->format->pkcs7_contents_get) {
@@ -5291,13 +5361,15 @@ skip_signing:
}
if (!ret && options.cmd == CMD_ATTACH) {
ret = check_attached_data(&options);
if (!ret)
if (!ret) {
printf("Signature successfully attached\n");
/* else
* the new PKCS#7 signature has been successfully appended to the outfile
* but only its verification failed (incorrect verification parameters?)
* so the output file is not deleted
*/
} else {
/*
* The output file was created, but its content does not match
* the attached PKCS#7 digest, so it is treated as invalid and removed.
*/
remove_file(options.outfile);
}
}
err_cleanup:
@@ -5360,7 +5432,9 @@ int main(int argc, char **argv)
DO_EXIT_0("Failed to create objects\n");
/* perform the requested operation */
ui_method = ui_osslsigncode();
ret = main_execute(argc, argv);
UI_destroy_method(ui_method);
err_cleanup:
return ret;
+554
View File
@@ -0,0 +1,554 @@
---
title: osslsigncode
lang: en-US
---
# NAME
osslsigncode - Authenticode signing, timestamping, extraction, attachment, removal, and verification tool
# SYNOPSIS
`osslsigncode` [`--help`] [`--version`]
`osslsigncode` `sign`
[`-certs` *file* | `-spc` *file* | `-pkcs12` *file*]
[`-key` *file-or-URI*]
[`-ac` *file*]
[`-pass` *password* | `-readpass` *file* | `-askpass`]
[`-pkcs11module` *module*] [`-pkcs11cert` *URI*]
[`-engine` *engine*] [`-provider` *provider*]
[`-login`] [`-engineCtrl` *command*[:*parameter*]]
[`-h` *digest*]
[`-n` *description*] [`-i` *URL*]
[`-jp` `low`] [`-comm`] [`-ph`]
[`-t` *URL* ... | `-ts` *URL* ...]
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
[`-time` *unix-time*]
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
[`-nest`] [`-add-msi-dse`] [`-verbose`] [`-pem`]
`-in` *input* `-out` *output*
`osslsigncode` `extract-data`
[`-pem`] [`-h` *digest*] [`-ph`] [`-add-msi-dse`]
`-in` *input* `-out` *output*
`osslsigncode` `add`
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
[`-t` *URL* ... | `-ts` *URL* ...]
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
[`-h` *digest*] [`-index` *n*] [`-verbose`] [`-add-msi-dse`]
`-in` *input* `-out` *output*
`osslsigncode` `attach-signature`
`-sigin` *signature*
[`-h` *digest*] [`-nest`] [`-add-msi-dse`]
`-in` *input* `-out` *output*
`osslsigncode` `extract-signature`
[`-pem`]
`-in` *input* `-out` *output*
`osslsigncode` `remove-signature`
`-in` *input* `-out` *output*
`osslsigncode` `verify`
`-in` *input*
[`-c` | `-catalog` *catalog-file*]
[`-CAfile` *file*] [`-CRLfile` *file*]
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
[`-TSA-CAfile` *file*] [`-TSA-CRLfile` *file*]
[`-p` *proxy*] [`-index` *n*]
[`-ignore-timestamp`] [`-ignore-cdp`] [`-ignore-crl`]
[`-time` *unix-time*]
[`-require-leaf-hash` *alg*:*hex*]
[`-verbose`]
# DESCRIPTION
`osslsigncode` signs and verifies Microsoft Authenticode signatures on
supported file formats. It can also extract data for detached signing,
attach an externally produced signature, add timestamps or unauthenticated
blobs to an existing signature, and remove an embedded signature.
Supported input formats include PE files such as EXE, DLL, and SYS, CAB,
CAT, MSI, APPX, and several script file types, including `.ps1`, `.ps1xml`,
`.psc1`, `.psd1`, `.psm1`, `.cdxml`, `.mof`, and `.js`.
The program supports these common workflows:
- direct signing of an unsigned file
- detached signing via `extract-data`, `sign`, and `attach-signature`
- post-sign timestamping with `add`
- verification of embedded signatures or catalog signatures with `verify`
If no subcommand is given, `sign` is assumed.
# FORMATS
Support is not identical across all file formats.
In particular, detached-signature workflows, nested signatures, catalog-based
verification, and signature removal are format-dependent features. A command
that is valid for one supported file type may be unsupported for another.
CAT files are a special case. They are detached catalog containers for
hashes of other files, not ordinary embedded-signature payloads. A CAT
file is itself a PKCS#7 structure containing authenticated entries for one
or more external files. In practice, the catalog signs file digests
recorded in the catalog, rather than embedding a signature into each
covered file.
Because of this, CAT files behave differently from embedded-signature
formats. They do not support `attach-signature`, `remove-signature`,
`extract-data`, or nested signatures.
MSI files are also a special case. They support an extended signature mode
controlled by `-add-msi-dse`. In this mode, the MSI signature covers file
metadata as well as file content. Detached-signing workflows and any later
re-signing or nesting operations must use a mode consistent with the MSI
file's existing signature structure.
# COMMANDS
## `sign`
Create a new Authenticode signature.
This command can sign a normal unsigned file, or it can sign PKCS#7 data
previously produced by `extract-data`.
## `extract-data`
Extract the PKCS#7 content to be signed later. This is used for detached
signing workflows.
## `add`
Add unauthenticated attributes to an existing signature, typically an
Authenticode timestamp, an RFC 3161 timestamp, or an unauthenticated blob.
With `-index`, the selected signature in a multi-signature file is updated.
## `attach-signature`
Attach a detached PKCS#7 signature to an input file.
With `-nest`, the new signature is attached as a nested signature instead of
replacing the primary one, if the file format supports nested signatures.
## `extract-signature`
Extract the embedded PKCS#7 signature from a signed file.
## `remove-signature`
Remove the embedded signature from a signed file.
## `verify`
Verify an embedded signature or a catalog signature.
Verification may include digest consistency, certificate chain validation,
certificate revocation checking, timestamp validation, and optional checking
of the signer's leaf certificate hash.
When verifying that a file is covered by a catalog, use `verify -catalog
catalog.cat -in file`. Verifying the CAT file by itself validates the
catalog signature; verifying with `-catalog` checks whether the specified
input file is covered by that catalog.
# OPTIONS
Some options are available only in particular builds or OpenSSL versions.
In particular, `-askpass` is build-dependent, `-provider` and `-nolegacy`
require OpenSSL 3, and engine-related options depend on engine support in the
build.
## General options
`--help`
: Show help text. With a subcommand, show help for that subcommand.
`-v`, `--version`
: Show version information.
`-in` *file*
: Input file.
`-out` *file*
: Output file. Required for all commands except `verify`.
`-verbose`
: Produce more detailed diagnostic output.
## Signing material
`-pkcs12` *file*
: Read the signing certificate and private key from a PKCS#12 container.
`-certs`, `-spc` *file*
: Read the signing certificate chain. The historical alias `-spc` is accepted.
`-key` *file-or-URI*
: Read the private key. This may also be a store or PKCS#11 URI.
`-ac` *file*
: Add extra certificates to the signature block.
`-pass` *password*
: Password or PIN for the key, token, or PKCS#12 container.
`-readpass` *file*
: Read the password or PIN from *file*. Use `-` to read from standard input.
`-askpass`
: Prompt for the password interactively.
## PKCS#11, engines, and providers
`-pkcs11module` *module*
: Path to a PKCS#11 module.
`-pkcs11cert` *URI*
: PKCS#11 URI identifying the certificate object.
`-provider` *provider*
: OpenSSL 3 provider to load. This is the preferred modern interface for
provider-based PKCS#11 use.
`-engine`, `-pkcs11engine` *engine*
: OpenSSL engine identifier or path to a dynamic engine module. This
interface is retained for compatibility with builds and deployments that
still support engines.
`-login`
: Force login to the token for engine-based PKCS#11 use.
`-engineCtrl` *command*[:*parameter*]
: Pass a control command to the selected engine.
`-nolegacy`
: On OpenSSL 3 builds, do not automatically load the legacy provider.
## Signature contents and digest control
`-h` `md5` | `sha1` | `sha2` | `sha256` | `sha384` | `sha512`
: Select the digest algorithm. The default is `sha256`. `sha2` and
`sha256` are equivalent.
`-n` *description*
: Description of the signed content.
`-i` *URL*
: Informational URL associated with the signed content.
`-comm`
: Use Microsoft Commercial Code Signing purpose instead of the default
individual purpose.
`-jp` `low`
: Add the Java CAB permission attribute. Only `low` is currently supported.
`-ph`
: Generate page hashes for executable files.
`-add-msi-dse`
: For MSI files, enable the `MsiDigitalSignatureEx` signing mode. In this
mode, the signature covers MSI metadata as well as file content. The
metadata portion includes stream names, sizes, and selected timestamps in
the MSI structure. This option changes the MSI signature format and should
be used consistently in any detached-signing workflow involving
`extract-data`, `sign`, `attach-signature`, or `add`.
For a newly signed MSI, this mode is generally preferred because it extends
signing coverage beyond file content alone. For an already signed MSI,
however, the chosen mode must match the file's existing signature
structure. Switching between basic MSI signing and `MsiDigitalSignatureEx`
during re-signing or nested-signature operations can invalidate the
existing signature.
`-pem`
: Write PKCS#7 output in PEM format instead of DER.
## Timestamping and network options
The following timestamping modes are **mutually exclusive** within a single
`sign` or `add` invocation:
- Authenticode timestamping with `-t`
- RFC 3161 timestamping with `-ts`
- built-in RFC 3161 timestamp generation with `-TSA-certs` and `-TSA-key`
`-t` *URL*
: Add an Authenticode timestamp from the specified URL. May be repeated.
`-ts` *URL*
: Add an RFC 3161 timestamp from the specified URL. May be repeated.
`-p` *proxy*
: Proxy used for timestamp or CRL retrieval.
`-noverifypeer`
: Do not verify the TLS certificate of the remote timestamp service.
`-HTTPS-CAfile` *file*
: PEM bundle used to verify HTTPS peers contacted by `osslsigncode`.
`-HTTPS-CRLfile` *file*
: PEM CRL file used while verifying HTTPS peers.
`-TSA-certs` *file*
: PEM certificate chain for locally generated RFC 3161 timestamps.
`-TSA-key` *file-or-URI*
: Private key for locally generated RFC 3161 timestamps.
`-TSA-time` *unix-time*
: Timestamp time for locally generated RFC 3161 responses.
## Nested signatures and indexed operations
`-nest`
: Add a nested signature instead of replacing the primary signature.
`-index` *n*
: Select a signature by index for `add` or `verify`. Index 0 is the primary
signature.
## Unauthenticated blob options
`-addUnauthenticatedBlob`
: Add an unauthenticated blob to the signature.
`-blobFile` *file*
: Read blob contents from *file*. If omitted, a placeholder blob is created.
## Verification options
`-c`, `-catalog` *file*
: Verify the input file against the specified catalog file.
`-CAfile` *file*
: PEM bundle of trusted CA certificates for signer validation.
`-CRLfile` *file*
: PEM file containing CRLs for signer validation.
`-TSA-CAfile`, `-untrusted` *file*
: PEM bundle of trusted CA certificates for timestamp validation.
`-TSA-CRLfile`, `-CRLuntrusted` *file*
: PEM file containing CRLs for timestamp validation.
`-time`, `-st` *unix-time*
: Verification time. If a valid timestamp is present and used, chain
validation is normally performed at the timestamp time.
`-ignore-timestamp`
: Skip verification of the timestamp signature.
`-ignore-cdp`
: Do not fetch CRLs from CRL Distribution Points.
`-ignore-crl`
: Disable CRL retrieval and CRL validation.
`-require-leaf-hash` *alg*:*hex*
: Require the signer's leaf certificate to hash to the specified value.
The hash is computed over the DER encoding of the leaf certificate.
# EXIT STATUS
`0`
: Success.
non-zero
: Failure.
# DIAGNOSTICS
Common causes of failure include:
missing CA trust bundle
: On Unix-like systems, `verify` expects a readable CA bundle, either from
`-CAfile` or from a detected system default.
detached-signing mismatch
: `extract-data`, `sign`, and `attach-signature` must use compatible
digest-affecting options such as `-h`, and where relevant `-ph` and
`-add-msi-dse`.
unsupported format feature
: Some file formats do not support every subcommand or every signature mode.
missing TSA trust chain
: Timestamp verification may fail unless the appropriate TSA trust anchors
are supplied with `-TSA-CAfile`, and where needed `-TSA-CRLfile`.
conflicting timestamp modes
: `-t`, `-ts`, and built-in TSA signing cannot be combined in one command.
MSI signature mode mismatch
: Re-signing or nesting an MSI signature must be consistent with whether the
file already uses `MsiDigitalSignatureEx`. Mixing modes may invalidate the
existing signature.
# ENVIRONMENT
`HTTP_PROXY`, `http_proxy`
: Default proxy for HTTP access if `-p` is not given.
`HTTPS_PROXY`, `https_proxy`
: Default proxy for HTTPS access if `-p` is not given.
`OPENSSL_ENGINES`
: May help OpenSSL find engine modules.
# FILES
On Unix-like systems, `osslsigncode` tries common CA bundle locations for
its default `-CAfile`, including:
- `/etc/ssl/certs/ca-certificates.crt`
- `/etc/pki/tls/certs/ca-bundle.crt`
- `/usr/share/ssl/certs/ca-bundle.crt`
- `/usr/local/share/certs/ca-root-nss.crt`
- `/etc/ssl/cert.pem`
If no readable CA bundle is available, `verify` may require an explicit
`-CAfile`.
# NOTES
Use `extract-data` when you need to create a new detached signature object.
Use `extract-signature` when you need to copy an existing embedded PKCS#7
signature out of a file.
For safer secret handling, prefer `-readpass` or `-askpass` over `-pass`.
Data added with `-addUnauthenticatedBlob` is not protected by the signature
and must not be treated as trusted.
For new MSI signatures, `-add-msi-dse` is generally preferred because it
extends signing coverage to MSI metadata as well as file content. However,
it is format-affecting rather than cosmetic, so existing signed MSI files
should be re-signed only in a mode consistent with their current signature
structure.
Output files are not overwritten.
# EXAMPLES
## Sign and verify a file
```sh
osslsigncode sign \
-pkcs12 signer.p12 \
-readpass p12-pass.txt \
-n "Example Application" \
-i "https://example.com/" \
-ts "https://tsa.example.net/" \
-in app.exe \
-out app-signed.exe
osslsigncode verify \
-CAfile ca-bundle.pem \
-TSA-CAfile tsa-ca-bundle.pem \
-in app-signed.exe
```
## Detached signing workflow
```sh
osslsigncode extract-data \
-h sha384 \
-ph \
-in app.exe \
-out app-data.der
osslsigncode sign \
-pkcs12 signer.p12 \
-readpass p12-pass.txt \
-h sha384 \
-in app-data.der \
-out app-sig.der
osslsigncode attach-signature \
-h sha384 \
-sigin app-sig.der \
-in app.exe \
-out app-signed.exe
osslsigncode verify \
-CAfile ca-bundle.pem \
-in app-signed.exe
```
## Sign a new MSI with extended MSI metadata coverage
```sh
osslsigncode sign \
-pkcs12 signer.p12 \
-readpass p12-pass.txt \
-add-msi-dse \
-in installer.msi \
-out installer-signed.msi
```
## Use a PKCS#11 provider
```sh
osslsigncode sign \
-provider /path/to/pkcs11prov.so \
-pkcs11module /path/to/opensc-pkcs11.so \
-pkcs11cert 'pkcs11:token=my-token;object=cert' \
-key 'pkcs11:token=my-token;object=key' \
-readpass pin.txt \
-in app.exe \
-out app-signed.exe
```
## Add a timestamp to an already signed file
```sh
osslsigncode add \
-ts "https://tsa.example.net/" \
-in app-signed.exe \
-out app-signed-ts.exe
```
## Verify that a file is covered by a catalog
```sh
osslsigncode verify \
-catalog drivers.cat \
-CAfile ca-bundle.pem \
-CRLfile ca-crl.pem \
-in driver.sys
```
# REPORTING BUGS
Report bugs and suspected issues via the project issue tracker:
<https://github.com/mtrojnar/osslsigncode/issues>
# AUTHORS
Originally written by Per Allansson.
Maintained and extended by Michał Trojnara.
Major contributions by Małgorzata Olszówka.
Additional contributions by other project contributors.
# SEE ALSO
**OpenSSL** Library
<https://openssl-library.org/>
+102 -47
View File
@@ -87,6 +87,7 @@ static uint32_t pe_calc_checksum(BIO *bio, uint32_t header_size);
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx);
static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype);
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj);
static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype);
static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype);
@@ -247,51 +248,34 @@ static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
u_char *cmdbuf = NULL;
u_char *ph = NULL;
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (!pe_page_hash_get(&ph, &phlen, &phtype, idc->data)) {
fprintf(stderr, "Failed to extract a page hash\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
OPENSSL_free(ph);
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
fprintf(stderr, "Failed to extract current message digest\n\n");
OPENSSL_free(ph);
return 0; /* FAILED */
}
md = EVP_get_digestbynid(mdtype);
cmdbuf = pe_digest_calc(ctx, md);
if (!cmdbuf) {
fprintf(stderr, "Failed to calculate message digest\n\n");
OPENSSL_free(ph);
return 0; /* FAILED */
}
if (!compare_digests(mdbuf, cmdbuf, mdtype)) {
fprintf(stderr, "Signature verification: failed\n\n");
OPENSSL_free(ph);
OPENSSL_free(cmdbuf);
return 0; /* FAILED */
}
OPENSSL_free(cmdbuf);
if (!pkcs7_get_page_hash(p7, &ph, &phlen, &phtype)) {
fprintf(stderr, "Failed to extract page hash\n\n");
return 0; /* FAILED */
}
if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) {
fprintf(stderr, "Signature verification: failed\n\n");
OPENSSL_free(ph);
OPENSSL_free(cmdbuf);
return 0; /* FAILED */
}
OPENSSL_free(ph);
OPENSSL_free(cmdbuf);
return 1; /* OK */
}
@@ -839,6 +823,36 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
* Page hash support
*/
/*
* Retrieve a page hash from PKCS7 SPC_INDIRECT_DATA structure.
* [in] p7: PKCS7 signature
* [out] ph: page hash
* [out] phlen: page hash length
* [out] phtype: NID_sha1 or NID_sha256
* [returns] 0 on error or 1 on success
*/
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype)
{
SpcIndirectDataContent *idc = pkcs7_get_indirect_data_content(p7);
if (!idc) {
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
return 0; /* FAILED */
}
if (!idc->data) {
fprintf(stderr, "Missing SpcIndirectDataContent data\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
if (!pe_page_hash_get(ph, phlen, phtype, idc->data)) {
fprintf(stderr, "Failed to extract a page hash\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
return 1; /* OK */
}
/*
* Retrieve a page hash from SPC_INDIRECT_DATA structure.
* [out] ph: page hash
@@ -847,43 +861,71 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
* [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash
* [returns] 0 on error or 1 on success
*/
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj)
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype,
SpcAttributeTypeAndOptionalValue *obj)
{
const u_char *blob;
const unsigned char *blob;
const unsigned char *sequence_data;
const unsigned char *classid_data;
const unsigned char *serialized_data;
SpcPeImageData *id;
SpcSerializedObject *so;
int l, l2;
int sequence_len, classid_len, serialized_len, l, l2;
char buf[128];
/* Validate input object */
if (!obj || !obj->value)
return 0; /* FAILED */
blob = obj->value->value.sequence->data;
id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length);
if (!id) {
/* Decode SpcPeImageData from ASN.1 sequence */
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
/* d2i_* modifies the input pointer, so use a temporary variable */
blob = sequence_data;
id = d2i_SpcPeImageData(NULL, &blob, sequence_len);
if (!id)
return 0; /* FAILED */
}
/* Validate SpcPeImageData contents */
if (!id->file) {
SpcPeImageData_free(id);
return 0; /* FAILED */
}
/* Type 1 means SpcSerializedObject */
if (id->file->type != 1) {
SpcPeImageData_free(id);
return 1; /* OK - This is not SpcSerializedObject structure that contains page hashes */
return 1; /* OK - no page hashes present */
}
so = id->file->value.moniker;
if (so->classId->length != sizeof classid_page_hash ||
memcmp(so->classId->data, classid_page_hash, sizeof classid_page_hash)) {
/* Validate serialized object class ID */
classid_data = ASN1_STRING_get0_data((ASN1_STRING *)so->classId);
classid_len = ASN1_STRING_length((ASN1_STRING *)so->classId);
if (classid_len != sizeof classid_page_hash ||
memcmp(classid_data, classid_page_hash, sizeof classid_page_hash)) {
SpcPeImageData_free(id);
return 0; /* FAILED */
}
/* skip ASN.1 SET hdr */
l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length);
blob = so->serializedData->data + l;
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l);
/*Get serialized ASN.1 blob */
serialized_data = ASN1_STRING_get0_data((ASN1_STRING *)so->serializedData);
serialized_len = ASN1_STRING_length((ASN1_STRING *)so->serializedData);
/* Skip ASN.1 SET header */
l = asn1_simple_hdr_len(serialized_data, serialized_len);
blob = serialized_data + l;
/* Decode nested SpcAttributeTypeAndOptionalValue */
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, serialized_len - l);
SpcPeImageData_free(id);
if (!obj)
return 0; /* FAILED */
/* Determine page hash algorithm */
*phtype = 0;
buf[0] = 0x00;
OBJ_obj2txt(buf, sizeof buf, obj->type, 1);
@@ -895,15 +937,30 @@ static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTy
SpcAttributeTypeAndOptionalValue_free(obj);
return 0; /* FAILED */
}
/* Skip ASN.1 SET hdr */
l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length);
/* Skip ASN.1 OCTET STRING hdr */
l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2);
/* IMPORTANT:
* obj now points to the newly decoded structure,
* so refresh sequence_data/sequence_len */
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
/* Skip ASN.1 SET header */
l2 = asn1_simple_hdr_len(sequence_data, sequence_len);
/* Skip ASN.1 OCTET STRING header */
l = asn1_simple_hdr_len(sequence_data + l2, sequence_len - l2);
l += l2;
*phlen = obj->value->value.sequence->length - l;
/* Extract raw page hash blob */
*phlen = sequence_len - l;
*ph = OPENSSL_malloc((size_t)*phlen);
memcpy(*ph, obj->value->value.sequence->data + l, (size_t)*phlen);
if (!*ph) {
SpcAttributeTypeAndOptionalValue_free(obj);
return 0; /* FAILED */
}
memcpy(*ph, sequence_data + l, (size_t)*phlen);
SpcAttributeTypeAndOptionalValue_free(obj);
return 1; /* OK */
}
@@ -1085,7 +1142,6 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
return NULL; /* FAILED */
}
BIO_free_all(bhash);
bhash = NULL;
for (i = 0; i < (int)nsections; i++) {
/* SizeOfRawData and PointerToRawData from section header */
@@ -1177,7 +1233,6 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
return NULL; /* FAILED */
}
BIO_free_all(bhash);
bhash = NULL;
}
lastpos = ro + rs;
sections += 40;
+1 -15
View File
@@ -288,21 +288,7 @@ static int script_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const EVP_MD *md;
BIO *bhash;
/* FIXME: this shared code most likely belongs in osslsigncode.c */
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) {
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
SpcIndirectDataContent_free(idc);
}
}
if (mdtype == -1) {
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}