[PATCH 2 of 4] Implement codesigning in the administrator tool
Wald Commits
scm-commit at wald.intevation.org
Fri May 23 18:23:34 CEST 2014
# HG changeset patch
# User Andre Heinecke <aheinecke at intevation.de>
# Date 1400861838 0
# Node ID 6c4fff1469994e339dc0a4bb71afd08eeec256a0
# Parent c9d296f04995982d23bf18c830a9212af7a6755f
Implement codesigning in the administrator tool
diff -r c9d296f04995 -r 6c4fff146999 ui/createinstallerdialog.cpp
--- a/ui/createinstallerdialog.cpp Fri May 23 16:16:33 2014 +0000
+++ b/ui/createinstallerdialog.cpp Fri May 23 16:17:18 2014 +0000
@@ -19,10 +19,25 @@
#include <QStyle>
#include <QApplication>
#include <QMessageBox>
+#include <QTemporaryDir>
+
+/* Static information used in codesigning */
+#ifndef SIGN_HASH
+#define SIGN_HASH "sha256"
+#endif
+#ifndef SIGN_URL
+#define SIGN_URL "https://wald.intevation.org/projects/trustbridge/"
+#endif
+#ifndef SIGN_PUBLISHER
+#define SIGN_PUBLISHER "TrustBridge Test with ümlaut"
+#endif
+
CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) :
QDialog(parent),
- mProgress(this)
+ mProgress(this),
+ mInstallerPath(),
+ mCurrentWorkingDir(NULL)
{
QSettings settings;
setWindowTitle(tr("Create binary installer"));
@@ -116,7 +131,6 @@
setLayout(topLayout);
mProgress.setWindowModality(Qt::WindowModal);
- mProgress.setLabelText(tr("Creating installer package..."));
mProgress.setCancelButton(0);
mProgress.setRange(0,0);
mProgress.setMinimumDuration(0);
@@ -162,9 +176,18 @@
void CreateInstallerDialog::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
+ if (mCurrentWorkingDir) {
+ delete mCurrentWorkingDir;
+ mCurrentWorkingDir = NULL;
+ }
FinishedDialog *fin = new FinishedDialog(0, tr("Created installer in %1.")
.arg(mSaveFile->text()), mNSISProc.readAll(), false);
qDebug() << "Finished: " << mNSISProc.readAll();
+ mProgress.setLabelText(tr("Signing installer package..."));
+ if (!signFile(mInstallerPath)) {
+ showErrorMessage(tr("Failed to sign installer package."));
+ QFile::remove(mInstallerPath);
+ }
mProgress.cancel();
fin->show();
close();
@@ -172,12 +195,17 @@
void CreateInstallerDialog::processError(QProcess::ProcessError error)
{
+ if (mCurrentWorkingDir) {
+ delete mCurrentWorkingDir;
+ mCurrentWorkingDir = NULL;
+ }
qDebug() << "Error: " << mNSISProc.readAll();
mProgress.cancel();
}
void CreateInstallerDialog::createInstaller()
{
+ mProgress.setLabelText(tr("Creating installer package..."));
QDir binDir(mBinaryFolder->text());
QDir outDir(mSaveFile->text());
if (mBinaryFolder->text().isEmpty() || !binDir.exists()) {
@@ -201,28 +229,39 @@
return;
}
+ QTemporaryDir *signedFilesDir = codesignBinaries(binDir.path() + "/windows");
+
+ if (!signedFilesDir) {
+ /* Error messages should have been shown by the codesign function */
+ return;
+ }
+
+ mProgress.setLabelText(tr("Creating NSIS package..."));
+
/* Copy windows directory contents to tmpdir */
QStringList arguments;
mNSISProc.setProgram("makensis");
mNSISProc.setProcessChannelMode(QProcess::MergedChannels);
mNSISProc.setWorkingDirectory(outDir.path());
#ifdef Q_OS_WIN
- arguments << QString::fromLatin1("/Dfiles_dir=") + binDir.path().replace("/", "\\") + "\\windows";
+ arguments << QString::fromLatin1("/Dfiles_dir=") + signedFilesDir->path().replace("/", "\\");
arguments << "/Dpath_sep=\\";
foreach (const QString &key, keys) {
QString value = options.value(key, QString()).toString();
if (key == "setupname") {
value = value.arg(outDir.path().replace("/", "\\") + "\\");
+ mInstallerPath = value;
}
arguments << QString::fromLatin1("/D%1=%2").arg(key, value);
}
#else
- arguments << QString::fromLatin1("-Dfiles_dir=") + binDir.path() + "/windows";
+ arguments << QString::fromLatin1("-Dfiles_dir=") + signedFilesDir->path();
arguments << "-Dpath_sep=/";
foreach (const QString &key, keys) {
QString value = options.value(key, QString()).toString();
if (key == "setupname") {
value = value.arg(outDir.path() + "/");
+ mInstallerPath = value;
}
arguments << QString::fromLatin1("-D%1=%2").arg(key, value);
}
@@ -242,6 +281,85 @@
}
}
+bool CreateInstallerDialog::signFile(QString filePath) {
+ QProcess signProc;
+ signProc.setProcessChannelMode(QProcess::MergedChannels);
+ signProc.setProgram("osslsigncode");
+ QStringList arguments;
+
+ QSettings mySettings;
+
+ QString publisher = mySettings.value("sign_publisher", SIGN_PUBLISHER).toString();
+ QString url = mySettings.value("sign_url", SIGN_URL).toString();
+ QString hash = mySettings.value("sign_hash", SIGN_HASH).toString();
+
+ arguments << "sign" << "-certs" << mCertFile->text() << "-key"
+ << mCertFile->text() << "-n" << publisher << "-i" <<
+ url << "-h" << hash << "-in" << filePath << "-out" << filePath + ".signed";
+
+ qDebug() << "Starting osslsigncode with arguments: " << arguments;
+ signProc.setArguments(arguments);
+ signProc.start();
+
+ if (!signProc.waitForFinished(30000)) {
+ qDebug() << "Signing takes longer then 30 seconds. Aborting.";
+ return false;
+ }
+
+ if (signProc.exitStatus() != QProcess::NormalExit ||
+ signProc.exitCode() != 0) {
+ qDebug() << "Error process returned: " << signProc.exitCode();
+ qDebug() << "Output: " << signProc.readAllStandardOutput();
+ return false;
+ }
+
+ if (!QFile::remove(filePath)) {
+ qDebug() << "Failed to remove file.";
+ return false;
+ }
+ if (!QFile::copy(filePath + ".signed", filePath)) {
+ qDebug() << "Failed to copy signed file in place.";
+ return false;
+ }
+ if (!QFile::remove(filePath + ".signed")) {
+ qDebug() << "Failed to remove signed.";
+ return false;
+ }
+ return true;
+}
+
+
+QTemporaryDir *CreateInstallerDialog::codesignBinaries(const QDir& sourceDir) {
+ QTemporaryDir* target = new QTemporaryDir();
+ /* Copy all files from the source dir to a temporary location */
+ mProgress.setLabelText(tr("Signing binaries..."));
+
+ mProgress.show();
+ foreach (const QFileInfo& entry, sourceDir.entryInfoList()) {
+ QString targetPath = target->path() + QString::fromLatin1("/") + entry.fileName();
+ if (entry.fileName() == "." || entry.fileName() == "..") {
+ continue;
+ }
+ if (!QFile::copy(entry.absoluteFilePath(), targetPath)) {
+ qDebug() << "Failed to copy: " << entry.absoluteFilePath() << " to: " << targetPath;
+ showErrorMessage(tr("Failed to copy binaries to temporary location."));
+ mProgress.cancel();
+ return NULL;
+ }
+ if (entry.suffix() == "exe") {
+ if (!signFile(targetPath)) {
+ showErrorMessage(tr("Failed to sign binaries with osslsigncode.\n"
+ "Please check that %1 is a valid code signing certificate and that"
+ "osslsigncode can be found in the PATH.").arg(mCertFile->text()));
+ mProgress.cancel();
+ return NULL;
+ }
+ }
+ }
+ mProgress.cancel();
+ return target;
+}
+
FinishedDialog::FinishedDialog(QDialog *parent,
QString msg, QString details, bool isErr):
QDialog(parent)
diff -r c9d296f04995 -r 6c4fff146999 ui/createinstallerdialog.h
--- a/ui/createinstallerdialog.h Fri May 23 16:16:33 2014 +0000
+++ b/ui/createinstallerdialog.h Fri May 23 16:17:18 2014 +0000
@@ -13,12 +13,14 @@
#include <QLineEdit>
#include <QProcess>
#include <QProgressDialog>
+#include <QDir>
/**
* @file createinstallerdialog.h
* @brief The dialog to show settings and create an installer.
*/
class QListWidget;
+class QTemporaryDir;
class CreateInstallerDialog : public QDialog
{
@@ -38,6 +40,8 @@
QProcess mNSISProc;
QProgressDialog mProgress;
+ QString mInstallerPath;
+ QTemporaryDir *mCurrentWorkingDir;
/** @brief show an error message with QMessageBox
*
@@ -49,8 +53,44 @@
void openCertificateSelect();
void openFolderSelect();
void openSaveLocation();
+
+ /**@brief entry point for installer creation
+ *
+ * check the selected parameters (certificate / folder etc.) and
+ * create the nsis installer. This also creates the signatures. */
void createInstaller();
+ /**@brief Create tempoary dir with signed binaries from sourcedir
+ *
+ * Copies all files from the sourceDir to a temporary directory
+ * and signs all .exe files in that directory.
+ *
+ * The caller needs to delete the temporary directory. If an error
+ * occurs NULL is returned.
+ *
+ * @param[in] sourceDir the directory with the binaries to sign
+ * @returns a pointer to a temporary dir containing the signed binaries
+ * or NULL.
+ */
+ QTemporaryDir *codesignBinaries(const QDir& sourceDir);
+
+ /**@brief Sign a file with the codesigning certificate from mCertFile
+ *
+ * Calls osslsigncode to sign the file pointed to in filePath.
+ * The signing operation is logged.
+ *
+ * Sign information (hash algo / publisher / url) can be set at
+ * build time or in the settings with the variables:
+ *
+ * sign_hash # the hash algorithm to use. Values are the same as in singtool
+ * sign_publisher # the publisher information
+ * sign_url # the product url to use in the signature
+ *
+ * @param[in] filePath the absolute path to the file.
+ * @returns true on success, false on error
+ */
+ bool signFile(QString filePath);
+
/* Slots for the creator process */
void processError(QProcess::ProcessError error);
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
diff -r c9d296f04995 -r 6c4fff146999 ui/tests/data/NOTES
--- a/ui/tests/data/NOTES Fri May 23 16:16:33 2014 +0000
+++ b/ui/tests/data/NOTES Fri May 23 16:17:18 2014 +0000
@@ -114,5 +114,6 @@
osslsigncode sign -certs codesigning.pem -key codesigning.key \
-n "TrustBridgeTest" -i https://wald.intevation.org/projects/trustbridge/ \
+ -h sha256 \
-in ~/ubuntu/src/m13-repo/build-windows/TrustBridge-0.6+21-aee3eb10bbba.exe \
-out TrustBridge-0.6+21-aee3eb10bbba-signed.exe
More information about the Trustbridge-commits
mailing list