Skip to content

Commit

Permalink
Assorted code_sign mod refactoring, including trust cert installer si…
Browse files Browse the repository at this point in the history
…gning via the new config factory driven option.
  • Loading branch information
BuvinJ committed Feb 16, 2021
1 parent bff31a9 commit e34ffb9
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 59 deletions.
74 changes: 32 additions & 42 deletions distbuilder/code_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#------------------------------------------------------------------------------
SIGNTOOL_PATH_ENV_VAR = "SIGNTOOL_PATH"

# TODO: Refactor, so as to not be quite so MS SignTool specific
#TODO: SUPPORT OTHER PLATFORMS!!!
class CodeSignConfig:

DEFAULT_DIGEST = "sha256"
Expand All @@ -25,8 +25,10 @@ class CodeSignConfig:
__ARM_64BIT_DIR = "arm64"
__SIGNTOOL_NAME = "signtool.exe"

_KEY_EXT = ".pfx" if IS_WINDOWS else ".???" # TODO

@staticmethod
def _builtInInstallerPath():
def _builtInSignToolInstallerPath():
return joinPath( _RES_DIR_PATH,
CodeSignConfig.__RES_DIR_NAME, CodeSignConfig.__MSI_NAME )

Expand All @@ -46,10 +48,10 @@ def _defaultSignToolPath( isVerified=False ):
if isVerified and not isFile( path ): path = None
return path

def __init__( self, pfxFilePath=None, keyPassword=None ):
def __init__( self, keyFilePath=None, keyPassword=None ):

self.pfxFilePath = absPath( pfxFilePath )
self.keyPassword = keyPassword
self.keyFilePath = absPath( keyFilePath )
self.keyPassword = keyPassword

self.signToolPath = None # if None, this will be auto resolved

Expand All @@ -61,16 +63,12 @@ def __init__( self, pfxFilePath=None, keyPassword=None ):
self.isDebugMode = True

