Skip to content

Commit c65a3bb

Browse files
author
Gautam Sheth
committed
Refactor certificate handling to use UserKeySet for X509KeyStorageFlags and remove local admin requirement for cmdlets
1 parent aae61ff commit c65a3bb

8 files changed

+30
-43
lines changed

src/Commands/AzureAD/RegisterAzureADApp.cs

+1-9
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,6 @@ public class RegisterAzureADApp : BasePSCmdlet, IDynamicParameters
9494

9595
protected override void ProcessRecord()
9696
{
97-
if (!PSUtility.IsUserLocalAdmin())
98-
{
99-
throw new PSArgumentException("Running this cmdlet requires elevated permissions (Run as Admin) to generate a certificate.");
100-
}
101-
10297
if (ParameterSpecified(nameof(Store)) && !OperatingSystem.IsWindows())
10398
{
10499
throw new PSArgumentException("The Store parameter is only supported on Microsoft Windows");
@@ -470,7 +465,7 @@ private X509Certificate2 GetCertificate(PSObject record)
470465

471466
try
472467
{
473-
cert = new X509Certificate2(CertificatePath, CertificatePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
468+
cert = new X509Certificate2(CertificatePath, CertificatePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
474469
}
475470
catch (CryptographicException e) when (e.Message.Contains("The specified password is not correct"))
476471
{
@@ -658,7 +653,6 @@ private void StartConsentFlow(string loginEndPoint, AzureADApp azureApp, string
658653
progressRecord.RecordType = ProgressRecordType.Completed;
659654
WriteProgress(progressRecord);
660655

661-
662656
if (!Stopping)
663657
{
664658
if (ParameterSpecified(nameof(DeviceLogin)))
@@ -685,9 +679,7 @@ private void StartConsentFlow(string loginEndPoint, AzureADApp azureApp, string
685679
authManager.ClearTokenCache();
686680
authManager.GetAccessToken(resource, Microsoft.Identity.Client.Prompt.Consent);
687681
}
688-
689682
}
690-
WriteObject(record);
691683
}
692684

693685
WriteObject(record);

src/Commands/AzureAD/RegisterEntraIDAppForInteractiveLogin.cs

-5
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ public class RegisterEntraIDAppForInteractiveLogin : BasePSCmdlet, IDynamicParam
4949

5050
protected override void ProcessRecord()
5151
{
52-
if (!PSUtility.IsUserLocalAdmin())
53-
{
54-
throw new PSArgumentException("Running this cmdlet requires elevated permissions (Run as Admin) to generate a certificate.");
55-
}
56-
5752
var redirectUri = "http://localhost";
5853
// if (ParameterSpecified(nameof(DeviceLogin)) || OperatingSystem.IsMacOS())
5954
if (ParameterSpecified(nameof(DeviceLogin)) || OperatingSystem.IsMacOS())

src/Commands/Base/ConnectOnline.cs

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using PnP.PowerShell.Commands.Base.PipeBinds;
1+
using Microsoft.SharePoint.Client;
2+
using PnP.Framework;
3+
using PnP.PowerShell.Commands.Base.PipeBinds;
24
using PnP.PowerShell.Commands.Enums;
35
using PnP.PowerShell.Commands.Model;
46
using PnP.PowerShell.Commands.Provider;
@@ -13,9 +15,7 @@
1315
using System.Threading;
1416
using System.Threading.Tasks;
1517
using File = System.IO.File;
16-
using PnP.Framework;
1718
using Resources = PnP.PowerShell.Commands.Properties.Resources;
18-
using Microsoft.SharePoint.Client;
1919

2020
namespace PnP.PowerShell.Commands.Base
2121
{
@@ -591,7 +591,7 @@ private PnPConnection ConnectAppOnlyWithCertificate()
591591
if (!ParameterSpecified(nameof(X509KeyStorageFlags)))
592592
{
593593
X509KeyStorageFlags = X509KeyStorageFlags.Exportable |
594-
X509KeyStorageFlags.MachineKeySet |
594+
X509KeyStorageFlags.UserKeySet |
595595
X509KeyStorageFlags.PersistKeySet;
596596
}
597597

@@ -612,7 +612,7 @@ private PnPConnection ConnectAppOnlyWithCertificate()
612612
if (!ParameterSpecified(nameof(X509KeyStorageFlags)))
613613
{
614614
X509KeyStorageFlags = X509KeyStorageFlags.Exportable |
615-
X509KeyStorageFlags.MachineKeySet |
615+
X509KeyStorageFlags.UserKeySet |
616616
X509KeyStorageFlags.PersistKeySet;
617617
}
618618
var certificate = new X509Certificate2(certificateBytes, CertificatePassword, X509KeyStorageFlags);
@@ -821,7 +821,7 @@ private PnPConnection ConnectEnvironmentVariable(InitializationType initializati
821821
if (!ParameterSpecified(nameof(X509KeyStorageFlags)))
822822
{
823823
X509KeyStorageFlags = X509KeyStorageFlags.Exportable |
824-
X509KeyStorageFlags.MachineKeySet |
824+
X509KeyStorageFlags.UserKeySet |
825825
X509KeyStorageFlags.PersistKeySet;
826826
}
827827

@@ -984,8 +984,6 @@ private PSCredential GetCredentials()
984984
private string GetAppId()
985985
{
986986
var connectionUri = new Uri(Url);
987-
988-
989987
// Try to get the credentials by full url
990988
string appId = PnPConnection.GetCacheClientId(connectionUri.ToString()) ?? Utilities.CredentialManager.GetAppId(connectionUri.ToString());
991989
if (appId == null)

src/Commands/Base/GetAzureCertificate.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using System.Management.Automation;
1+
using PnP.PowerShell.Commands.Utilities;
22
using System;
3+
using System.Linq;
4+
using System.Management.Automation;
35
using System.Security;
4-
using System.Security.Cryptography.X509Certificates;
5-
using PnP.PowerShell.Commands.Utilities;
66
using System.Security.Cryptography;
7-
using System.Linq;
7+
using System.Security.Cryptography.X509Certificates;
88

99
namespace PnP.PowerShell.Commands.Base
1010
{
@@ -28,7 +28,7 @@ protected override void ProcessRecord()
2828
}
2929
if (System.IO.File.Exists(Path))
3030
{
31-
var certificate = new X509Certificate2(Path, Password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
31+
var certificate = new X509Certificate2(Path, Password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet);
3232
WriteAzureCertificateOutput(this, certificate, Password);
3333
}
3434
else
@@ -88,7 +88,7 @@ internal static void WriteAzureCertificateOutput(BasePSCmdlet cmdlet, X509Certif
8888
certificate: CertificateHelper.CertificateToBase64(certificate),
8989
privateKey: CertificateHelper.PrivateKeyToBase64(certificate),
9090
sanNames: certificate.Extensions.Cast<X509Extension>()
91-
.Where(n => n.Oid.FriendlyName=="Subject Alternative Name")
91+
.Where(n => n.Oid.FriendlyName == "Subject Alternative Name")
9292
.Select(n => new AsnEncodedData(n.Oid, n.RawData))
9393
.Select(n => n.Format(false))
9494
.FirstOrDefault().Split(',', StringSplitOptions.TrimEntries)

src/Commands/Base/NewAzureCertificate.cs

-5
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,6 @@ protected override void ProcessRecord()
5454
throw new PSArgumentException("The Store parameter is only supported on Microsoft Windows");
5555
}
5656

57-
if (!PSUtility.IsUserLocalAdmin())
58-
{
59-
throw new PSArgumentException("Running this cmdlet requires elevated permissions (Run as Admin) to generate a certificate.");
60-
}
61-
6257
if (ValidYears < 1 || ValidYears > 30)
6358
{
6459
ValidYears = 10;

src/Commands/PnP.PowerShell.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
<PackageReference Include="PnP.Core.Admin" Version="1.14.*-*" Condition="'$(IsRelease)' == '1'" />
6767

6868
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.70.2" />
69+
<PackageReference Include="Microsoft.Bcl.Cryptography" Version="9.0.2" />
6970

7071
<ProjectReference Include="..\ALC\PnP.PowerShell.ALC.csproj" />
7172

src/Commands/Utilities/CertificateHelper.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ internal static X509Certificate2 GetCertificateFromStore(string thumbprint)
129129
internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string certificatePath, SecureString certificatePassword,
130130
X509KeyStorageFlags x509KeyStorageFlags =
131131
X509KeyStorageFlags.Exportable |
132-
X509KeyStorageFlags.MachineKeySet |
132+
X509KeyStorageFlags.UserKeySet |
133133
X509KeyStorageFlags.PersistKeySet)
134134
{
135135
if (System.IO.File.Exists(certificatePath))
@@ -268,7 +268,7 @@ internal static X509Certificate2 CreateSelfSignedCertificate(string commonName,
268268
X500DistinguishedName distinguishedName = new X500DistinguishedName(distinguishedNameString);
269269

270270
#pragma warning disable CA1416 // Validate platform compatibility
271-
using (RSA rsa = Platform.IsWindows ? MakeExportable(new RSACng(2048)) : RSA.Create(2048))
271+
using (RSA rsa = RSA.Create(2048))
272272
{
273273
var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
274274

@@ -287,8 +287,14 @@ internal static X509Certificate2 CreateSelfSignedCertificate(string commonName,
287287
{
288288
certificate.FriendlyName = friendlyName;
289289
}
290+
string passString = password != null ? CredentialManager.SecureStringToString(password) : null;
290291

291-
return new X509Certificate2(certificate.Export(X509ContentType.Pfx, password), password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
292+
if (PSUtility.PSVersion == "7.5")
293+
{
294+
return X509CertificateLoader.LoadPkcs12(certificate.Export(X509ContentType.Pfx, password), passString, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
295+
}
296+
297+
return new X509Certificate2(certificate.Export(X509ContentType.Pfx, password), password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
292298
}
293299
#pragma warning restore CA1416 // Validate platform compatibility
294300
}

src/Commands/Utilities/CredentialManager.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System;
1+
using Microsoft.Identity.Client.Extensions.Msal;
2+
using Microsoft.Win32.SafeHandles;
3+
using PnP.Framework.Modernization.Cache;
4+
using System;
25
using System.Linq;
36
using System.Management.Automation;
47
using System.Management.Automation.Runspaces;
@@ -7,9 +10,6 @@
710
using System.Runtime.InteropServices;
811
using System.Security;
912
using System.Text;
10-
using Microsoft.Identity.Client.Extensions.Msal;
11-
using Microsoft.Win32.SafeHandles;
12-
using PnP.Framework.Modernization.Cache;
1313
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
1414

1515
[assembly: InternalsVisibleTo("PnP.PowerShell.Tests")]
@@ -498,7 +498,7 @@ private static PSCredential ReadMacOSKeyChainEntry(string applicationName)
498498
}
499499
return null;
500500
}
501-
private static void WriteMacOSKeyChainEntry(string applicationName,string password)
501+
private static void WriteMacOSKeyChainEntry(string applicationName, string password)
502502
{
503503
var keychain = new MacOSKeychain();
504504
keychain.AddOrUpdate(applicationName, applicationName, password.ToByteArray());
@@ -507,14 +507,14 @@ private static void WriteMacOSKeyChainEntry(string applicationName,string passwo
507507
private static bool DeleteMacOSKeyChainEntry(string name)
508508
{
509509
var keychain = new MacOSKeychain();
510-
return keychain.Remove(name,name);
510+
return keychain.Remove(name, name);
511511
// var cmd = $"/usr/bin/security delete-generic-password -s '{name}'";
512512
// var output = Shell.Bash(cmd);
513513
// var success = output.Count > 1 && !output[0].StartsWith("security:");
514514
// return success;
515515
}
516516

517-
private static string SecureStringToString(SecureString value)
517+
public static string SecureStringToString(SecureString value)
518518
{
519519
IntPtr valuePtr = IntPtr.Zero;
520520
try

0 commit comments

Comments
 (0)