Skip to content

Commit

Permalink
Adding possibility to set SAN when using New-PnPAzureCertificate (#…
Browse files Browse the repository at this point in the history
…3555)

* Adding possibility to set the Subject Alternative Names on the self signed certificate

* Adding changelog entry

---------

Co-authored-by: Gautam Sheth <[email protected]>
  • Loading branch information
KoenZomers and gautamdsheth authored Nov 10, 2023
1 parent 327a2ad commit 11d195b
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- 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)
- 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)
- 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)
- 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)
- Added Information Barriers information to the output of `Get-PnPTenantSite` [#3556](https://github.com/pnp/powershell/pull/3556)
- Added `RequestFilesLinkEnabled` and `RequestFilesLinkExpirationInDays` to the output of `Get-PnPSite` [#3557](https://github.com/pnp/powershell/pull/3557)
- Added `CoreRequestFilesLinkEnabled`, `CoreRequestFilesLinkExpirationInDays`, `OneDriveRequestFilesLinkEnabled`, `OneDriveRequestFilesLinkExpirationInDays`, `BusinessConnectivityServiceDisabled` to the output of `Get-PnPTenant` [#3557](https://github.com/pnp/powershell/pull/3557)
Expand Down
35 changes: 30 additions & 5 deletions documentation/New-PnPAzureCertificate.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ PrivateKey contains the PEM encoded private key of the certificate.
```powershell
New-PnPAzureCertificate [-CommonName <String>] [-Country <String>] [-State <String>]
[-Locality <String>] [-Organization <String>] [-OrganizationUnit <String>] [-OutPfx <String>]
[-OutCert <String>] [-ValidYears <Int32>] [-CertificatePassword <SecureString>] [-Store <StoreLocation>]
[-OutCert <String>] [-ValidYears <Int32>] [-CertificatePassword <SecureString>] [-Store <StoreLocation>] [-SanNames <String[]>]
```

## DESCRIPTION

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

## EXAMPLES

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

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.
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.

### EXAMPLE 2
```powershell
New-PnPAzureCertificate -CommonName "My Certificate" -ValidYears 30
```

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.
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.

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

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.
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.

### EXAMPLE 4
```powershell
New-PnPAzureCertificate -OutPfx pnp.pfx -OutCert pnp.cer -SanNames $null
```

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.

## PARAMETERS

Expand Down Expand Up @@ -169,6 +176,24 @@ Accept pipeline input: False
Accept wildcard characters: False
```
### -SanNames
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".
Provide $null to not add any Subject Alternative names to the certificate.
Omit to add localhost and the machine name as Subject Alternative Names.
```yaml
Type: String[]
Parameter Sets: (All)

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -State
State or Province Name (full name)
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/AzureAD/RegisterAzureADApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ private X509Certificate2 GetCertificate(PSObject record)
}
DateTime validFrom = DateTime.Today;
DateTime validTo = validFrom.AddYears(ValidYears);
cert = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo);
cert = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo, Array.Empty<string>());

if (Directory.Exists(OutPath))
{
Expand Down
10 changes: 9 additions & 1 deletion src/Commands/Base/GetAzureCertificate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using System.Security;
using System.Security.Cryptography.X509Certificates;
using PnP.PowerShell.Commands.Utilities;
using System.Runtime.ConstrainedExecution;
using System.Security.Cryptography;
using System.Linq;

namespace PnP.PowerShell.Commands.Base
{
Expand Down Expand Up @@ -84,7 +87,12 @@ internal static void WriteAzureCertificateOutput(PSCmdlet cmdlet, X509Certificat
pfxBase64: pfxBase64,
keyCredentials: manifestEntry,
certificate: CertificateHelper.CertificateToBase64(certificate),
privateKey: CertificateHelper.PrivateKeyToBase64(certificate)
privateKey: CertificateHelper.PrivateKeyToBase64(certificate),
sanNames: certificate.Extensions.Cast<X509Extension>()
.Where(n => n.Oid.FriendlyName=="Subject Alternative Name")
.Select(n => new AsnEncodedData(n.Oid, n.RawData))
.Select(n => n.Format(false))
.FirstOrDefault().Split(',', StringSplitOptions.TrimEntries)
);

cmdlet.WriteObject(record);
Expand Down
10 changes: 9 additions & 1 deletion src/Commands/Base/NewAzureCertificate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public class NewPnPAdalCertificate : PSCmdlet
[Parameter(Mandatory = false)]
public StoreLocation Store;

[Parameter(Mandatory = false)]
public string[] SanNames;

protected override void ProcessRecord()
{
if (MyInvocation.BoundParameters.ContainsKey(nameof(Store)) && !Utilities.OperatingSystem.IsWindows())
Expand All @@ -60,7 +63,12 @@ protected override void ProcessRecord()
DateTime validFrom = DateTime.Today;
DateTime validTo = validFrom.AddYears(ValidYears);

X509Certificate2 certificate = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo);
if(MyInvocation.BoundParameters.ContainsKey(nameof(SanNames)) && SanNames == null)
{
SanNames = Array.Empty<string>();
}

X509Certificate2 certificate = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo, SanNames);

if (!string.IsNullOrWhiteSpace(OutPfx))
{
Expand Down
12 changes: 4 additions & 8 deletions src/Commands/Model/AzureCertificate.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
using PnP.PowerShell.Commands.Utilities;

using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System;

namespace PnP.PowerShell.Commands.Model
{
public sealed class AzureCertificate
{
internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter, string thumbprint, string/*?*/ pfxBase64, string keyCredentials, string certificate, string privateKey)
internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter, string thumbprint, string/*?*/ pfxBase64, string keyCredentials, string certificate, string privateKey, string[] sanNames)
{
Subject = subject ?? throw new ArgumentNullException(nameof(subject));
NotBefore = notBefore;
Expand All @@ -19,6 +14,7 @@ internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter,
KeyCredentials = keyCredentials ?? throw new ArgumentNullException(nameof(keyCredentials));
Certificate = certificate ?? throw new ArgumentNullException(nameof(certificate));
PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey));
SanNames = sanNames;
}

public string Subject { get; }
Expand All @@ -29,6 +25,6 @@ internal AzureCertificate(string subject, DateTime notBefore, DateTime notAfter,
public string KeyCredentials { get; }
public string Certificate { get; }
public string PrivateKey { get; }

public string[] SanNames { get; }
}
}
16 changes: 13 additions & 3 deletions src/Commands/Utilities/CertificateHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,21 @@ private static IEnumerable<string> SplitText(string text, int length)

#endregion

internal static X509Certificate2 CreateSelfSignedCertificate(string commonName, string country, string state, string locality, string organization, string organizationUnit, SecureString password, string friendlyName, DateTimeOffset from, DateTimeOffset to)
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)
{
SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");
sanBuilder.AddDnsName(Environment.MachineName);
if (sanNames != null)
{
foreach (var sanName in sanNames)
{
sanBuilder.AddDnsName(sanName);
}
}
else
{
sanBuilder.AddDnsName("localhost");
sanBuilder.AddDnsName(Environment.MachineName);
}

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

0 comments on commit 11d195b

Please sign in to comment.