I'm looking to verify that a user entered password stored as a SecureString is not blank.
Currently I am checking the length (i.e., if ($password.Length -eq 0) { # prompt again }) and that seems to work. If that's the "correct" way to do it, great. Otherwise I want to know how I should do it.
I'm new to PowerShell so any guidance will be very appreciated. Thanks ahead of time.
You can access the password to check the contents by converting it to a PsCredential object.
Create a SecureString (just for this demo - sounds like you already have this):
$securePassword = ConvertTo-SecureString "TopSecretPassword" -AsPlainText -Force
Convert to a PsCredential object (value of username is irrelevant):
$creds = New-Object System.Management.Automation.PSCredential("username", $securePassword)
Get the password back:
$cleartextPassword = $creds.GetNetworkCredential().Password
Check if it is blank:
if ([string]::IsNullOrWhiteSpace($cleartextPassword))
{
# prompt again
}
Related
Does anyone know how to use password in System.DirectoryServices.ActiveDirectory context. Password is stored in a file.
$UserName="DomainName.com\JohnP"
$PassFile="C:\Temp\Pass.PPP"
$password = get-content $PassFile | ConvertTo-SecureString -AsPlainText -Force
$creds = new-object -typename System.Management.Automation.PSCredential("$UserName",$password)
$a = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest", "MyForest.com",$UserName,$Password)
It always returns "Server rejected the credentials". If I store password in $Password variable, it works. For example, below code works:
$UserName="DomainName.com\JohnP"
$PassFile="C:\Temp\Pass.PPP"
$password = "MyPassword"
$creds = new-object -typename System.Management.Automation.PSCredential("$UserName",$password)
$a = System.DirectoryServices.ActiveDirectory.DirectoryContext("Forest", "MyForest.com",$UserName,$Password)
Can someone please help as to how to use password from a file and then use with System.DirectoryServices.ActiveDirectory context.
Many thanks!
Nratwa
A [PSCredential] stores the password as a secure string, so it's encrypted.
To get the unencrypted password value:
$creds.GetNetworkCredential().Password
In powershell I want to use the username and password which are stored in $uname, $pass. These are actually read from a file and is stored in $uname and $pass, each time the script executes. I have tried like
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $uname,$pass
But the error is
new-object : Cannot find an overload for "PSCredential" and the argument count: "2".
I need to use these credentials for a New-PsSession later on in the script. Can someone please help at the earliest?
You will have to convert your plaintext password to a secure string first:
$password=$pass|ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PsCredential("$uname#domain.tld",$password)
I'm trying to use WinSCP .NET assembly with secure credential, when the password secured in external file.
# Load WinSCP .NET assembly
Add-Type -Path "D:\WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
# Env
$sessionOptions.HostName = "blabla.com"
$sessionOptions.UserName = "UUUU"
#$sessionOptions.Password = "PPPP"
$sessionOptions.SshHostKeyFingerprint = "XXXXXXXXX"
$remotePath = "/home/UUUU/"
With hard coded password its working. If I would like to use securestring for the password, how should I do that?
I tried:
read-host -assecurestring | convertfrom-securestring | out-file D:\securestring.txt
To keep the secure password in a file. Then, in my script, I get it back:
$sessionOptions.Password = get-Content D:\securestring.txt | convertto-securestring
$Cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $sessionOptions.UserName, $sessionOptions.Password}
But it's not working...
Since WinSCP 5.7, the assembly accepts the SecureString using the SessionOptions.SecurePassword.
See https://winscp.net/eng/docs/library_sessionoptions
Thanks for inspiring this improvement.
While the assembly keeps the password encrypted in memory, it still needs to decrypt it eventually. Improvements to limit internal copies of the decrypted password are pending.
As #Matt pointed out, the WinSCP .Net assembly doesn't accept secure strings or credential objects. You need to pass the password as a plaintext string. You can store the secure string in a file, though:
Read-Host 'Enter password' -AsSecureString |
ConvertFrom-SecureString |
Out-File 'C:\password.txt'
and use a PSCredential object to retrieve the decrypted password from the secure string after you read it from the file:
$un = 'username'
$pw = Get-Content 'C:\password.txt' | ConvertTo-SecureString
$cred = New-Object Management.Automation.PSCredential $un, $pw
try {
Add-Type -Path 'WinSCPnet.dll'
$opt = New-Object WinSCP.SessionOptions
$opt.Protocol = [WinSCP.Protocol]::Sftp
$opt.HostName = 'example.org'
$opt.UserName = $cred.UserName
$opt.Password = $cred.GetNetworkCredential().Password
$opt.SshHostKeyFingerprint = 'ssh-rsa 2048 ...'
$sftp = New-Object WinSCP.Session
$sftp.Open($opt)
...
} catch {
...
} finally {
if ($sftp) { $sftp.Dispose() }
}
WinSCP sample code taken from the documentation.
Note, however, that the password must be saved by the same user who is running the SFTP PowerShell script, because the encryption key is derived from that user's password.
According to WinSCP the password property just supports a string. So trying to pass a secure string will not work. If you really want to store the password in a file, you could attempt to store it as the secure string but that is a really bad idea in general since it can be unsecured just as easily (Also not sure if it is possible). I recommend the following option.
# Only stored in memory which is safer.
$sessionOptions.Password = read-host
If you have your heart set on something else you could try this. Just know for previous reasons I do not condone this. Also i have to see if it even works because it looks like you cant output securestring to file.
read-host | out-file D:\securestring.txt
$sessionOptions.Password = get-Content D:\securestring.txt
Ansgar's explains what I didn't know was possible. You can stored the secure string in a file and use it elsewhere.
I use a command like this:
get-pfxcertificate C:\test.pfx
Enter password: *******
The command ask me to fill the prompt. But I can't do that in my script (test.ps1 for ex)
What I need is like this:
get-pfxcertificate C:\test.pfx -password "123456"
or something similar so I can run my script without fill in the prompt each time
I'm very thankful for any reply
There's no Password parameter, you can try with a .NET class:
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import('C:\test.pfx','123456','DefaultKeySet')
Another option is to extend the abilities of Get-PfxCertificate, essentially enabling the password to be passed in.
# create a backup of the original cmdlet
if(Test-Path Function:\Get-PfxCertificate){
Copy Function:\Get-PfxCertificate Function:\Get-PfxCertificateOriginal
}
# create a new cmdlet with the same name (overwrites the original)
function Get-PfxCertificate {
[CmdletBinding(DefaultParameterSetName='ByPath')]
param(
[Parameter(Position=0, Mandatory=$true, ParameterSetName='ByPath')] [string[]] $filePath,
[Parameter(Mandatory=$true, ParameterSetName='ByLiteralPath')] [string[]] $literalPath,
[Parameter(Position=1, ParameterSetName='ByPath')]
[Parameter(Position=1, ParameterSetName='ByLiteralPath')] [string] $password,
[Parameter(Position=2, ParameterSetName='ByPath')]
[Parameter(Position=2, ParameterSetName='ByLiteralPath')] [string]
[ValidateSet('DefaultKeySet','Exportable','MachineKeySet','PersistKeySet','UserKeySet','UserProtected')] $x509KeyStorageFlag = 'DefaultKeySet'
)
if($PsCmdlet.ParameterSetName -eq 'ByPath'){
$literalPath = Resolve-Path $filePath
}
if(!$password){
# if the password parameter isn't present, just use the original cmdlet
$cert = Get-PfxCertificateOriginal -literalPath $literalPath
} else {
# otherwise use the .NET implementation
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($literalPath, $password, $X509KeyStorageFlag)
}
return $cert
}
And now you can call it
# tada: extended cmdlet with `password` parameter
Get-PfxCertificate 'C:\path\to\cert.pfx' 'password'
Also, if you still need the prompt, you can do something like this.
$pwd = Read-Host 'Please enter your SSL Certificate password.'
Get-PfxCertificate 'C:\path\to\cert.pfx' $pwd
There is now a Get-PfxData command in PowerShell that gets the certificate and chain. The command includes a -Password parameter that takes a SecureString object so you can avoid being prompted.
The EndEntityCertificates property contains an array of certificates at the end of the certificate chain and will contain the same certificate object created by the Get-PfxCertificate command.
The following example converts a normal string to a SecureString object, loads the certificates from a file, then assigns the first/only end certificate to the $SigningCert variable:
$SecurePassword=ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force
$PfxData=Get-PfxData -FilePath ".\cert_filename.pfx" -Password $SecurePassword
$SigningCert=$PfxData.EndEntityCertificates[0]
You can now apply $SigningCert without being prompted for the password.
Thanks to Shay for pointing me in the right direction.
My need was getting the Thumbprint from the PFX file, so I used the non-persistent DefaultKeySet.
Testing under 2012 PS3 fails unless the key set is fully qualified.
Also, a blank space between Import and left parenthesis, i.e. "$cert.Import^^(Sys..." causes an error. Picky, picky parser.
My PFX password is encrypted in the source.
I decrypt it at runtime so it is not visible in the souce.
Set-StrictMode -Version Latest
[string] $strPW = '123456'
[string] $strPFX = 'C:\MyCert.pfx'
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($strPFX,$strPW,[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]"DefaultKeySet")
$cert.Thumbprint
If you are forced to use Windows Powershell (v5.*), then previous solutions in the thread are for you.
However if you can use Powershell 7/Powershell Core, then its own Get-PfxCertificate command does have a -Password parameter.
After installing Powershell 7 and making sure to use pwsh and not powershell, you can load the certificate that way:
$certificate = (Get-PfxCertificate <certificate_path>.pfx -Password (ConvertTo-SecureString -String "<password>" -AsPlainText -Force))
This also works using native PowerShell instead of .NET:
$securePassword = ConvertTo-SecureString -String $strPW -Force -AsPlainText
$cert = Import-PfxCertificate -FilePath $strPFX cert:\LocalMachine\My -Password $securePassword
I'm having a lot of difficulty with a PowerShell script that I'm trying to call a DirectoryServices query from. Currently, if I do a
$password = read-host "Password" -asSecureString
and subsequently
$credential = New-Object System.Management.Automation.PSCredential $username,$password
everything works fine. However if I try to pass the string parameter with a param($password) and then convert it to a secure string with this code:
$password = ConvertTo-SecureString -AsPlainText -Force $password
After extensive debugging I can see this is working fine in terms of converting the string to a securestring, but I get a bad user/password from DirectoryServices when I use the parameter. Everything works fine when read from the console. Any ideas on what I can do to accept a parameter OR take console input in the absence of a parameter?
This is what I was hoping would work, but doesn't:
if($password -eq $null) {
$password = read-host "Password" -asSecureString
} else {
$password = ConvertTo-SecureString -AsPlainText -Force $password
}
$credential = New-Object System.Management.Automation.PSCredential $username,$password
I recently created a script and was running into the same issue. The work around I found in my case was the following:
#Prompts for the username/password, enter the username in the form of DomainName\UserName
$Credential = get-credential
#Converts the password to clear text to pass it through correctly as passing through a secure string does not work.
$Password = $credential.GetNetworkCredential().password
#Converts the $Credential to just the DomainName/UsernName.
$Account = $credential.UserName
Hopefully this will work in your situation