Skip to content

Commit 11d195b

Browse files
Adding possibility to set SAN when using New-PnPAzureCertificate (#3555)
* Adding possibility to set the Subject Alternative Names on the self signed certificate * Adding changelog entry --------- Co-authored-by: Gautam Sheth <gautamdsheth@outlook.com>
1 parent 327a2ad commit 11d195b

File tree

7 files changed

+67
-19
lines changed

7 files changed

+67
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
4141
- Added `-RelativeUrl` parameter to `Connect-PnPOnline` cmdlet to allow specifying custom URLs for usage with `-WebLogin` method. [#3530](https://github.com/pnp/powershell/pull/3530)
4242
- Added `-RetryCount` to `Submit-PnPSearchQuery` which allows for specifying the number of retries to perform when an exception occurs [#3528](https://github.com/pnp/powershell/pull/3528)
4343
- Added `-MailNickname` parameter to `Set-PnPMicrosoft365Group` cmdlet to allow changing of this property on a Microsoft 365 Group [#3529](https://github.com/pnp/powershell/pull/3529)
44+
- Added `-SanNames` to `New-PnPAzureCertificate` which allows for controlling the Subject Alternative Names set on the generated certificate [#3555](https://github.com/pnp/powershell/pull/3555)
4445
- Added Information Barriers information to the output of `Get-PnPTenantSite` [#3556](https://github.com/pnp/powershell/pull/3556)
4546
- Added `RequestFilesLinkEnabled` and `RequestFilesLinkExpirationInDays` to the output of `Get-PnPSite` [#3557](https://github.com/pnp/powershell/pull/3557)
4647
- Added `CoreRequestFilesLinkEnabled`, `CoreRequestFilesLinkExpirationInDays`, `OneDriveRequestFilesLinkEnabled`, `OneDriveRequestFilesLinkExpirationInDays`, `BusinessConnectivityServiceDisabled` to the output of `Get-PnPTenant` [#3557](https://github.com/pnp/powershell/pull/3557)

documentation/New-PnPAzureCertificate.md

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ PrivateKey contains the PEM encoded private key of the certificate.
2525
```powershell
2626
New-PnPAzureCertificate [-CommonName <String>] [-Country <String>] [-State <String>]
2727
[-Locality <String>] [-Organization <String>] [-OrganizationUnit <String>] [-OutPfx <String>]
28-
[-OutCert <String>] [-ValidYears <Int32>] [-CertificatePassword <SecureString>] [-Store <StoreLocation>]
28+
[-OutCert <String>] [-ValidYears <Int32>] [-CertificatePassword <SecureString>] [-Store <StoreLocation>] [-SanNames <String[]>]
2929
```
3030

3131
## DESCRIPTION
3232

33-
Allows to create a self-signed certificate and manifest settings to be used with CSOM via an app-only ADAL application.
33+
Allows to create a self-signed certificate and manifest settings to be used with PnP PowerShell via an app-only application registration.
3434

3535
## EXAMPLES
3636

@@ -39,21 +39,28 @@ Allows to create a self-signed certificate and manifest settings to be used with
3939
New-PnPAzureCertificate -OutPfx pnp.pfx -OutCert pnp.cer
4040
```
4141

42-
This will generate a default self-signed certificate named "pnp.contoso.com" valid for 10 years and output a pfx and cer file to disk. The private key file (pfx) will not be password protected.
42+
This will generate a default self-signed certificate named "pnp.contoso.com" valid for 10 years and output a pfx and cer file to disk. The private key file (pfx) will not be password protected. It will have localhost and the machinename as the Subject Alternative Names.
4343

4444
### EXAMPLE 2
4545
```powershell
4646
New-PnPAzureCertificate -CommonName "My Certificate" -ValidYears 30
4747
```
4848

49-
This will output a certificate named "My Certificate" which expires in 30 years from now to the screen. It will not write the certificate files to disk.
49+
This will output a certificate named "My Certificate" which expires in 30 years from now to the screen. It will not write the certificate files to disk. It will have localhost and the machinename as the Subject Alternative Names.
5050

5151
### EXAMPLE 3
5252
```powershell
5353
New-PnPAzureCertificate -OutPfx pnp.pfx -OutCert pnp.cer -CertificatePassword (ConvertTo-SecureString -String "pass@word1" -AsPlainText -Force)
5454
```
5555

56-
This will generate a default self-signed certificate named "pnp.contoso.com" valid for 10 years and output a pfx and cer file to disk. The pfx file will have the password pass@word1 set on it.
56+
This will generate a default self-signed certificate named "pnp.contoso.com" valid for 10 years and output a pfx and cer file to disk. The pfx file will have the password pass@word1 set on it. It will have localhost and the machinename as the Subject Alternative Names.
57+
58+
### EXAMPLE 4
59+
```powershell
60+
New-PnPAzureCertificate -OutPfx pnp.pfx -OutCert pnp.cer -SanNames $null
61+
```
62+
63+
This will generate a default self-signed certificate named "pnp.contoso.com" valid for 10 years and output a pfx and cer file to disk. There will not be any Subject Alternative Names in the generated certificate.
5764

5865
## PARAMETERS
5966

@@ -169,6 +176,24 @@ Accept pipeline input: False
169176
Accept wildcard characters: False
170177
```
171178
179+
### -SanNames
180+
One or more DNS names to add to the certificate as Subject Alternative Names. Separate multiple names with a comma, i.e. "host1.domain.com","host2.domain.com".
181+
182+
Provide $null to not add any Subject Alternative names to the certificate.
183+
184+
Omit to add localhost and the machine name as Subject Alternative Names.
185+
186+
```yaml
187+
Type: String[]
188+
Parameter Sets: (All)
189+
190+
Required: False
191+
Position: Named
192+
Default value: None
193+
Accept pipeline input: False
194+
Accept wildcard characters: False
195+
```
196+
172197
### -State
173198
State or Province Name (full name)
174199

src/Commands/AzureAD/RegisterAzureADApp.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ private X509Certificate2 GetCertificate(PSObject record)
493493
}
494494
DateTime validFrom = DateTime.Today;
495495
DateTime validTo = validFrom.AddYears(ValidYears);
496-
cert = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo);
496+
cert = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo, Array.Empty<string>());
497497

498498
if (Directory.Exists(OutPath))
499499
{

src/Commands/Base/GetAzureCertificate.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
using System.Security;
44
using System.Security.Cryptography.X509Certificates;
55
using PnP.PowerShell.Commands.Utilities;
6+
using System.Runtime.ConstrainedExecution;
7+
using System.Security.Cryptography;
8+
using System.Linq;
69

710
namespace PnP.PowerShell.Commands.Base
811
{
@@ -84,7 +87,12 @@ internal static void WriteAzureCertificateOutput(PSCmdlet cmdlet, X509Certificat
8487
pfxBase64: pfxBase64,
8588
keyCredentials: manifestEntry,
8689
certificate: CertificateHelper.CertificateToBase64(certificate),
87-
privateKey: CertificateHelper.PrivateKeyToBase64(certificate)
90+
privateKey: CertificateHelper.PrivateKeyToBase64(certificate),
91+
sanNames: certificate.Extensions.Cast<X509Extension>()
92+
.Where(n => n.Oid.FriendlyName=="Subject Alternative Name")
93+
.Select(n => new AsnEncodedData(n.Oid, n.RawData))
94+
.Select(n => n.Format(false))
95+
.FirstOrDefault().Split(',', StringSplitOptions.TrimEntries)
8896
);
8997

9098
cmdlet.WriteObject(record);

src/Commands/Base/NewAzureCertificate.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public class NewPnPAdalCertificate : PSCmdlet
4646
[Parameter(Mandatory = false)]
4747
public StoreLocation Store;
4848

49+
[Parameter(Mandatory = false)]
50+
public string[] SanNames;
51+
4952
protected override void ProcessRecord()
5053
{
5154
if (MyInvocation.BoundParameters.ContainsKey(nameof(Store)) && !Utilities.OperatingSystem.IsWindows())
@@ -60,7 +63,12 @@ protected override void ProcessRecord()
6063
DateTime validFrom = DateTime.Today;
6164
DateTime validTo = validFrom.AddYears(ValidYears);
6265

63-
X509Certificate2 certificate = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo);
66+
if(MyInvocation.BoundParameters.ContainsKey(nameof(SanNames)) && SanNames == null)
67+
{
68+
SanNames = Array.Empty<string>();
69+
}
70+
71+
X509Certificate2 certificate = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo, SanNames);
6472

6573
if (!string.IsNullOrWhiteSpace(OutPfx))
6674
{
Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
1-
using PnP.PowerShell.Commands.Utilities;
2-
3-
using System;
4-
using System.Collections.Generic;
5-
using System.Security.Cryptography.X509Certificates;
6-
using System.Text;
1+
using System;
72

83
namespace PnP.PowerShell.Commands.Model
94
{
105
public sealed class AzureCertificate
116
{
12-
internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter, string thumbprint, string/*?*/ pfxBase64, string keyCredentials, string certificate, string privateKey)
7+
internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter, string thumbprint, string/*?*/ pfxBase64, string keyCredentials, string certificate, string privateKey, string[] sanNames)
138
{
149
Subject = subject ?? throw new ArgumentNullException(nameof(subject));
1510
NotBefore = notBefore;
@@ -19,6 +14,7 @@ internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter,
1914
KeyCredentials = keyCredentials ?? throw new ArgumentNullException(nameof(keyCredentials));
2015
Certificate = certificate ?? throw new ArgumentNullException(nameof(certificate));
2116
PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey));
17+
SanNames = sanNames;
2218
}
2319

2420
public string Subject { get; }
@@ -29,6 +25,6 @@ internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter,
2925
public string KeyCredentials { get; }
3026
public string Certificate { get; }
3127
public string PrivateKey { get; }
32-
28+
public string[] SanNames { get; }
3329
}
3430
}

src/Commands/Utilities/CertificateHelper.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,21 @@ private static IEnumerable<string> SplitText(string text, int length)
241241

242242
#endregion
243243

244-
internal static X509Certificate2 CreateSelfSignedCertificate(string commonName, string country, string state, string locality, string organization, string organizationUnit, SecureString password, string friendlyName, DateTimeOffset from, DateTimeOffset to)
244+
internal static X509Certificate2 CreateSelfSignedCertificate(string commonName, string country, string state, string locality, string organization, string organizationUnit, SecureString password, string friendlyName, DateTimeOffset from, DateTimeOffset to, string[] sanNames = null)
245245
{
246246
SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
247-
sanBuilder.AddDnsName("localhost");
248-
sanBuilder.AddDnsName(Environment.MachineName);
247+
if (sanNames != null)
248+
{
249+
foreach (var sanName in sanNames)
250+
{
251+
sanBuilder.AddDnsName(sanName);
252+
}
253+
}
254+
else
255+
{
256+
sanBuilder.AddDnsName("localhost");
257+
sanBuilder.AddDnsName(Environment.MachineName);
258+
}
249259

250260
var x500Values = new List<string>();
251261
if (!string.IsNullOrWhiteSpace(commonName)) x500Values.Add($"CN={commonName}");

0 commit comments

Comments
 (0)