diff --git a/src/Commands/AzureAD/RegisterAzureADApp.cs b/src/Commands/AzureAD/RegisterAzureADApp.cs index d3f03956b..fcea7df00 100644 --- a/src/Commands/AzureAD/RegisterAzureADApp.cs +++ b/src/Commands/AzureAD/RegisterAzureADApp.cs @@ -1,24 +1,25 @@ using PnP.Framework; +using PnP.PowerShell.Commands.Base; +using PnP.PowerShell.Commands.Enums; using PnP.PowerShell.Commands.Model; using PnP.PowerShell.Commands.Utilities; using PnP.PowerShell.Commands.Utilities.REST; using System; using System.Collections.Generic; +using System.Dynamic; using System.IO; using System.Linq; using System.Management.Automation; using System.Net.Http; +using System.Reflection; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using TextCopy; using OperatingSystem = PnP.PowerShell.Commands.Utilities.OperatingSystem; using Resources = PnP.PowerShell.Commands.Properties.Resources; -using PnP.PowerShell.Commands.Base; -using System.Dynamic; -using PnP.PowerShell.Commands.Enums; -using TextCopy; namespace PnP.PowerShell.Commands.AzureAD { @@ -486,7 +487,10 @@ 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, Array.Empty()); + + var psVersion = GetPSVersion(); + + cert = CertificateHelper.CreateSelfSignedCertificate(CommonName, Country, State, Locality, Organization, OrganizationUnit, CertificatePassword, CommonName, validFrom, validTo, Array.Empty(), psVersion); if (Directory.Exists(OutPath)) { @@ -757,5 +761,31 @@ private void SetLogo(AzureADApp azureApp, string token) WriteWarning("Logo File does not exist, ignoring setting the logo"); } } + + private string GetPSVersion() + { + var caller = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(a => a.GetName().Name == "System.Management.Automation"); + //var caller = Assembly.GetCallingAssembly(); + var psVersionType = caller.GetType("System.Management.Automation.PSVersionInfo"); + if (null != psVersionType) + { + PropertyInfo propInfo = psVersionType.GetProperty("PSVersion"); + if (null == propInfo) + { + propInfo = psVersionType.GetProperty("PSVersion", BindingFlags.NonPublic | BindingFlags.Static); + } + var getter = propInfo.GetGetMethod(true); + var version = getter.Invoke(null, new object[] { }); + + if (null != version) + { + var versionType = version.GetType(); + var versionProperty = versionType.GetProperty("Major"); + var minorVersionProperty = versionType.GetProperty("Minor"); + return ((int)versionProperty.GetValue(version)).ToString() + "." + ((int)minorVersionProperty.GetValue(version)).ToString(); + } + } + return ""; + } } } diff --git a/src/Commands/PnP.PowerShell.csproj b/src/Commands/PnP.PowerShell.csproj index bced9be9a..4b1ef1db7 100644 --- a/src/Commands/PnP.PowerShell.csproj +++ b/src/Commands/PnP.PowerShell.csproj @@ -67,6 +67,8 @@ + + diff --git a/src/Commands/Utilities/CertificateHelper.cs b/src/Commands/Utilities/CertificateHelper.cs index 2307c40c7..7bfed2fdf 100644 --- a/src/Commands/Utilities/CertificateHelper.cs +++ b/src/Commands/Utilities/CertificateHelper.cs @@ -1,12 +1,12 @@ -using System; +using PnP.Framework.Diagnostics; +using System; using System.Collections.Generic; using System.IO; +using System.Management.Automation; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using SecureString = System.Security.SecureString; -using System.Management.Automation; -using PnP.Framework.Diagnostics; namespace PnP.PowerShell.Commands.Utilities { @@ -18,12 +18,12 @@ internal static string PrivateKeyToBase64(X509Certificate2 certificate, bool use RSAParameters param = new RSAParameters(); var a = certificate.GetRSAPrivateKey(); - - using(var rsa = MakeExportable(a)) + + using (var rsa = MakeExportable(a)) { param = rsa.ExportParameters(true); } - + string base64String; using (var stream = new MemoryStream()) { @@ -126,15 +126,15 @@ internal static X509Certificate2 GetCertificateFromStore(string thumbprint) /// 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, - X509KeyStorageFlags x509KeyStorageFlags = + internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string certificatePath, SecureString certificatePassword, + X509KeyStorageFlags x509KeyStorageFlags = X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet) { if (System.IO.File.Exists(certificatePath)) { - Log.Debug("CertificateHelper",$"Reading certificate from file '{certificatePath}'"); + Log.Debug("CertificateHelper", $"Reading certificate from file '{certificatePath}'"); var certFile = System.IO.File.OpenRead(certificatePath); if (certFile.Length == 0) @@ -145,7 +145,7 @@ internal static X509Certificate2 GetCertificateFromPath(Cmdlet cmdlet, string ce var certificateBytes = new byte[certFile.Length]; certFile.Read(certificateBytes, 0, (int)certFile.Length); - Log.Debug("CertificateHelper",$"Opening certificate in file '{certificatePath}' {(certificatePassword == null ? "without" : "using")} a certificate password"); + Log.Debug("CertificateHelper", $"Opening certificate in file '{certificatePath}' {(certificatePassword == null ? "without" : "using")} a certificate password"); try { var certificate = new X509Certificate2( @@ -239,7 +239,7 @@ private static IEnumerable 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, string[] sanNames = null) + 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, string psVersion = "7.4") { SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder(); if (sanNames != null) @@ -268,8 +268,9 @@ internal static X509Certificate2 CreateSelfSignedCertificate(string commonName, X500DistinguishedName distinguishedName = new X500DistinguishedName(distinguishedNameString); #pragma warning disable CA1416 // Validate platform compatibility - using (RSA rsa = Platform.IsWindows ? MakeExportable(new RSACng(2048)) : RSA.Create(2048)) - { + using (RSA rsa = RSA.Create(2048)) + { + rsa.ExportRSAPrivateKey(); var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add( @@ -288,6 +289,13 @@ internal static X509Certificate2 CreateSelfSignedCertificate(string commonName, certificate.FriendlyName = friendlyName; } + string passString = password != null ? CredentialManager.SecureStringToString(password) : null; + + if (psVersion == "7.5") + { + return X509CertificateLoader.LoadPkcs12(certificate.Export(X509ContentType.Pfx, password), passString, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); + } + return new X509Certificate2(certificate.Export(X509ContentType.Pfx, password), password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); } #pragma warning restore CA1416 // Validate platform compatibility diff --git a/src/Commands/Utilities/CredentialManager.cs b/src/Commands/Utilities/CredentialManager.cs index 9b99a22e8..4ac5d4f54 100644 --- a/src/Commands/Utilities/CredentialManager.cs +++ b/src/Commands/Utilities/CredentialManager.cs @@ -1,4 +1,7 @@ -using System; +using Microsoft.Identity.Client.Extensions.Msal; +using Microsoft.Win32.SafeHandles; +using PnP.Framework.Modernization.Cache; +using System; using System.Linq; using System.Management.Automation; using System.Management.Automation.Runspaces; @@ -7,9 +10,6 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; -using Microsoft.Identity.Client.Extensions.Msal; -using Microsoft.Win32.SafeHandles; -using PnP.Framework.Modernization.Cache; using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; [assembly: InternalsVisibleTo("PnP.PowerShell.Tests")] @@ -498,7 +498,7 @@ private static PSCredential ReadMacOSKeyChainEntry(string applicationName) } return null; } - private static void WriteMacOSKeyChainEntry(string applicationName,string password) + private static void WriteMacOSKeyChainEntry(string applicationName, string password) { var keychain = new MacOSKeychain(); keychain.AddOrUpdate(applicationName, applicationName, password.ToByteArray()); @@ -507,14 +507,14 @@ private static void WriteMacOSKeyChainEntry(string applicationName,string passwo private static bool DeleteMacOSKeyChainEntry(string name) { var keychain = new MacOSKeychain(); - return keychain.Remove(name,name); + return keychain.Remove(name, name); // var cmd = $"/usr/bin/security delete-generic-password -s '{name}'"; // var output = Shell.Bash(cmd); // var success = output.Count > 1 && !output[0].StartsWith("security:"); // return success; } - private static string SecureStringToString(SecureString value) + public static string SecureStringToString(SecureString value) { IntPtr valuePtr = IntPtr.Zero; try