Skip to content

Commit

Permalink
Merge pull request #156 from YehudaKremer/SignToolFix
Browse files Browse the repository at this point in the history
Fix using SignTool options
  • Loading branch information
YehudaKremer authored Nov 1, 2022
2 parents 4ad9da5 + 6220f26 commit 5587d23
Show file tree
Hide file tree
Showing 18 changed files with 488 additions and 259 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 3.7.0

- add full support for [SignTool](https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe) usage (addressing [#155](https://github.com/YehudaKremer/msix/pull/155#issue-1421291620)), see [examples page](https://github.com/YehudaKremer/msix/tree/main/example)

## 3.6.6

- replacing cli_dialog package with console package to solve transitive dependencies [https://github.com/timsneath/dart_console/issues/54](https://github.com/timsneath/dart_console/issues/54)
Expand Down Expand Up @@ -85,7 +89,7 @@

## 3.0.0

- add `publish` command and configurations, for sideloading publish (outside the microsoft store)
- add `publish` command and configurations, for side loading publish (outside the microsoft store)
- user asked (cli dialog) if he want to **increment version number** (if needed)
- user asked (cli dialog) if he want to install the test certificate
- add [toast notifications](https://github.com/YehudaKremer/msix/issues/94) configuration
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,14 @@ See [Configurations Examples And Use Cases].
<details>
<summary>Sign configuration (click to expand)</summary>

| YAML name | Command-line argument | Description | Example |
| ---------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `certificate_path` | `--certificate-path` `-c` | Path to the certificate content to place in the store. | `C:\certs\signcert.pfx` |
| `certificate_password` | `--certificate-password` `-p` | Password for the certificate. | `1234` |
| `publisher` | `--publisher` `-b` | The `Subject` value in the certificate. | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` |
| `signtool_options` | `--signtool-options` | Options to be provided to the `signtool` for app signing (see below.) | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer` |
| `sign_msix` | `--sign-msix <true/false>` | If `false`, don't sign the msix file, default is `true`.<br />Note: when **false**, `publisher` is Required. | `true` |
| `install_certificate` | `--install-certificate <true/false>` | If `false`, don't try to install the certificate, default is `true`. | `true` |
| YAML name | Command-line argument | Description | Example |
| ---------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `certificate_path` | `--certificate-path` `-c` | Path to the certificate content to place in the store. | `C:\certs\signcert.pfx` or `C:\certs\signcert.crt` |
| `certificate_password` | `--certificate-password` `-p` | Password for the certificate. | `1234` |
| `publisher` | `--publisher` `-b` | The `Subject` value in the certificate.<br /> Required only if publish to the store, or if the `Publisher` will not found automatically by this package. | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` |
| `signtool_options` | `--signtool-options` | Options to be provided to the `signtool` for app signing (see below.) | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer` |
| `sign_msix` | `--sign-msix <true/false>` | If `false`, don't sign the msix file, default is `true`.<br />Note: when **false**, `publisher` is Required. | `true` |
| `install_certificate` | `--install-certificate <true/false>` | If `false`, don't try to install the certificate, default is `true`. | `true` |

</details>

Expand Down
54 changes: 52 additions & 2 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ msix_config:
msix_config:
display_name: Flutter App
publisher_display_name: Company Name
identity_name: 48434MySoftware.MyFlutterApp
identity_name: 48434MySoftware.MyFlutterApp
publisher: CN=BF212345-5644-46DF-8668-012044C1B138
msix_version: 1.0.1.0
store: true
Expand Down Expand Up @@ -43,7 +43,10 @@ msix_config:
display_name: Flutter App
install_certificate: false
```
Note: The main app version will be used as a basis for the MSIX version (`1.3.2` to `1.3.2.0`)
##### Note: The main app version will be used as a basis for the MSIX version (`1.3.2` to `1.3.2.0`)

<br />

###### Without signing

Expand All @@ -64,3 +67,50 @@ msix_config:
languages: en-us, de-de
capabilities: "internetClient,location,microphone,webcam"
```

#### Signing With Installed Certificate Using SignTool

###### By Subject:

```yaml
msix_config:
display_name: Flutter App
msix_version: 1.0.0.0
signtool_options: /v /debug /sm /fd sha256 /n "Msix Testing" /tr http://timestamp.digicert.com
```

###### By Issuer:

```yaml
msix_config:
display_name: Flutter App
msix_version: 1.0.0.0
signtool_options: /fd sha256 /i "Msix Testing" /tr http://timestamp.digicert.com
```

###### By Thumbprint:

```yaml
msix_config:
display_name: Flutter App
msix_version: 1.0.0.0
signtool_options: /fd sha256 /sha1 028bc9922d198ee83d776aa19cb8e82897691e0c /tr http://timestamp.digicert.com
```

###### By .crt File:

```yaml
msix_config:
display_name: Flutter App
msix_version: 1.0.0.0
signtool_options: /fd SHA256 /f "<path_to>\test_certificate.crt"
```

###### By .pfx File:

```yaml
msix_config:
display_name: Flutter App
msix_version: 1.0.0.0
signtool_options: /fd SHA256 /f "<path_to>\test_certificate.pfx" /p 1234
```
31 changes: 18 additions & 13 deletions lib/msix.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:io';

import 'package:cli_util/cli_logging.dart' show Logger, Ansi;
import 'package:cli_util/cli_logging.dart';
import 'package:get_it/get_it.dart';
import 'src/app_installer.dart';
import 'src/windows_build.dart';
Expand All @@ -10,7 +9,7 @@ import 'src/makepri.dart';
import 'src/appx_manifest.dart';
import 'src/makeappx.dart';
import 'src/sign_tool.dart';
import 'src/extensions.dart';
import 'src/method_extensions.dart';

/// Main class that handles all the msix package functionality
class Msix {
Expand All @@ -27,7 +26,7 @@ class Msix {
Future<void> build() async {
await _initConfig();
await _buildMsixFiles();
final msixStyledPath = File(_msixOutputPath).parent.path.blue.emphasized;
String msixStyledPath = File(_msixOutputPath).parent.path.blue.emphasized;
_logger
.write('${'unpackaged msix files created in: '.green}$msixStyledPath');
}
Expand All @@ -37,9 +36,9 @@ class Msix {
await _initConfig();

/// check if the appx manifest is exist
var appxManifestPath = '${_config.buildFilesFolder}/AppxManifest.xml';
String appxManifestPath = '${_config.buildFilesFolder}/AppxManifest.xml';
if (!(await File(appxManifestPath).exists())) {
var error = 'run "msix:build" first';
String error = 'run "msix:build" first';
_logger.stderr(error.red);
exit(-1);
}
Expand All @@ -63,12 +62,12 @@ class Msix {
Future<void> publish() async {
await _initConfig();
await _config.validateAppInstallerConfigValues();
var appInstaller = AppInstaller();
AppInstaller appInstaller = AppInstaller();
await appInstaller.validatePublishVersion();

await _createMsix();

var loggerProgress = _logger.progress('publishing');
Progress loggerProgress = _logger.progress('publishing');
await appInstaller.copyMsixToVersionsFolder();
await appInstaller.generateAppInstaller();
await appInstaller.generateAppInstallerWebSite();
Expand Down Expand Up @@ -103,10 +102,10 @@ class Msix {
Future<void> _buildMsixFiles() async {
if (_config.buildWindows) await WindowsBuild().build();

var loggerProgress = _logger.progress('building msix files');
Progress loggerProgress = _logger.progress('building msix files');

await _config.validateWindowsBuildFiles();
final assets = Assets();
Assets assets = Assets();
await assets.cleanTemporaryFiles(clearMsixFiles: true);
await assets.createIcons();
await assets.copyVCLibsFiles();
Expand All @@ -121,14 +120,20 @@ class Msix {
}

Future<void> _packMsixFiles() async {
var loggerProgress = _logger.progress('packing msix files');
Progress loggerProgress = _logger.progress('packing msix files');

await MakeAppx().pack();
await Assets().cleanTemporaryFiles();

if (_config.signMsix && !_config.store) {
final signTool = SignTool();
if (_config.installCert) await signTool.installCertificate();
SignTool signTool = SignTool();

if (_config.installCert &&
(_config.signToolOptions == null ||
_config.signToolOptions!.isEmpty)) {
await signTool.installCertificate();
}

await signTool.sign();
}

Expand Down
72 changes: 32 additions & 40 deletions lib/src/app_installer.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'dart:io';
import 'dart:convert' show HtmlEscape, base64Encode;
import 'dart:convert';
import 'package:console/console.dart';
import 'package:get_it/get_it.dart';
import 'package:image/image.dart'
show Image, copyResize, decodeImage, encodePng, trim;
import 'package:msix/src/extensions.dart';
import 'package:path/path.dart' show basename;
import 'package:pub_semver/pub_semver.dart' show Version;
import 'package:cli_util/cli_logging.dart' show Logger;
import 'package:image/image.dart';
import 'method_extensions.dart';
import 'package:path/path.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:cli_util/cli_logging.dart';
import 'configuration.dart';

/// Handles the creation of .appinstaller file and msix versions
Expand All @@ -26,18 +25,18 @@ class AppInstaller {

if (!await File(_config.appInstallerPath).exists()) return;

var appInstallerContent =
String appInstallerContent =
await File(_config.appInstallerPath).readAsString();
var appInstallerVersion = appInstallerContent.substring(
String appInstallerVersion = appInstallerContent.substring(
appInstallerContent.indexOf('Version="') + 9,
appInstallerContent.indexOf(
'"', appInstallerContent.indexOf('Version="') + 9));
appInstallerVersion =
appInstallerVersion.substring(0, appInstallerVersion.lastIndexOf('.'));
var lastPublishVersion = Version.parse(appInstallerVersion);
var msixVersion = Version.parse(_config.msixVersion!
Version lastPublishVersion = Version.parse(appInstallerVersion);
Version msixVersion = Version.parse(_config.msixVersion!
.substring(0, _config.msixVersion!.lastIndexOf('.')));
var msixVersionRevision =
String msixVersionRevision =
_config.msixVersion!.substring(_config.msixVersion!.lastIndexOf('.'));

if (lastPublishVersion == msixVersion || lastPublishVersion > msixVersion) {
Expand All @@ -49,7 +48,7 @@ class AppInstaller {
'You publishing older version ($msixVersion) then last publish version ($lastPublishVersion)');
}

var installCertificate = await readInput(
String installCertificate = await readInput(
'Do you want to increment it to version ${lastPublishVersion.nextPatch} ?'
.emphasized +
' (y/N) '.gray);
Expand All @@ -73,7 +72,7 @@ class AppInstaller {
Future<void> generateAppInstaller() async {
_logger.trace('generate app installer');

var appInstallerContent = '''<?xml version="1.0" encoding="utf-8"?>
String appInstallerContent = '''<?xml version="1.0" encoding="utf-8"?>
<AppInstaller xmlns="http://schemas.microsoft.com/appx/appinstaller/2018"
Uri="${_config.appInstallerPath}" Version="${_config.msixVersion}">
<MainPackage Name="${_config.identityName}" Version="${_config.msixVersion}"
Expand All @@ -99,28 +98,21 @@ class AppInstaller {
Future<void> generateAppInstallerWebSite() async {
_logger.trace('generate app installer web site');

webInstallerSite = webInstallerSite.replaceAll(
'PAGE_TITLE', _config.displayName ?? _config.appName!);
webInstallerSite = webInstallerSite.replaceAll(
'PAGE_DESCRIPTION',
_config.appDescription ??
'${_config.displayName ?? _config.appName!} installer');
webInstallerSite = webInstallerSite.replaceAll(
'PAGE_TITLE', _config.displayName ?? _config.appName!);
webInstallerSite = webInstallerSite.replaceAll(
'APP_NAME', _config.displayName ?? _config.appName!);
webInstallerSite =
webInstallerSite.replaceAll('APP_VERSION', _config.msixVersion!);
webInstallerSite = webInstallerSite.replaceAll(
'APP_INSTALLER_LINK', basename(_config.appInstallerPath));
webInstallerSite = webInstallerSite.replaceAll(
'REQUIRED_OS_VERSION', _config.osMinVersion);
webInstallerSite =
webInstallerSite.replaceAll('ARCHITECTURE', _config.architecture!);
webInstallerSite =
webInstallerSite.replaceAll('PUBLISHER_NAME', _config.publisherName!);

var logoImage = decodeImage(await File(_config.logoPath ??
webInstallerSite = webInstallerSite
.replaceAll('PAGE_TITLE', _config.displayName ?? _config.appName!)
.replaceAll(
'PAGE_DESCRIPTION',
_config.appDescription ??
'${_config.displayName ?? _config.appName!} installer')
.replaceAll('PAGE_TITLE', _config.displayName ?? _config.appName!)
.replaceAll('APP_NAME', _config.displayName ?? _config.appName!)
.replaceAll('APP_VERSION', _config.msixVersion!)
.replaceAll('APP_INSTALLER_LINK', basename(_config.appInstallerPath))
.replaceAll('REQUIRED_OS_VERSION', _config.osMinVersion)
.replaceAll('ARCHITECTURE', _config.architecture!)
.replaceAll('PUBLISHER_NAME', _config.publisherName!);

Image logoImage = decodeImage(await File(_config.logoPath ??
'${_config.defaultsIconsFolderPath}/Square44x44Logo.altform-lightunplated_targetsize-256.png')
.readAsBytes())!;

Expand All @@ -134,16 +126,16 @@ class AppInstaller {
String base64Logo = base64Encode(encodePng(siteLogo));
String base64favicon = base64Encode(encodePng(favicon));

webInstallerSite = webInstallerSite.replaceAll('IMAGE_BASE64', base64Logo);
webInstallerSite =
webInstallerSite.replaceAll('FAVICON_BASE64', base64favicon);
webInstallerSite = webInstallerSite
.replaceAll('IMAGE_BASE64', base64Logo)
.replaceAll('FAVICON_BASE64', base64favicon);

await File('${_config.publishFolderPath}/index.html')
.writeAsString(webInstallerSite);
}
}

var webInstallerSite = '''
String webInstallerSite = '''
<!DOCTYPE html>
<html lang="en">
Expand Down
16 changes: 8 additions & 8 deletions lib/src/appx_manifest.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:io';
import 'package:cli_util/cli_logging.dart' show Logger;
import 'package:cli_util/cli_logging.dart';
import 'package:get_it/get_it.dart';
import 'capabilities.dart';
import 'configuration.dart';
import 'extensions.dart';
import 'method_extensions.dart';

/// Handles the creation of the manifest file
class AppxManifest {
Expand All @@ -14,7 +14,7 @@ class AppxManifest {
Future<void> generateAppxManifest() async {
_logger.trace('generate appx manifest');

var manifestContent = '''<?xml version="1.0" encoding="utf-8"?>
String manifestContent = '''<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
Expand Down Expand Up @@ -76,7 +76,7 @@ class AppxManifest {
//clear empty rows
manifestContent = manifestContent.replaceAll(' \n', '');

var appxManifestPath = '${_config.buildFilesFolder}/AppxManifest.xml';
String appxManifestPath = '${_config.buildFilesFolder}/AppxManifest.xml';
await File(appxManifestPath).writeAsString(manifestContent);
}

Expand Down Expand Up @@ -112,8 +112,8 @@ class AppxManifest {

/// Add extension section for [_config.protocolActivation]
String _getProtocolActivationExtension() {
var protocolsActivation = '';
for (var protocol in _config.protocolActivation) {
String protocolsActivation = '';
for (String protocol in _config.protocolActivation) {
protocolsActivation += '''
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="${protocol.toHtmlEscape()}">
Expand Down Expand Up @@ -169,13 +169,13 @@ class AppxManifest {

String _normalizeCapability(String capability) {
capability = capability.trim();
var firstLetter = capability.substring(0, 1).toLowerCase();
String firstLetter = capability.substring(0, 1).toLowerCase();
return firstLetter + capability.substring(1);
}

/// Add capabilities section
String _getCapabilities() {
var capabilities = _config.capabilities?.split(',') ?? [];
List<String> capabilities = _config.capabilities?.split(',') ?? [];
capabilities.add('runFullTrust');
capabilities = capabilities.toSet().toList();
String capabilitiesString = '';
Expand Down
Loading

0 comments on commit 5587d23

Please sign in to comment.