Skip to content

Commit 9c6f4d2

Browse files
JaxelrVeryEarly
andauthored
Add get cert chain operation to Az.CodeSigning preview module (#25528)
* feat: initial implementation of the get certificate chain method * fix: root cert operation should return PSSigningCertificate type * test: added get test chain certificate function * docs: added help sample file * docs: added link to new cert chain operation Also fixed minor typo * docs: updated changelog.md * fix: static analyzer failure * Update ChangeLog.md * docs: fix markdown parsing for cert chain operation --------- Co-authored-by: Yabo Hu <[email protected]>
1 parent 5103565 commit 9c6f4d2

14 files changed

+379
-21
lines changed

src/CodeSigning/CodeSigning.Test/ScenarioTests/CodeSigningTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,12 @@ public void TestGetSigningRootCertificate()
3636
{
3737
TestRunner.RunTestScript("Test-GetCodeSigningRootCert");
3838
}
39+
40+
[Fact]
41+
[Trait(Category.AcceptanceType, Category.LiveOnly)]
42+
public void TestGetSigningCertificateChain()
43+
{
44+
TestRunner.RunTestScript("Test-GetCodeSigningCertChain");
45+
}
3946
}
4047
}

src/CodeSigning/CodeSigning.Test/ScenarioTests/CodeSigningTests.ps1

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,26 @@ function Test-GetCodeSigningRootCert {
5252

5353
finally {
5454

55+
}
56+
}
57+
58+
<#
59+
.SYNOPSIS
60+
Test codesigning command to get the certificate chain from the certificate profile
61+
#>
62+
function Test-GetCodeSigningCertChain {
63+
$accountName = "acs-test-account"
64+
$profileName = "acs-test-account-ci"
65+
$endPointUrl = "https://scus.codesigning.azure.net/"
66+
$destination = "C:\temp"
67+
68+
try {
69+
# Test Get CodeSigning Certificate Chain
70+
$chain = Get-AzCodeSigningCertChain -AccountName $accountName -ProfileName $profileName -EndpointUrl $endPointUrl -Destination $destination
71+
Assert-NotNullOrEmpty $chain
72+
}
73+
74+
finally {
75+
5576
}
5677
}

src/CodeSigning/CodeSigning/Az.CodeSigning.psd1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ DotNetFrameworkVersion = '4.7.2'
5454
RequiredModules = @(@{ModuleName = 'Az.Accounts'; ModuleVersion = '3.0.1'; })
5555

5656
# Assemblies that must be loaded prior to importing this module
57-
RequiredAssemblies = 'Azure.CodeSigning.Client.CryptoProvider.dll',
58-
'Azure.CodeSigning.Client.CryptoProvider.Models.dll',
59-
'Azure.CodeSigning.Client.CryptoProvider.Utilities.dll',
57+
RequiredAssemblies = 'Azure.CodeSigning.Client.CryptoProvider.dll',
58+
'Azure.CodeSigning.Client.CryptoProvider.Models.dll',
59+
'Azure.CodeSigning.Client.CryptoProvider.Utilities.dll',
6060
'Azure.CodeSigning.dll', 'Polly.dll'
6161

6262
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
@@ -75,7 +75,7 @@ NestedModules = @('Microsoft.Azure.PowerShell.Cmdlets.CodeSigning.dll')
7575
FunctionsToExport = @()
7676

7777
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
78-
CmdletsToExport = 'Get-AzCodeSigningCustomerEku', 'Get-AzCodeSigningRootCert',
78+
CmdletsToExport = 'Get-AzCodeSigningCustomerEku', 'Get-AzCodeSigningRootCert', 'Get-AzCodeSigningCertChain',
7979
'Invoke-AzCodeSigningCIPolicySigning'
8080

8181
# Variables to export from this module

src/CodeSigning/CodeSigning/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- Additional information about change #1
1919
-->
2020
## Upcoming Release
21+
* Added `Get-AzCodeSigningCertChain` cmdlet to retrieve the certificate chain for a certificate profile.
2122

2223
## Version 0.1.2
2324
* Updated signed 3rd party assembly Polly.dll to PSGallery