def __str__( self ) :
if not isFile( self.pfxFilePath ):
raise Exception(
"Missing or invalid pfx path in CodeSignConfig: %s" %
(self.pfxFilePath,) )
operation = "sign"
verbose = '/v' if self.isDebugMode else ''
fileDigest = "/fd %s" % (self.fileDigest,)
timeStampServer = "/tr %s" % (self.timeStampServerUrl,)
timeStampDigest = "/td %s" % (self.timeStampDigest,)
pfxFilePath = '/f "%s"' % (self.pfxFilePath,)
pfxFilePath = '/f "%s"' % (self.keyFilePath,)
keyPassword =('/p "%s"' % (self.keyPassword,)
if self.keyPassword else "" )
tokens = (operation, verbose, fileDigest,
Expand All @@ -88,9 +86,12 @@ def __useSignTool( exePath, codeSignConfig ):
return exePath

def __validateCodeSignConfig( cfg ):
if not isFile( cfg.pfxFilePath ):
raise Exception( "Missing or invalid PFX file path: %s" %
(cfg.pfxFilePath,) )
if( not isFile( cfg.keyFilePath ) or
fileExt( cfg.keyFilePath ).lower() != CodeSignConfig._KEY_EXT ):
raise Exception(
"Missing or invalid key path in CodeSignConfig: %s" %
(cfg.keyFilePath,) )

if cfg.signToolPath is None:
cfg.signToolPath = getenv( SIGNTOOL_PATH_ENV_VAR )
if cfg.signToolPath is None:
Expand All @@ -103,13 +104,14 @@ def __validateCodeSignConfig( cfg ):

def __installSignTool():
print( "Installing SignTool utility...\n" )
if not util._isSystemSuccess( CodeSignConfig._builtInInstallerPath() ):
if not util._isSystemSuccess( CodeSignConfig._builtInSignToolInstallerPath() ):
raise Exception( "SignTool installation FAILED" )
return CodeSignConfig._defaultSignToolPath( isVerified=True )

#------------------------------------------------------------------------------
MAKECERT_PATH_ENV_VAR = "MAKECERT_PATH"


#TODO: SUPPORT OTHER PLATFORMS!!! via OpenSSL?
class SelfSignedCertConfig:

DEFAULT_END_DATE = '12/31/2050'
Expand Down Expand Up @@ -311,7 +313,7 @@ def __init__( self, caCertPath, privateKeyPath,
self.caCertPath = absPath( caCertPath )
self.privateKeyPath = absPath( privateKeyPath )
self.keyPassword = keyPassword
self.pfxFilePath =( pfxFilePath if pfxFilePath else
self.keyFilePath =( pfxFilePath if pfxFilePath else
joinExt( splitExt( privateKeyPath )[0], Pvk2PfxConfig._PFX_EXT ) )

self.pvk2PfxPath = None # if None, this will be auto resolved
Expand All @@ -320,18 +322,10 @@ def __init__( self, caCertPath, privateKeyPath,

self.isDebugMode = True

def __str__( self ) :
if not isFile( self.privateKeyPath ):
raise Exception(
"Missing or invalid private key path in Pvk2PfxConfig: %s" %
(self.privateKeyPath,) )
if not isFile( self.caCertPath ):
raise Exception(
"Missing or invalid CA cert path in Pvk2PfxConfig: %s" %
(self.caCertPath,) )
def __str__( self ) :
privateKeyPath = '/pvk "%s"' % (self.privateKeyPath,)
caCertPath = '/spc "%s"' % (self.caCertPath,)
pfxFilePath = '/pfx "%s"' % (self.pfxFilePath,)
pfxFilePath = '/pfx "%s"' % (self.keyFilePath,)
keyPassword =( '/pi "%s"' % (self.keyPassword,)
if self.keyPassword else "" )
tokens = (privateKeyPath, caCertPath, pfxFilePath, keyPassword,
Expand All @@ -342,10 +336,10 @@ def __usePvk2Pfx( pvk2PfxConfig, isOverwrite ):
__validatePvk2PfxConfig( pvk2PfxConfig, isOverwrite )
cmd = '"%s" %s' % ( pvk2PfxConfig.pvk2PfxPath, str(pvk2PfxConfig) )
if( not util._isSystemSuccess( cmd ) or
not isFile( pvk2PfxConfig.pfxFilePath ) ):
not isFile( pvk2PfxConfig.keyFilePath ) ):
raise Exception( 'FAILED convert private key to PFX file' )
print( "Generated Personal Information Exchange (PFX) file successfully!" )
return pvk2PfxConfig.pfxFilePath
return pvk2PfxConfig.keyFilePath

def __validatePvk2PfxConfig( cfg, isOverwrite ):
if not isFile( cfg.privateKeyPath ):
Expand All @@ -356,13 +350,13 @@ def __validatePvk2PfxConfig( cfg, isOverwrite ):
raise Exception(
"Missing or invalid CA cert path in Pvk2PfxConfig: %s" %
(cfg.caCertPath,) )
destDirPath = dirPath( cfg.pfxFilePath )

destDirPath = dirPath( cfg.keyFilePath )
if isDir( destDirPath ):
if isOverwrite:
removeFromDir( baseFileName( cfg.pfxFilePath ), destDirPath )
elif isFile( cfg.pfxFilePath ):
raise Exception( "File exists: %s" % (cfg.pfxFilePath,) )
removeFromDir( baseFileName( cfg.keyFilePath ), destDirPath )
elif isFile( cfg.keyFilePath ):
raise Exception( "File exists: %s" % (cfg.keyFilePath,) )
else: makeDir( destDirPath )

if cfg.pvk2PfxPath is None:
Expand Down Expand Up @@ -488,13 +482,14 @@ def trustCertInstallerConfigFactory( companyTradeName,
f.iconFilePath = iconFilePath
f.version = version
f.isOneFile = True
f.entryPointPy = script.fileName()
f.entryPointPy = script.fileName()
# sign the installer itself
f.codeSignConfig = CodeSignConfig( keyFilePath= keyFilePath,
keyPassword=keyPassword )

# Adding custom attributes on the fly!
f._trustCertScript = script
f._caCertPath = caCertPath
f._keyFilePath = keyFilePath
f._keyPassword = keyPassword

return f

Expand All @@ -511,12 +506,7 @@ def onPyInstConfig( self, cfg ):
cfg.dataFilePaths.append( self.configFactory.iconFilePath )

def onFinalize( self ):
self.configFactory._trustCertScript.remove()
# sign the installer itself
signConfig = CodeSignConfig(
pfxFilePath=absPath( self.configFactory._keyFilePath ),
keyPassword=self.configFactory._keyPassword )
signExe( self.binPath, signConfig )
self.configFactory._trustCertScript.remove()

def signExe( exePath, codeSignConfig ):
exePath = normBinaryName( exePath, isPathPreserved=True )
Expand Down
7 changes: 3 additions & 4 deletions examples/hello_code_signing/certs/generateTrustCerts.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from distbuilder import( SelfSignedCertConfig, TrustInstallerBuilderProcess,
getPassword, generateTrustCerts, trustCertInstallerConfigFactory )
trustCertInstallerConfigFactory, generateTrustCerts, getPassword )

companyTradeName = "Some Company"
companyLegalName = "Some Company Inc."
iconFilePath = "../../hello_world_tk/demo.ico"

# Supply a PFX password using one of the following options.
# Supply a key password using one of the following options.
password = getPassword( isGuiPrompt=True )
#password = None # Verify in prompts that appear that you really don't want a password!
#password = "my-secure-password"

# Generate code signing files to retain (securely!) in house
# It is recommended you set isOverwrite=False in production, to prevent
# accidental losses of certs / keys.
# accidental losses of certs / keys!
certConfig = SelfSignedCertConfig( companyTradeName )
caCertPath, keyFilePath = generateTrustCerts(
certConfig, keyPassword=password, isOverwrite=True )
Expand All @@ -26,4 +26,3 @@
p.isExeTest = True
p.isElevatedTest = True
p.run()

15 changes: 8 additions & 7 deletions examples/hello_code_signing/signedInstaller.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from distbuilder import( PyToBinInstallerProcess, ConfigFactory,
CodeSignConfig, getPassword)
CodeSignConfig, getPassword )

f = configFactory = ConfigFactory()
f.productName = "Hello World Tk Example"
Expand All @@ -13,13 +13,14 @@
f.version = (1,0,0,0)
f.setupName = "HelloSignedSetup"

# You must run the generateTrustCerts example first, to have the PFX file
# NOTE: BOTH the installer and program binary will be signed in this process.
# You must run the generateTrustCerts example first, to have the key file
# referenced below.
# Supply the PFX password using one of the following options shown.
f.codeSignConfig = CodeSignConfig( pfxFilePath="./certs/SomeCompany.pfx",
keyPassword=getPassword( isGuiPrompt=True ) )
#keyPassword=None )
#keyPassword="my-secure-password" )
# Supply the key password using one of the following options shown.
f.codeSignConfig = CodeSignConfig( keyFilePath="./certs/SomeCompany.pfx",
keyPassword=getPassword( isGuiPrompt=True ) )
#keyPassword=None )
#keyPassword="my-secure-password" )

p = PyToBinInstallerProcess( configFactory, isDesktopTarget=True )
p.isAutoInstallTest = True
Expand Down
13 changes: 7 additions & 6 deletions examples/hello_code_signing/signedPyBin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
f.binaryName = "HelloWorld"
f.version = (1,0,0,0)
f.entryPointPy = "../hello_world/hello.py"
# You must run the generateTrustCerts example first, to have the PFX file

# You must run the generateTrustCerts example first, to have the key file
# referenced below.
# Supply the PFX password using one of the following options shown.
f.codeSignConfig = CodeSignConfig( pfxFilePath="./certs/SomeCompany.pfx",
keyPassword=getPassword( isGuiPrompt=True ) )
#keyPassword=None )
#keyPassword="my-secure-password" )
# Supply the key password using one of the following options shown.
f.codeSignConfig = CodeSignConfig( keyFilePath="./certs/SomeCompany.pfx",
keyPassword=getPassword( isGuiPrompt=True ) )
#keyPassword=None )
#keyPassword="my-secure-password" )

p = PyToBinPackageProcess( configFactory )
p.run()

0 comments on commit e34ffb9

Please sign in to comment.