Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/Sign.Cli/CertificateStoreResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Sign.Cli/CertificateStoreResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,7 @@
<data name="UseMachineKeyContainerOptionDescription" xml:space="preserve">
<value>Use a machine-level private key container. (The default is user-level.)</value>
</data>
<data name="ContainersDescription" xml:space="preserve">
<value>Sign container contents.</value>
</data>
</root>
4 changes: 4 additions & 0 deletions src/Sign.Cli/CodeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal sealed class CodeCommand : Command
internal Option<Uri?> DescriptionUrlOption { get; } = new(["--description-url", "-u"], ParseUrl, description: Resources.DescriptionUrlOptionDescription);
internal Option<HashAlgorithmName> FileDigestOption { get; } = new(["--file-digest", "-fd"], HashAlgorithmParser.ParseHashAlgorithmName, description: Resources.FileDigestOptionDescription);
internal Option<string?> FileListOption = new(["--file-list", "-fl"], Resources.FileListOptionDescription);
internal Option<bool> RecurseContainersOption { get; } = new(["--recurse-containers", "-rc"], getDefaultValue: () => true, description: CertificateStoreResources.ContainersDescription);
internal Option<int> MaxConcurrencyOption { get; } = new(["--max-concurrency", "-m"], ParseMaxConcurrencyOption, description: Resources.MaxConcurrencyOptionDescription);
internal Option<string?> OutputOption { get; } = new(["--output", "-o"], Resources.OutputOptionDescription);
internal Option<string?> PublisherNameOption { get; } = new(["--publisher-name", "-pn"], Resources.PublisherNameOptionDescription);
Expand All @@ -51,6 +52,7 @@ internal CodeCommand()
AddGlobalOption(OutputOption);
AddGlobalOption(PublisherNameOption);
AddGlobalOption(FileListOption);
AddGlobalOption(RecurseContainersOption);
AddGlobalOption(FileDigestOption);
AddGlobalOption(TimestampUrlOption);
AddGlobalOption(TimestampDigestOption);
Expand All @@ -68,6 +70,7 @@ internal async Task HandleAsync(InvocationContext context, IServiceProviderFacto
string? description = context.ParseResult.GetValueForOption(DescriptionOption);
Uri? descriptionUrl = context.ParseResult.GetValueForOption(DescriptionUrlOption);
string? fileListFilePath = context.ParseResult.GetValueForOption(FileListOption);
bool recurseContainers = context.ParseResult.GetValueForOption(RecurseContainersOption);
HashAlgorithmName fileHashAlgorithmName = context.ParseResult.GetValueForOption(FileDigestOption);
HashAlgorithmName timestampHashAlgorithmName = context.ParseResult.GetValueForOption(TimestampDigestOption);
Uri timestampUrl = context.ParseResult.GetValueForOption(TimestampUrlOption)!;
Expand Down Expand Up @@ -179,6 +182,7 @@ internal async Task HandleAsync(InvocationContext context, IServiceProviderFacto
inputFiles,
output,
fileList,
recurseContainers,
baseDirectory,
applicationName,
publisherName,
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Heslo pro soubor certifikátu.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Zprostředkovatel kryptografických služeb obsahující kontejner privátního klíče. Vyžaduje /k a volitelně /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Kennwort für Zertifikatdatei.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Kryptografiedienstanbieter, der den privaten Schlüsselcontainer enthält. Erfordert /k und optional /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Contraseña del archivo de certificado.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Proveedor de servicios criptográficos que contiene el contenedor de claves privadas. Requiere /k y, opcionalmente, /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Mot de passe du fichier du certificat.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Fournisseur de services de chiffrement contenant le conteneur de clé privée. Nécessite /k et éventuellement /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Password per file certificato.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Provider del servizio di crittografia contenente il contenitore di chiavi private. Richiede /k e facoltativamente /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">証明書ファイルのパスワード。</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">秘密キー コンテナーを含む暗号化サービス プロバイダー。/k および必要に応じて /km が必要です。</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">인증서 파일의 암호입니다.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">프라이빗 키 컨테이너를 포함하는 암호화 서비스 공급자입니다. /k 및 선택적으로 /km이 필요합니다.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Hasło dla pliku certyfikatu.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Dostawca usług kryptograficznych zawierający kontener kluczy prywatnych. Wymaga opcji /k i opcjonalnie /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Senha do arquivo do certificado.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Provedor de Serviços Criptográficos que contém o contêiner de chave privada. Requer /k e, opcionalmente, /km.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Пароль для файла сертификата.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Поставщик служб шифрования, содержащий контейнер закрытого ключа. Требуется /k и /km (необязательно).</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">Sertifika dosyasının parolası.</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">Özel anahtar kapsayıcısını içeren Şifreleme Hizmeti Sağlayıcısı. /k ve alternatif olarak /km gerektirir.</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">证书文件的密码。</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">包含私钥容器的加密服务提供程序。需要 /k 和 /km (可选)。</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Sign.Cli/xlf/CertificateStoreResources.zh-Hant.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
<target state="translated">憑證檔案的密碼。</target>
<note />
</trans-unit>
<trans-unit id="ContainersDescription">
<source>Sign container contents</source>
<target state="new">Sign container contents</target>
<note />
</trans-unit>
<trans-unit id="CspOptionDescription">
<source>Cryptographic Service Provider containing the private key container. Requires /k and optionally /km.</source>
<target state="translated">包含私密金鑰容器的加密服務提供者。需要 /k 並選擇性地 /km。</target>
Expand Down
59 changes: 34 additions & 25 deletions src/Sign.Core/DataFormatSigners/AggregatingSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,38 @@ public async Task SignAsync(IEnumerable<FileInfo> files, SignOptions options)
ArgumentNullException.ThrowIfNull(files, nameof(files));
ArgumentNullException.ThrowIfNull(options, nameof(options));

if (options.RecurseContainers)
{
await SignContainerContentsAsync(files, options);
}

// split by code sign service and fallback to default

var grouped = (from signer in _signers
from file in files
where signer.CanSign(file)
group file by signer into groups
select groups).ToList();

// get all files and exclude existing;

// This is to catch PE files that don't have the correct extension set
var defaultFiles = files.Except(grouped.SelectMany(g => g))
.Where(_fileMetadataService.IsPortableExecutable)
.Select(f => new { _defaultSigner.Signer, f })
.GroupBy(a => a.Signer, k => k.f)
.SingleOrDefault(); // one group here

if (defaultFiles != null)
{
grouped.Add(defaultFiles);
}

await Task.WhenAll(grouped.Select(g => g.Key.SignAsync(g.ToList(), options)));
}

private async Task SignContainerContentsAsync(IEnumerable<FileInfo> files, SignOptions options)
{
// See if any of them are archives
List<FileInfo> archives = (from file in files
where _containerProvider.IsZipContainer(file) || _containerProvider.IsNuGetContainer(file)
Expand Down Expand Up @@ -178,32 +210,9 @@ where _containerProvider.IsAppxBundleContainer(file)
containers.ForEach(tz => tz.Dispose());
containers.Clear();
}

// split by code sign service and fallback to default

var grouped = (from signer in _signers
from file in files
where signer.CanSign(file)
group file by signer into groups
select groups).ToList();

// get all files and exclude existing;

// This is to catch PE files that don't have the correct extension set
var defaultFiles = files.Except(grouped.SelectMany(g => g))
.Where(_fileMetadataService.IsPortableExecutable)
.Select(f => new { _defaultSigner.Signer, f })
.GroupBy(a => a.Signer, k => k.f)
.SingleOrDefault(); // one group here

if (defaultFiles != null)
{
grouped.Add(defaultFiles);
}

await Task.WhenAll(grouped.Select(g => g.Key.SignAsync(g.ToList(), options)));
}


public void CopySigningDependencies(FileInfo file, DirectoryInfo destination, SignOptions options)
{
// pass the handling for this down to the actual implementations
Expand Down Expand Up @@ -240,4 +249,4 @@ private static IEnumerable<FileInfo> GetFiles(IContainer container, SignOptions
return files;
}
}
}
}
9 changes: 6 additions & 3 deletions src/Sign.Core/DataFormatSigners/SignOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal sealed class SignOptions
internal HashAlgorithmName FileHashAlgorithm { get; } = HashAlgorithmName.SHA256;
internal HashAlgorithmName TimestampHashAlgorithm { get; } = HashAlgorithmName.SHA256;
internal Uri TimestampService { get; }
internal bool RecurseContainers { get; }

