I have been tasked with automating installation of LetsEncrypt certficates on several Windows Server 2016 systems.
One requirement is to copy the ACL from the old private key to the new one, and so my script first identifies the unique container name from 'C:\ProgramData\Microsoft\Crypto'.
Assuming that I have set the value of $Thumbprint to the correct certifciate thumbprint - which I have... then I believe should be able to locate the private key in the filesystem using:
(Get-Item "CERT:\LocalMachine\My\$Thumbprint").PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
This works great when I import the PFX using certlm.msc but not when I have imported the PFX using Powershell Import-PFXCertificate?
When imported using this cmdlet the .HasPrivateKey property is True, but the .PrivateKey property itself is empty.
I have the script working using certutil with the -ImportPfx switch which is ok, however will not accept the SecureString password.
Any help or ideas would be much appreciated.
To mods:
Symptoms here are the same as this > (PowerShell Import Pfx, and Private Key "Lost"), however the conditions are not. I am not defining my own function to import the PFX, I am using the native Powershell cmdlet Import-PfxCertificate (https://learn.microsoft.com/en-us/powershell/module/pki/import-pfxcertificate?view=windowsserver2016-ps) and therefore am unable to edit the x509 flags as described in that resolution.
In fact, it's not a PowerShell problem and keys are not lost. The problem is in underlying .NET Framework APIs.
(Get-Item "CERT:\LocalMachine\My$Thumbprint").PrivateKey
this call is obsolete since .NET 4.6. and is completely broken in .NET 4.7. See my blog post on this topic: Accessing and using certificate private keys in .NET Framework/.NET Core
What you actually, have to do is to use X509Certificate2 extension methods which will return one of the following types: RSACng, DSACng or ECDsaCng. In PowerShell, extension methods are invoked as static methods, for example:
$rsaCngKey = [System.Security.Cryptography.RSA]::GetRSAPrivateKey($cert)
These types (with "Cng" suffix) are CNG implementations and not supported by X509Certificate2.PrivateKey. In C#, this property will throw an InvalidCastException exception, but PowerShell swallows exceptions from property getters and return $null instead, which may look like "lost" key.
Every CNG private key object contains Key property (for example, RSACng.Key) which is of type of CngKey.
CngKey type has a pair of methods: GetProperty and SetProperty which you shall use in order to read and write key ACL by specifying a Security Descr as a property name.
Related
I have a sqlite database say c:\myDb.sqlite
I have figured out how to build a query to this db in SQLKata:
$query = New-Object SqlKata.Query("myTable")
$compiler = New-Object SqlKata.Compilers.SqliteCompiler
$query.Where("myColumn", "1")
$result = $compiler.Compile($query)
But I have no clue at all how to submit this to my Sqlite database.
Can anyone help?
Thanks,
Alex
Getting this to work from PowerShell is hampered by two difficulties:
Loading the assemblies related to NuGet packages in general and the Microsoft.Data.Sqlite NuGet package in particular often requires extra, non-obvious work in PowerShell.
PowerShell generally doesn't surface extension methods as such - e.g. .Get() on query instances - necessitating explicit calls to the static methods of [SqlKata.Execution.QueryExtensions] instead.
Specifically, using NuGet packages from PowerShell requires the following steps, which are neither convenient nor obvious:
Merely installing NuGet packages with Install-Package or trying to use them from the local cache created by .NET SDK projects in $HOME/.nuget/packages is often not enough, because any assemblies they depend on aren't then present in the same directory, which is what Add-Type requires.
They must also be unpacked in a platform-appropriate manner via an auxiliary .NET SDK project to a single target folder (per package or combined), as outlined in this answer.
Additionally, for the Microsoft.Data.Sqlite package, the platform-appropriate native library (e.g., win-x64\native\*.dll from the "runtimes" folder subtree of the .NET SDK project's publish folder) must be copied directly to the target folder in PowerShell (Core), but curiously not in Windows PowerShell, at least as of package version 5.0.9
The following sample code uses the Add-NuGetType helper function, available from this MIT-licensed Gist, which automates all of the steps above:
Note:
Assuming you have looked at the linked code to ensure that it is safe (which I can personally assure you of, but you should always check), you can install Add-NuGetType directly as follows (instructions for how to make the function available in future sessions or to convert it to a script will be displayed):
irm https://gist.github.com/mklement0/7436c9e4b2f73d7256498f959f0d5a7c/raw/Add-NuGetType.ps1 | iex
When first run, the function downloads and installs a private copy of the .NET SDK embedded inside the folder in which NuGet packages downloaded later are cached. This initial installation takes a while, and the -Verbose switch used below reports its progress.
Add-NuGetType is not meant for production use, but for experimentation with NuGet packages; run help Add-NuGetType for more information.
# Reference the relevant namespaces.
using namespace SqlKata
using namespace SqlKata.Compilers
using namespace SqlKata.Execution
using namespace Microsoft.Data.Sqlite
# Load the SqlKata and Sqlite asssemblies.
# See the comments above for how to install the Add-NuGetType function.
# Note: On first call, a private copy of the .NET SDK is downloaded
# on demand, which takes a while.
Add-NuGetType -Verbose SqlKata, SqlKata.Execution, Microsoft.Data.Sqlite
# First, create sample database './sample.db' with table 'sample_table'
#'
create table sample_table (Name string, Age int);
insert into sample_table (Name, Age) values ("JDoe", 42), ("JRoe", 43);
.save ./sample.db
'# | sqlite3
# Create a [SqliteConnection] instance...
$connection = [SqliteConnection]::new("Data Source=$PWD/sample.db")
# ... and create a query factory for it.
$sqliteDb = [QueryFactory]::new($connection, [SqlServerCompiler]::new())
# Create and execute a sample query.
$query = $sqliteDb.Query("sample_table").Where("Name", "JRoe")
# Note the need to use the static methods of [SqlKata.Execution.QueryExtensions],
# because PowerShell doesn't make *extension methods* automatically available.
[SqlKata.Execution.QueryExtensions]::Get($query) # outputs [Dapper.SqlMapper+DapperRow] instances
Convert .crt, .csr, and .key files to .pfx or .p12 using powershell on Windows server 2016.
I have .cert, .csr, and .key files. But in order to execute the "netsh http add sslcert ..." command, I need the .pfx or .p12 file. And I need this to be done in powershell. Openssl is not an option.
I have accomplished the above using openssl. But Im restricted from downloading software now, so thats not an option any more. Im looking for equivalent of openssl pkcs12 -export -out domain.name.pfx -inkey key.key -in cert.crt command in powershell.
This is an old thread but since I was stuck on the exact same problem and finally found the correct answer that wasn't just everyone shouting to use openssl which sometimes isn't available I thought I'd share here for the next lucky soul.
There is a built-in windows utility call CertUtil which can be called from PS and will do exactly this. It's available out of the box at least as far back as server 2012, cant' speak to older versions.
certutil –MergePFX certfile.cer certfile.pfx
A couple things to keep in mind, the -MergePFX only prompts for the certfile not the key so:
Private key file must have .KEY extension.
Cert and key files must have the same base file name.
Cert and key file must be in the same directory.
If you can use .NET Core 3.0:
Load the certificate via cert = new X509Certificate2(certFile)
If the keyfile is PEM encoded (e.g. starts with "----- BEGIN ") then load it, remember what type it is (human or software), find the base64 contents between the header and footer, and run that through Convert.FromBase64String to get the BER/DER encoded format of the key.
key = RSA.Create()
key.ImportPkcs8PrivateKey(bytes, out _), key.ImportEncryptedPkcs8PrivateKey(password, bytes, out _), or key.ImportRSAPrivateKey(bytes, out _); depending on what format the private key file is in.
certWithKey = cert.CopyWithPrivateKey(key)
File.WriteAllBytes("new.pfx", certWithKey.Export(X509ContentType.Pkcs12, password))
If you can use .NET Core 2.1 / .NET Framework 4.7.2:
Load the cert, as above.
Load the key manually:
RSAPrivateKey
How to get RSACryptoServiceProvider public and private key only in c#
How to parse(Convert to RSAParameters) X.509 private key in C#?
PKCS#8 PrivateKeyInfo
Digital signature in c# without using BouncyCastle
PKCS#8 EncryptedPrivateKeyInfo
X509Certificate2.Import with NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG (ImportEncryptedPkcs8Overwrite method)
Use CopyWithPrivateKey/Export/WriteAllBytes as above
If you're stuck on something older:
You could try loading the cert, manually loading the key into an RSACryptoServiceProvider, using cert.set_PrivateKey, and exporting that. Only works on .NET Framework (eliminated from .NET Core because it has too many side effects, especially when done to a cert already in a persisted certificate store).
I've created a power shell script that sets the SSL based on a provided PFX file.
Using the VSTS pipeline, what is the recommended way of passing PFX file to the script?
Including PFX file in a solution
getting the PFX file path on a target environment (contains dependency,
assuming that PFX file is already placed on target environment)
any other solution...?
The common way to pass authentication to the script is using option 1 (Including PFX file in a solution) as you listed.
After adding the pfx file into your solution, you can import certificates and private keys by import-PfxCertificate.
Detail usage and examples of Import-PfxCertificate, you can refer this document.
I am using WinSCP to write to connect a SQL Server to an SFTP server. I am trying to write a file to an SFTP server where I only have write access, not modify. I am having a problem because I get back
Cannot create remote file '/xxx.filepart'.
The documentation suggests this is because I do not have modify access to the target directory. I did this WinSCP -> Preferences -> Endurance -> Disable
I checked the winscp.ini file and ResumeSupport is 2 (I believe this means disabled). I ran "echo $transferOptions.ResumeSupport" and it says that it is in a default state.
I have checked this documentation:
https://winscp.net/eng/docs/ui_pref_resume
https://winscp.net/eng/docs/library_transferoptions#resumesupport
However, I don't see a PowerShell example, just C#.
I have tried various permutations of $transferOptions.ResumeSupport.State = Off, $transferOptions.ResumeSupport.Off, and whatnot. One of these says that it's read-only.
I know $transferOptions is a variable here but it comes from the default script. The object determines transfer options $transferOptions = New-Object WinSCP.TransferOptions
Thanks in advance for help
edit: The overall problem is I only have write access to the server, but not modify. I am getting a new error: "Cannot overwrite remote file '/xxx'.$$. It looks like the dollar signs are some sort of temp file that it's trying to create. Is there a way to disable whatever setting is causing this?
Syntax for using an enumeration in PowerShell is described in
the article Using WinSCP .NET assembly from PowerShell.
Enumeration values are accessed using static field syntax [Namespace.Type]::Member, for example [WinSCP.Protocol]::Sftp.
You can find a PowerShell example for TransferResumeSupport.State in Converting to .NET assembly section of get and put command documentation:
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.ResumeSupport.State = [WinSCP.TransferResumeSupportState]::Off
$session.GetFiles(..., ..., $False, $transferOptions).Check()
WinSCP GUI can also generate a code template (including TransferOptions and TransferResumeSupportState code) for you.
I have developed a powershell module in C#, implemented a few commands.
How can I execute C# code in this module when it's imported by Powershell?
Create a module manifest with the ModuleToProcess (or RootModule in V3) field set to the PSM1 file and the NestedModules set to the DLL e.g.:
RootModule = 'Pscx.psm1'
NestedModules = 'Pscx.dll'
This is what we do in the PowerShell Community Extensions where we do the same thing - fire up a script first. You can see our PSD1 file here.
This is a very basic solution, simply replace code within the {} with your source. (my test below)
add-type 'public class c{public const string s="Hello World";}';[c]::s
enjoy
I'm also writing a binary cmdLet in .NET. I have found that if you create a class that inherits from at least DriveCmdletProvider, that class can implement InitializeDefaultDrives.
This method will get call when import-module is called on your DLL.
You could use this 'feature' to stand up some session (or module session) data.