src/CodeSigning/CodeSigning/CodeSigning.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<ItemGroup>
2525
<PackageReference Include="Polly" Version="7.2.4" />
2626
<PackageReference Include="Azure.CodeSigning.Client.CryptoProvider" Version="0.1.16" />
27+
<PackageReference Include="Azure.CodeSigning.Sdk" Version="0.1.106" />
2728
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="6.0.3" />
2829
</ItemGroup>
2930

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.Azure.Commands.CodeSigning.Models;
16+
using System.Collections.Generic;
17+
using System.IO;
18+
using System.Management.Automation;
19+
using System.Security.Cryptography.X509Certificates;
20+
21+
namespace Microsoft.Azure.Commands.CodeSigning
22+
{
23+
[Cmdlet("Get", ResourceManager.Common.AzureRMConstants.AzurePrefix + "CodeSigningCertChain", DefaultParameterSetName = ByAccountProfileNameParameterSet)]
24+
[OutputType(typeof(IEnumerable<PSSigningCertificate>))]
25+
public class GetAzureCodeSigningCertChain : CodeSigningCmdletBase
26+
{
27+
#region Parameter Set Names
28+
29+
private const string ByAccountProfileNameParameterSet = "ByAccountProfileNameParameterSet";
30+
private const string ByMetadataFileParameterSet = "ByMetadataFileParameterSet";
31+
32+
#endregion
33+
34+
#region Input Parameter Definitions
35+
36+
/// <summary>
37+
/// Account Profile name
38+
/// </summary>
39+
[Parameter(Mandatory = true,
40+
Position = 0,
41+
ParameterSetName = ByAccountProfileNameParameterSet,
42+
ValueFromPipelineByPropertyName = true,
43+
HelpMessage = "The account name of Azure CodeSigning.")]
44+
[ValidateNotNullOrEmpty]
45+
public string AccountName { get; set; }
46+
47+
[Parameter(Mandatory = true,
48+
Position = 1,
49+
ParameterSetName = ByAccountProfileNameParameterSet,
50+
ValueFromPipelineByPropertyName = true,
51+
HelpMessage = "The certificate profile name of Azure CodeSigning account.")]
52+
[ValidateNotNullOrEmpty()]
53+
public string ProfileName { get; set; }
54+
[Parameter(Mandatory = true,
55+
Position = 2,
56+
ParameterSetName = ByAccountProfileNameParameterSet,
57+
ValueFromPipelineByPropertyName = true,
58+
HelpMessage = "The endpoint url used to submit request to Azure CodeSigning.")]
59+
public string EndpointUrl { get; set; }
60+
61+
62+
/// <summary>
63+
/// Metadata File Path
64+
/// </summary>
65+
[Parameter(Mandatory = true,
66+
Position = 0,
67+
ParameterSetName = ByMetadataFileParameterSet,
68+
ValueFromPipelineByPropertyName = true,
69+
HelpMessage = "Metadata File path. Cmdlet constructs the FQDN of an account profile based on the Metadata File and currently selected environment.")]
70+
[ValidateNotNullOrEmpty]
71+
public string MetadataFilePath { get; set; }
72+
73+
[Parameter(Mandatory = true,
74+
Position = 3,
75+
ParameterSetName = ByAccountProfileNameParameterSet,
76+
ValueFromPipelineByPropertyName = true,
77+
HelpMessage = "Downloaded Root Cert file full path, including file name")]
78+
[Parameter(Mandatory = true,
79+
Position = 1,
80+
ParameterSetName = ByMetadataFileParameterSet,
81+
ValueFromPipelineByPropertyName = true,
82+
HelpMessage = "Downloaded Root Cert file full path, including file name")]
83+
[ValidateNotNullOrEmpty]
84+
public string Destination { get; set; }
85+
#endregion
86+
87+
public override void ExecuteCmdlet()
88+
{
89+
Stream certchain;
90+
91+
if (!string.IsNullOrEmpty(AccountName))
92+
{
93+
certchain = CodeSigningServiceClient.GetCodeSigningCertChain(AccountName, ProfileName, EndpointUrl);
94+
WriteCertChain(certchain);
95+
}
96+
else if (!string.IsNullOrEmpty(MetadataFilePath))
97+
{
98+
certchain = CodeSigningServiceClient.GetCodeSigningCertChain(MetadataFilePath);
99+
WriteCertChain(certchain);
100+
}
101+
}
102+
103+
private void WriteCertChain(Stream certchain)
104+
{
105+
var downloadPath = ResolvePath(Destination);
106+
107+
var fileStream = new FileStream(downloadPath, FileMode.Create, FileAccess.Write);
108+
certchain.CopyTo(fileStream);
109+
fileStream.Dispose();
110+
111+
byte[] rawData = File.ReadAllBytes(downloadPath);
112+
113+
var chain = new X509Certificate2Collection();
114+
chain.Import(rawData);
115+
116+
WriteObject(downloadPath.Replace("\\", @"\"));
117+
118+
var pschain = new List<PSSigningCertificate>();
119+
120+
foreach (var cert in chain)
121+
{
122+
var pscert = new PSSigningCertificate()
123+
{
124+
Issuer = cert.Issuer,
125+
Subject = cert.Subject,
126+
Thumbprint = cert.Thumbprint
127+
};
128+
129+
pschain.Add(pscert);
130+
}
131+
132+
WriteObject(pschain);
133+
}
134+
}
135+
}

src/CodeSigning/CodeSigning/Commands/GetAzureCodeSigningRootCert.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
namespace Microsoft.Azure.Commands.CodeSigning
2121
{
2222
[Cmdlet("Get", ResourceManager.Common.AzureRMConstants.AzurePrefix + "CodeSigningRootCert", DefaultParameterSetName = ByAccountProfileNameParameterSet)]
23-
[OutputType(typeof(string))]
23+
[OutputType(typeof(PSSigningCertificate))]
2424
public class GetAzureCodeSigningRootCert : CodeSigningCmdletBase
2525
{
2626
#region Parameter Set Names
@@ -115,7 +115,8 @@ private void WriteRootCert(Stream rootcert)
115115
PSSigningCertificate pscert = new PSSigningCertificate
116116
{
117117
Subject = x509.Subject,
118-
Thumbprint = x509.Thumbprint
118+
Thumbprint = x509.Thumbprint,
119+
Issuer = x509.Issuer
119120
};
120121

121122
WriteObject(pscert, false);

src/CodeSigning/CodeSigning/Models/CodeSigningServiceClient.cs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
// limitations under the License.
1313
// ----------------------------------------------------------------------------------
1414

15-
using System;
1615
using Azure.CodeSigning;
17-
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
18-
using System.IO;
1916
using Azure.Core;
2017
using Microsoft.Azure.Commands.CodeSigning.Helpers;
18+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
2119
using Newtonsoft.Json;
20+
using System;
21+
using System.IO;
2222
using System.Linq;
2323

2424
namespace Microsoft.Azure.Commands.CodeSigning.Models
@@ -52,14 +52,6 @@ public CodeSigningServiceClient(IAuthenticationFactory authFactory, IAzureContex
5252
private Exception GetInnerException(Exception exception)
5353
{
5454
while (exception.InnerException != null) exception = exception.InnerException;
55-
//if need modify inner exception
56-
//if (exception is KeyVaultErrorException kvEx && kvEx?.Body?.Error != null)
57-
//{
58-
// var detailedMsg = exception.Message;
59-
// detailedMsg += string.Format(Environment.NewLine + "Code: {0}", kvEx.Body.Error.Code);
60-
// detailedMsg += string.Format(Environment.NewLine + "Message: {0}", kvEx.Body.Error.Message);
61-
// exception = new KeyVaultErrorException(detailedMsg, kvEx);
62-
//}
6355
return exception;
6456
}
6557

@@ -120,11 +112,31 @@ public Stream GetCodeSigningRootCert(string metadataPath)
120112
return GetCodeSigningRootCert(accountName, profileName, endpoint);
121113
}
122114

115+
public Stream GetCodeSigningCertChain(string accountName, string profileName, string endpoint)
116+
{
117+
GetCertificateProfileClient(endpoint);
118+
119+
var certChain = CertificateProfileClient.GetSignCertificateChain(accountName, profileName);
120+
return certChain;
121+
}
122+
123+
public Stream GetCodeSigningCertChain(string metadataPath)
124+
{
125+
var rawMetadata = File.ReadAllText(metadataPath);
126+
Metadata = JsonConvert.DeserializeObject<Metadata>(rawMetadata);
127+
128+
var accountName = Metadata.CodeSigningAccountName;
129+
var profileName = Metadata.CertificateProfileName;
130+
var endpoint = Metadata.Endpoint;
131+
132+
return GetCodeSigningCertChain(accountName, profileName, endpoint);
133+
}
134+
123135
public void SubmitCIPolicySigning(string accountName, string profileName, string endpoint,
124136
string unsignedCIFilePath, string signedCIFilePath, string timeStamperUrl = null)
125137
{
126-
var cipolicySigner = new CmsSigner();
127-
cipolicySigner.SignCIPolicy(user_creds, accountName, profileName, endpoint, unsignedCIFilePath, signedCIFilePath, timeStamperUrl);
138+
var cipolicySigner = new CmsSigner();
139+
cipolicySigner.SignCIPolicy(user_creds, accountName, profileName, endpoint, unsignedCIFilePath, signedCIFilePath, timeStamperUrl);
128140
}
129141

130142
public void SubmitCIPolicySigning(string metadataPath, string unsignedCIFilePath, string signedCIFilePath, string timeStamperUrl)

src/CodeSigning/CodeSigning/Models/ICodeSigningServiceClient.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ public interface ICodeSigningServiceClient
2626

2727
Stream GetCodeSigningRootCert(string metadataPath);
2828

29+
Stream GetCodeSigningCertChain(string accountName, string profileName, string endpoint);
30+
31+
Stream GetCodeSigningCertChain(string metadataPath);
32+
2933
void SubmitCIPolicySigning(string accountName, string profileName, string endpoint,
3034
string unsignedCIFilePath, string signedCIFilePath, string timeStamperUrl);
3135
void SubmitCIPolicySigning(string metadataPath,

src/CodeSigning/CodeSigning/Models/PSSigningCertificate.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ internal class PSSigningCertificate
1818
{
1919
public string Thumbprint { get; set; }
2020
public string Subject { get; set; }
21+
public string Issuer { get; set; }
2122
}
2223
}

0 commit comments

Comments
 (0)