internal SignOptions(
string? applicationName,
Expand All @@ -28,7 +29,8 @@ internal SignOptions(
HashAlgorithmName timestampHashAlgorithm,
Uri timestampService,
Matcher? matcher,
Matcher? antiMatcher)
Matcher? antiMatcher,
bool recurseContainers)
{
ApplicationName = applicationName;
PublisherName = publisherName;
Expand All @@ -39,13 +41,14 @@ internal SignOptions(
TimestampService = timestampService;
Matcher = matcher;
AntiMatcher = antiMatcher;
RecurseContainers = recurseContainers;
}

internal SignOptions(HashAlgorithmName fileHashAlgorithm, Uri timestampService)
: this(applicationName: null, publisherName: null, description: null, descriptionUrl: null,
fileHashAlgorithm, HashAlgorithmName.SHA256, timestampService, matcher: null,
antiMatcher: null)
antiMatcher: null, recurseContainers: true)
{
}
}
}
}
3 changes: 2 additions & 1 deletion src/Sign.Core/ISigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Task<int> SignAsync(
IReadOnlyList<FileInfo> inputFiles,
string? outputFile,
FileInfo? fileList,
bool recurseContainers,
DirectoryInfo baseDirectory,
string? applicationName,
string? publisherName,
Expand All @@ -22,4 +23,4 @@ Task<int> SignAsync(
HashAlgorithmName fileHashAlgorithm,
HashAlgorithmName timestampHashAlgorithm);
}
}
}
4 changes: 3 additions & 1 deletion src/Sign.Core/Signer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public async Task<int> SignAsync(
IReadOnlyList<FileInfo> inputFiles,
string? outputFile,
FileInfo? fileList,
bool recurseContainers,
DirectoryInfo baseDirectory,
string? applicationName,
string? publisherName,
Expand Down Expand Up @@ -70,7 +71,8 @@ public async Task<int> SignAsync(
timestampHashAlgorithm,
timestampUrl,
matcher,
antiMatcher);
antiMatcher,
recurseContainers);

try
{
Expand Down
5 changes: 4 additions & 1 deletion test/Sign.Cli.Test/TestInfrastructure/SignerSpy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal sealed class SignerSpy : ISigner
internal string? OutputFile { get; private set; }
internal FileInfo? FileList { get; private set; }
internal DirectoryInfo? BaseDirectory { get; private set; }
internal bool RecurseContainers { get; private set; }
internal string? ApplicationName { get; private set; }
internal string? PublisherName { get; private set; }
internal string? Description { get; private set; }
Expand All @@ -32,6 +33,7 @@ public Task<int> SignAsync(
IReadOnlyList<FileInfo> inputFiles,
string? outputFile,
FileInfo? fileList,
bool recurseContainers,
DirectoryInfo baseDirectory,
string? applicationName,
string? publisherName,
Expand All @@ -45,6 +47,7 @@ public Task<int> SignAsync(
InputFiles = inputFiles;
OutputFile = outputFile;
FileList = fileList;
RecurseContainers = recurseContainers;
BaseDirectory = baseDirectory;
ApplicationName = applicationName;
PublisherName = publisherName;
Expand All @@ -58,4 +61,4 @@ public Task<int> SignAsync(
return Task.FromResult(ExitCode);
}
}
}
}
Loading