diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md
index 27cddc667..a44eced5b 100644
--- a/documentation/Connect-PnPOnline.md
+++ b/documentation/Connect-PnPOnline.md
@@ -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
@@ -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)
diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs
index 6b12588f2..6c532b97c 100644
--- a/src/Commands/Base/ConnectOnline.cs
+++ b/src/Commands/Base/ConnectOnline.cs
@@ -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;
@@ -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();
}
@@ -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 &&
diff --git a/src/Commands/Utilities/CertificateHelper.cs b/src/Commands/Utilities/CertificateHelper.cs
index cbb4a9a95..66c8956cb 100644
--- a/src/Commands/Utilities/CertificateHelper.cs
+++ b/src/Commands/Utilities/CertificateHelper.cs
@@ -128,10 +128,15 @@ internal static X509Certificate2 GetCertificateFromStore(string thumbprint)
/// Cmdlet executing this function
/// Path to the private key certificate file
/// Password to open the certificate or NULL if no password set on the certificate
+ /// Key storage flags for created X509Certificate2
/// X509Certificate2 instance
/// Thrown if the certificate cannot be read
/// Thrown if the certificate cannot be found at the provided path
- 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))
{
@@ -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)