Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PeterRevsbech-feature/add-X509KeyStorageFlags #4324

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
30 changes: 30 additions & 0 deletions documentation/Connect-PnPOnline.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,18 @@ Connects to the Azure AD with WAM (aka native Windows authentication prompt), ac

WAM is a more secure & faster way of authenticating in Windows OS. It supports Windows Hello, FIDO keys , conditional access policies and more.

### EXAMPLE 9
```powershell
$keyStorageflags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet

Connect-PnPOnline -Url "contoso.sharepoint.com" -ClientId 6c5c98c7-e05a-4a0f-bcfa-0cfc65aa1f28 -CertificateBase64Encoded $base64encodedstring -X509KeyStorageFlags $keyStorageflags -Tenant 'contoso.onmicrosoft.com'
```

Connects using an Azure Active Directory registered application using a certificate with a private key that has been base64 encoded.
See [Security App-only EntraId guidance](https://learn.microsoft.com/sharepoint/dev/solution-guidance/security-apponly-azuread) for a sample on how to get started.

See [X509 key storage flags](https://learn.microsoft.com/dotnet/api/system.security.cryptography.x509certificates.x509keystorageflags) for information on how to configure key storage when creating the certificate.

## PARAMETERS

### -AccessToken
Expand Down Expand Up @@ -874,6 +886,24 @@ Accept pipeline input: False
Accept wildcard characters: False
```

### -X509KeyStorageFlags

Defines where and how to import the private key of an X.509 certificate.

This enumeration supports a bitwise combination of its member values.

```yaml
Type: System.Security.Cryptography.X509Certificates.X509KeyStorageFlags
Parameter Sets: App-Only with Azure Active Directory
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```

## RELATED LINKS

[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
14 changes: 12 additions & 2 deletions src/Commands/Base/ConnectOnline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ public class ConnectOnline : BasePSCmdlet
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE)]
public string CertificateBase64Encoded;

[Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE)]
public X509KeyStorageFlags X509KeyStorageFlags;

[Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE)]
public SecureString CertificatePassword;

Expand Down Expand Up @@ -631,10 +634,11 @@ private PnPConnection ConnectAppOnlyWithCertificate()
throw new FileNotFoundException("Certificate not found");
}

X509Certificate2 certificate = CertificateHelper.GetCertificateFromPath(this, CertificatePath, CertificatePassword);
X509Certificate2 certificate = CertificateHelper.GetCertificateFromPath(this, CertificatePath, CertificatePassword, X509KeyStorageFlags);
if (Connection?.ClientId == ClientId &&
Connection?.Tenant == Tenant &&
Connection?.Certificate?.Thumbprint == certificate.Thumbprint)

{
ReuseAuthenticationManager();
}
Expand All @@ -644,7 +648,13 @@ private PnPConnection ConnectAppOnlyWithCertificate()
else if (ParameterSpecified(nameof(CertificateBase64Encoded)))
{
var certificateBytes = Convert.FromBase64String(CertificateBase64Encoded);
var certificate = new X509Certificate2(certificateBytes, CertificatePassword);
if (!ParameterSpecified(nameof(X509KeyStorageFlags)))
{
X509KeyStorageFlags = X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet;
}
var certificate = new X509Certificate2(certificateBytes, CertificatePassword, X509KeyStorageFlags);

if (Connection?.ClientId == ClientId &&
Connection?.Tenant == Tenant &&
Expand Down
12 changes: 8 additions & 4 deletions src/Commands/Utilities/CertificateHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,15 @@ internal static X509Certificate2 GetCertificateFromStore(string thumbprint)
/// <param name="cmdlet">Cmdlet executing this function</param>
/// <param name="certificatePath">Path to the private key certificate file</param>
/// <param name="certificatePassword">Password to open the certificate or NULL if no password set on the certificate</param>
/// <param name="x509KeyStorageFlags">Key storage flags for created X509Certificate2</param>
/// <returns>X509Certificate2 instance</returns>
/// <exception cref="PSArgumentException">Thrown if the certificate cannot be read</exception>
/// <exception cref="FileNotFoundException">Thrown if the certificate cannot be found at the provided path</exception>
internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string certificatePath, SecureString certificatePassword)
internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string certificatePath, SecureString certificatePassword,
X509KeyStorageFlags x509KeyStorageFlags =
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet)
{
if (System.IO.File.Exists(certificatePath))
{
Expand All @@ -152,9 +157,8 @@ internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string ce
var certificate = new X509Certificate2(
certificateBytes,
certificatePassword,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
x509KeyStorageFlags
);
return certificate;
}
catch (CryptographicException e)
Expand Down
Loading