mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1059208 - Add scripts for signing manifest files of Trusted Hosted Apps r=dkeeler
This commit is contained in:
parent
b743822978
commit
3b04dbe2e6
@ -4,6 +4,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
GEN_CERT_HEADER = $(srcdir)/gen_cert_header.py
|
||||
TEST_SSL_PATH = $(srcdir)/../manager/ssl/tests/unit/test_signed_manifest/
|
||||
|
||||
marketplace-prod-public.inc: marketplace-prod-public.crt $(GEN_CERT_HEADER)
|
||||
$(PYTHON) $(GEN_CERT_HEADER) marketplaceProdPublicRoot $< > $@
|
||||
@ -20,6 +21,18 @@ marketplace-dev-reviewers.inc: marketplace-dev-reviewers.crt $(GEN_CERT_HEADER)
|
||||
marketplace-stage.inc: marketplace-stage.crt $(GEN_CERT_HEADER)
|
||||
$(PYTHON) $(GEN_CERT_HEADER) marketplaceStageRoot $< > $@
|
||||
|
||||
ifeq ($(shell test -s trusted-app-public.der; echo $$?),0)
|
||||
TRUSTED_APP_PUBLIC=trusted-app-public.der
|
||||
else
|
||||
TRUSTED_APP_PUBLIC=
|
||||
endif
|
||||
|
||||
manifest-signing-root.inc: $(TRUSTED_APP_PUBLIC) $(GEN_CERT_HEADER)
|
||||
$(PYTHON) $(GEN_CERT_HEADER) trustedAppPublicRoot $(TRUSTED_APP_PUBLIC) > $@
|
||||
|
||||
manifest-signing-test-root.inc: $(TEST_SSL_PATH)trusted_ca1.der $(GEN_CERT_HEADER)
|
||||
$(PYTHON) $(GEN_CERT_HEADER) trustedAppTestRoot $< > $@
|
||||
|
||||
xpcshell.inc: $(srcdir)/../manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der $(GEN_CERT_HEADER)
|
||||
$(PYTHON) $(GEN_CERT_HEADER) xpcshellRoot $< > $@
|
||||
|
||||
@ -29,5 +42,7 @@ export:: \
|
||||
marketplace-dev-public.inc \
|
||||
marketplace-dev-reviewers.inc \
|
||||
marketplace-stage.inc \
|
||||
manifest-signing-root.inc \
|
||||
manifest-signing-test-root.inc \
|
||||
xpcshell.inc \
|
||||
$(NULL)
|
||||
|
@ -22,8 +22,18 @@ def create_header(array_name, in_filename):
|
||||
print "};"
|
||||
return 0
|
||||
|
||||
def create_empty_header(array_name):
|
||||
# mfbt/ArrayUtils.h will not be able to pick up the
|
||||
# correct specialization for ArrayLength(const array[0])
|
||||
# so add a value of 0 which will fail cert verification
|
||||
# just the same as an empty array
|
||||
print "const uint8_t " + array_name + "[] = { 0x0 };"
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
if len(sys.argv) < 2:
|
||||
print 'ERROR: usage: gen_cert_header.py array_name in_filename'
|
||||
sys.exit(1);
|
||||
if len(sys.argv) == 2:
|
||||
sys.exit(create_empty_header(sys.argv[1]))
|
||||
sys.exit(create_header(sys.argv[1], sys.argv[2]))
|
||||
|
@ -0,0 +1,17 @@
|
||||
This folder contains the scripts needed to generate signed manifest files
|
||||
to verify the Trusted Hosted Apps concept.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
* NSS 3.4 or higher.
|
||||
* Python 2.7 (should work with 2.6 also)
|
||||
* Bash
|
||||
* OpenSSL
|
||||
|
||||
Usage:
|
||||
|
||||
Run
|
||||
I) For usage info execute ./create_test_files.sh --help
|
||||
|
||||
II) Upload the signed manifest.webapp and manifest.sig to the
|
||||
application hosting server.
|
181
security/manager/ssl/tests/unit/test_signed_manifest/create_test_files.sh
Executable file
181
security/manager/ssl/tests/unit/test_signed_manifest/create_test_files.sh
Executable file
@ -0,0 +1,181 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Mode: shell-script; sh-indentation:2;
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
export BASE_PATH=`dirname $0`
|
||||
echo $BASE_PATH
|
||||
|
||||
# location of the 'sign_b2g_manifest.py' script
|
||||
export SIGN_SCR_PATH=.
|
||||
|
||||
DB_PATH=${BASE_PATH}/signingDB
|
||||
PASSWORD_FILE=${DB_PATH}/passwordfile
|
||||
VALID_MANIFEST_PATH=${BASE_PATH}/testValidSignedManifest
|
||||
INVALID_MANIFEST_PATH=${BASE_PATH}/testInvalidSignedManifest
|
||||
|
||||
TRUSTED_EE=trusted_ee1
|
||||
UNTRUSTED_EE=untrusted_ee1
|
||||
TRUSTED_CA=trusted_ca1
|
||||
UNTRUSTED_CA=untrusted_ca1
|
||||
|
||||
# Print usage info
|
||||
usage() {
|
||||
echo
|
||||
echo
|
||||
tput bold
|
||||
echo "NAME"
|
||||
tput sgr0
|
||||
echo " create_test_files.sh - Signing a manifest for Trusted Hosted Apps."
|
||||
echo
|
||||
tput bold
|
||||
echo "SYNOPSIS"
|
||||
tput sgr0
|
||||
echo " create_test_files.sh"
|
||||
echo " create_test_files.sh [--regenerate-test-certs]"
|
||||
echo " create_test_files.sh [--help]"
|
||||
echo
|
||||
tput bold
|
||||
echo "DESCRIPTION"
|
||||
tput sgr0
|
||||
echo " The script signs a manifest for Trusted Hosted Apps if no parameter"
|
||||
echo " is given and if the manifest file and a certificate database directory"
|
||||
echo " is present in the current directory."
|
||||
echo " Two directories ./testValidSignedManifest and ./testInvalidSignedManifest"
|
||||
echo " are generated containing a manifest signature file each, signed with valid"
|
||||
echo " and invalid certificates respectively."
|
||||
echo " If the --regenerate-test-certs parameter is given, a new certificate database"
|
||||
echo " directory is generated before the signing takes place."
|
||||
echo " If the certificate database is not present and the --regenerate-test-certs"
|
||||
echo " parameter is not given the script exits whithout any operations."
|
||||
echo
|
||||
tput bold
|
||||
echo "OPTIONS"
|
||||
echo " --regenerate-test-certs,"
|
||||
tput sgr0
|
||||
echo " Generates a test certificate database and then signs the manifest.webapp"
|
||||
echo " file in the current directory."
|
||||
echo
|
||||
tput bold
|
||||
echo " --help,"
|
||||
tput sgr0
|
||||
echo " Show this usage information."
|
||||
echo
|
||||
}
|
||||
|
||||
# Function to create a signing database
|
||||
# Parameters:
|
||||
# $1: Output directory (where the DB will be created)
|
||||
# $2: Password file
|
||||
createDB() {
|
||||
local db_path=${1}
|
||||
local password_file=${2}
|
||||
|
||||
mkdir -p ${db_path}
|
||||
echo insecurepassword > ${password_file}
|
||||
certutil -d ${db_path} -N -f ${password_file} 2>&1 >/dev/null
|
||||
}
|
||||
|
||||
# Add a CA cert and a signing cert to the database
|
||||
# Arguments:
|
||||
# $1: DB directory
|
||||
# $2: CA CN (don't include the CN=, just the value)
|
||||
# $3: Signing Cert CN (don't include the CN=, just the value)
|
||||
# $4: CA short name (don't use spaces!)
|
||||
# $5: Signing Cert short name (don't use spaces!)
|
||||
# $6: Password file
|
||||
addCerts() {
|
||||
local db_path=${1}
|
||||
local password_file=${6}
|
||||
|
||||
org="O=Example Trusted Corporation,L=Mountain View,ST=CA,C=US"
|
||||
ca_subj="CN=${2},${org}"
|
||||
ee_subj="CN=${3},${org}"
|
||||
|
||||
noisefile=/tmp/noise.$$
|
||||
head -c 32 /dev/urandom > ${noisefile}
|
||||
|
||||
ca_responses=/tmp/caresponses.$$
|
||||
ee_responses=/tmp/earesponses
|
||||
|
||||
echo y > ${ca_responses} # Is this a CA?
|
||||
echo >> ${ca_responses} # Accept default path length constraint (no constraint)
|
||||
echo y >> ${ca_responses} # Is this a critical constraint?
|
||||
echo n > ${ee_responses} # Is this a CA?
|
||||
echo >> ${ee_responses} # Accept default path length constraint (no constraint)
|
||||
echo y >> ${ee_responses} # Is this a critical constraint?
|
||||
|
||||
make_cert="certutil -d ${db_path} -f ${password_file} -S -g 2048 -Z SHA256 \
|
||||
-z ${noisefile} -y 3 -2 --extKeyUsage critical,codeSigning"
|
||||
${make_cert} -v 480 -n ${4} -m 1 -s "${ca_subj}" --keyUsage critical,certSigning \
|
||||
-t ",,CTu" -x < ${ca_responses} 2>&1 >/dev/null
|
||||
${make_cert} -v 240 -n ${5} -c ${4} -m 2 -s "${ee_subj}" --keyUsage critical,digitalSignature \
|
||||
-t ",,," < ${ee_responses} 2>&1 >/dev/null
|
||||
|
||||
certutil -d ${db_path} -L -n ${4} -r -o ${SIGN_SCR_PATH}/${4}.der
|
||||
|
||||
rm -f ${noisefile} ${ee_responses} ${ca_responses}
|
||||
}
|
||||
|
||||
# Signs a manifest
|
||||
# Parameters:
|
||||
# $1: Database directory
|
||||
# $2: Unsigned manifest file path
|
||||
# $3: Signed manifest file path
|
||||
# $4: Nickname of the signing certificate
|
||||
# $5: Password file
|
||||
signManifest() {
|
||||
local db_path=${1}
|
||||
local password_file=${5}
|
||||
|
||||
python ${BASE_PATH}/${SIGN_SCR_PATH}/sign_b2g_manifest.py -d ${db_path} \
|
||||
-f ${password_file} -k ${4} -i ${2} -o ${3}
|
||||
}
|
||||
|
||||
# Generate the necessary files to be used for the signing
|
||||
generate_files() {
|
||||
# First create a new couple of signing DBs
|
||||
rm -rf ${DB_PATH} ${VALID_MANIFEST_PATH} ${INVALID_MANIFEST_PATH}
|
||||
createDB ${DB_PATH} ${PASSWORD_FILE}
|
||||
addCerts ${DB_PATH} "Trusted Valid CA" "Trusted Corp Cert" ${TRUSTED_CA} ${TRUSTED_EE} ${PASSWORD_FILE}
|
||||
addCerts ${DB_PATH} "Trusted Invalid CA" "Trusted Invalid Cert" ${UNTRUSTED_CA} ${UNTRUSTED_EE} ${PASSWORD_FILE}
|
||||
}
|
||||
|
||||
#Start of execution
|
||||
if [ ${1} ] && [ "${1}" == '--regenerate-test-certs' ]; then
|
||||
generate_files
|
||||
elif [ "${1}" == '--help' ]; then
|
||||
usage
|
||||
exit 1
|
||||
else
|
||||
if [ -d ${DB_PATH} ]; then
|
||||
rm -rf ${VALID_MANIFEST_PATH} ${INVALID_MANIFEST_PATH}
|
||||
else
|
||||
echo "Error! The directory ${DB_PATH} does not exist!"
|
||||
echo "New certificate database must be created!"
|
||||
usage $0
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create all the test manifests
|
||||
mkdir -p ${VALID_MANIFEST_PATH}
|
||||
mkdir -p ${INVALID_MANIFEST_PATH}
|
||||
|
||||
CURDIR=`pwd`
|
||||
cd $CURDIR
|
||||
|
||||
# Sign a manifest file with a known issuer
|
||||
signManifest ${DB_PATH} ${BASE_PATH}/manifest.webapp \
|
||||
${VALID_MANIFEST_PATH}/manifest.sig \
|
||||
${TRUSTED_EE} ${PASSWORD_FILE}
|
||||
|
||||
# Sign a manifest file with a unknown issuer
|
||||
signManifest ${DB_PATH} ${BASE_PATH}/manifest.webapp \
|
||||
${INVALID_MANIFEST_PATH}/manifest.sig \
|
||||
${UNTRUSTED_EE} ${PASSWORD_FILE}
|
||||
|
||||
echo "Done!"
|
@ -0,0 +1,10 @@
|
||||
{ "name": "Trusted App Example",
|
||||
"description": "A Manifest for a Trusted Hosted Application",
|
||||
"type": "trusted",
|
||||
"launch_path": "/index.html",
|
||||
"icons": { "128" : "icon-128.png" },
|
||||
"version": 1,
|
||||
"csp" : "script-src https://www.example.com; style-src https://www.example.com",
|
||||
"permissions": { "device-storage:videos":{ "access": "readonly" }, "device-storage:pictures":{ "access": "readwrite" } },
|
||||
"default_locale": "en-US"
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from ctypes import *
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
libprefix = "lib"
|
||||
libsuffix = ".dylib"
|
||||
elif os.name == 'posix':
|
||||
libprefix = "lib"
|
||||
libsuffix = ".so"
|
||||
else: # assume windows
|
||||
libprefix = ""
|
||||
libsuffix = ".dll"
|
||||
|
||||
plc = cdll.LoadLibrary(libprefix + "plc4" + libsuffix)
|
||||
nspr = cdll.LoadLibrary(libprefix + "nspr4" + libsuffix)
|
||||
nss = cdll.LoadLibrary(libprefix + "nss3" + libsuffix)
|
||||
smime = cdll.LoadLibrary(libprefix + "smime3" + libsuffix)
|
||||
|
||||
nspr.PR_GetError.argtypes = []
|
||||
nspr.PR_GetError.restype = c_int32
|
||||
nspr.PR_ErrorToName.argtypes = [c_int32]
|
||||
nspr.PR_ErrorToName.restype = c_char_p
|
||||
|
||||
def raise_if_not_SECSuccess(rv):
|
||||
SECSuccess = 0
|
||||
if (rv != SECSuccess):
|
||||
raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
|
||||
|
||||
def raise_if_NULL(p):
|
||||
if not p:
|
||||
raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
|
||||
return p
|
||||
|
||||
PRBool = c_int
|
||||
SECStatus = c_int
|
||||
|
||||
# from secoidt.h
|
||||
SEC_OID_SHA1 = 4
|
||||
|
||||
# from certt.h
|
||||
certUsageObjectSigner = 6
|
||||
|
||||
class SECItem(Structure):
|
||||
_fields_ = [("type", c_int),
|
||||
("data", c_char_p),
|
||||
("len", c_uint)]
|
||||
|
||||
nss.NSS_Init.argtypes = [c_char_p]
|
||||
nss.NSS_Init.restype = SECStatus
|
||||
def NSS_Init(db_dir):
|
||||
nss.NSS_Init.argtypes = [c_char_p]
|
||||
nss.NSS_Init.restype = SECStatus
|
||||
raise_if_not_SECSuccess(nss.NSS_Init(db_dir))
|
||||
|
||||
nss.NSS_Shutdown.argtypes = []
|
||||
nss.NSS_Shutdown.restype = SECStatus
|
||||
def NSS_Shutdown():
|
||||
raise_if_not_SECSuccess(nss.NSS_Shutdown())
|
||||
|
||||
PK11PasswordFunc = CFUNCTYPE(c_char_p, c_void_p, PRBool, c_char_p)
|
||||
|
||||
# pass the result of this as the wincx parameter when a wincx is required
|
||||
nss.PK11_SetPasswordFunc.argtypes = [PK11PasswordFunc]
|
||||
nss.PK11_SetPasswordFunc.restype = None
|
||||
|
||||
# Set the return type as *void so Python doesn't touch it
|
||||
plc.PL_strdup.argtypes = [c_char_p]
|
||||
plc.PL_strdup.restype = c_void_p
|
||||
def SetPasswordContext(password):
|
||||
def callback(slot, retry, arg):
|
||||
return plc.PL_strdup(password)
|
||||
wincx = PK11PasswordFunc(callback)
|
||||
nss.PK11_SetPasswordFunc(wincx)
|
||||
return wincx
|
||||
|
||||
nss.CERT_GetDefaultCertDB.argtypes = []
|
||||
nss.CERT_GetDefaultCertDB.restype = c_void_p
|
||||
def CERT_GetDefaultCertDB():
|
||||
return raise_if_NULL(nss.CERT_GetDefaultCertDB())
|
||||
|
||||
nss.PK11_FindCertFromNickname.argtypes = [c_char_p, c_void_p]
|
||||
nss.PK11_FindCertFromNickname.restype = c_void_p
|
||||
def PK11_FindCertFromNickname(nickname, wincx):
|
||||
return raise_if_NULL(nss.PK11_FindCertFromNickname(nickname, wincx))
|
||||
|
||||
nss.CERT_DestroyCertificate.argtypes = [c_void_p]
|
||||
nss.CERT_DestroyCertificate.restype = None
|
||||
def CERT_DestroyCertificate(cert):
|
||||
nss.CERT_DestroyCertificate(cert)
|
||||
|
||||
smime.SEC_PKCS7CreateSignedData.argtypes = [c_void_p, c_int, c_void_p,
|
||||
c_int, c_void_p,
|
||||
c_void_p, c_void_p]
|
||||
smime.SEC_PKCS7CreateSignedData.restype = c_void_p
|
||||
def SEC_PKCS7CreateSignedData(cert, certusage, certdb, digestalg, digest, wincx):
|
||||
item = SECItem(0, c_char_p(digest), len(digest))
|
||||
return raise_if_NULL(smime.SEC_PKCS7CreateSignedData(cert, certusage, certdb,
|
||||
digestalg,
|
||||
pointer(item),
|
||||
None, wincx))
|
||||
|
||||
smime.SEC_PKCS7AddSigningTime.argtypes = [c_void_p]
|
||||
smime.SEC_PKCS7AddSigningTime.restype = SECStatus
|
||||
def SEC_PKCS7AddSigningTime(p7):
|
||||
raise_if_not_SECSuccess(smime.SEC_PKCS7AddSigningTime(p7))
|
||||
|
||||
smime.SEC_PKCS7IncludeCertChain.argtypes = [c_void_p, c_void_p]
|
||||
smime.SEC_PKCS7IncludeCertChain.restype = SECStatus
|
||||
def SEC_PKCS7IncludeCertChain(p7, wincx):
|
||||
raise_if_not_SECSuccess(smime.SEC_PKCS7IncludeCertChain(p7, wincx))
|
||||
|
||||
SEC_PKCS7EncoderOutputCallback = CFUNCTYPE(None, c_void_p, c_void_p, c_long)
|
||||
smime.SEC_PKCS7Encode.argtypes = [c_void_p, SEC_PKCS7EncoderOutputCallback,
|
||||
c_void_p, c_void_p, c_void_p, c_void_p]
|
||||
smime.SEC_PKCS7Encode.restype = SECStatus
|
||||
def SEC_PKCS7Encode(p7, bulkkey, wincx):
|
||||
outputChunks = []
|
||||
def callback(chunks, data, len):
|
||||
outputChunks.append(string_at(data, len))
|
||||
callbackWrapper = SEC_PKCS7EncoderOutputCallback(callback)
|
||||
raise_if_not_SECSuccess(smime.SEC_PKCS7Encode(p7, callbackWrapper,
|
||||
None, None, None, wincx))
|
||||
return "".join(outputChunks)
|
||||
|
||||
smime.SEC_PKCS7DestroyContentInfo.argtypes = [c_void_p]
|
||||
smime.SEC_PKCS7DestroyContentInfo.restype = None
|
||||
def SEC_PKCS7DestroyContentInfo(p7):
|
||||
smime.SEC_PKCS7DestroyContentInfo(p7)
|
@ -0,0 +1,76 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import argparse
|
||||
from base64 import b64encode
|
||||
from hashlib import sha1
|
||||
import sys
|
||||
import ctypes
|
||||
import nss_ctypes
|
||||
|
||||
def nss_create_detached_signature(cert, dataToSign, wincx):
|
||||
certdb = nss_ctypes.CERT_GetDefaultCertDB()
|
||||
p7 = nss_ctypes.SEC_PKCS7CreateSignedData(cert,
|
||||
nss_ctypes.certUsageObjectSigner,
|
||||
certdb,
|
||||
nss_ctypes.SEC_OID_SHA1,
|
||||
sha1(dataToSign).digest(),
|
||||
wincx)
|
||||
try:
|
||||
nss_ctypes.SEC_PKCS7AddSigningTime(p7)
|
||||
nss_ctypes.SEC_PKCS7IncludeCertChain(p7, wincx)
|
||||
return nss_ctypes.SEC_PKCS7Encode(p7, None, wincx)
|
||||
finally:
|
||||
nss_ctypes.SEC_PKCS7DestroyContentInfo(p7)
|
||||
|
||||
# Sign a manifest file
|
||||
def sign_file(in_file, out_file, cert, wincx):
|
||||
contents = in_file.read()
|
||||
in_file.close()
|
||||
|
||||
# generate base64 encoded string of the sha1 digest of the input file
|
||||
in_file_hash = b64encode(sha1(contents).digest())
|
||||
|
||||
# sign the base64 encoded string with the given certificate
|
||||
in_file_signature = nss_create_detached_signature(cert, in_file_hash, wincx)
|
||||
|
||||
# write the content of the output file
|
||||
out_file.write(in_file_signature)
|
||||
out_file.close()
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Sign a B2G app manifest.')
|
||||
parser.add_argument('-d', action='store',
|
||||
required=True, help='NSS database directory')
|
||||
parser.add_argument('-f', action='store',
|
||||
type=argparse.FileType('rb'),
|
||||
required=True, help='password file')
|
||||
parser.add_argument('-k', action='store',
|
||||
required=True, help="nickname of signing cert.")
|
||||
parser.add_argument('-i', action='store', type=argparse.FileType('rb'),
|
||||
required=True, help="input manifest file (unsigned)")
|
||||
parser.add_argument('-o', action='store', type=argparse.FileType('wb'),
|
||||
required=True, help="output manifest file (signed)")
|
||||
args = parser.parse_args()
|
||||
|
||||
db_dir = args.d
|
||||
password = args.f.readline().strip()
|
||||
cert_nickname = args.k
|
||||
cert = None
|
||||
|
||||
try:
|
||||
nss_ctypes.NSS_Init(db_dir)
|
||||
wincx = nss_ctypes.SetPasswordContext(password)
|
||||
cert = nss_ctypes.PK11_FindCertFromNickname(cert_nickname, wincx)
|
||||
sign_file(args.i, args.o, cert, wincx)
|
||||
return 0
|
||||
finally:
|
||||
nss_ctypes.CERT_DestroyCertificate(cert)
|
||||
nss_ctypes.NSS_Shutdown()
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user