How to do LDAP query using Powershell and PKI - powershell

I'm new to Powershell, and I'm trying to do a secure LDAP query using PKI authentication. I'm getting stuck on how to set the certificate and key. Based on Googling/research, I have some of the basics, e.g.:
$connection = new-object System.DirectoryServices.Protocols.LDAPConnection('$domainName:$portNum')
[string[] $get] = "$attribute1", "$attribute2", "attribute3"
$request = new-object System.DirectoryServices.Protocol.SearchRequest("$targetOu", "$filter", "subtree", $get)
$response = new-object $connection.SendRequest($request)
Like I said, I'm getting stuck on how to set/send the certificate and key. I thought I could do $connection.ClientCertificates = $path, but that property is read-only. I also thought I had to do something with $System.Net.NetworkCredential, but I'm not sure if the cert and key actually correspond to username and password. I referred to a Perl script that did an LDAP query and used PKI, and you could do:
clientcert => '/path/to/cert.pem'
clientkey => '/path/to/key.pem'
What's the equivalent for Powershell? Do I have to do something with System.Security.Cryptography.X509Certificates.X509Certificate?
Any help would be appreciated!

$connection.ClientCertificates.Add($cert)
the $cert must be X509Certificate class
and get certificates from store using
$allPersonalCerts = #( Get-ChildItem -Path 'Cert:\CurrentUser\my' )
It returns array of X509Certificate objects (or X509Certificate2 which is child class for X509Certificate )
NB: When doing PowerShell programming, you can always search for help by googling C# or VB.net solutions. This is .Net and examples on .net-oriented languages just differ on syntax

Related

PowerShell - Read Certificate Issuer using public key

Could someone guide me to read the issuer from public key?
I used to do this earlier using the below piece of code.
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import([Convert]::FromBase64String($KeyCred.key))
But its no more working.
Getting the below error.
MethodInvocationException: Exception calling "Import" with "1" argument(s):
"X509Certificate is immutable on this platform. Use the equivalent constructor instead."
I am using PowerShell 7. Appreciate any help on this.
Thanks in advance.
If your $KeyCred.key stores a base64-encoded string that represents the certificate (not public key), then you can use appropriate constructor like this:
$cert = [Security.Cryptography.X509Certificates.X509Certificate2]::new([Convert]::FromBase64String($KeyCred.key))

Trying to understand HMAC SHA256

For an API I need to provide secured information hashed with the HMAC SHA256 protocol. I would like to prepare all my API requests in a stored procedure in an Azure SQL DB, so the hashing needs to take place in SQL. I'm not married to that idea, if there are better ways I can be flexible.
Currently I have the issue that I don't fully understand how different ways of creating the hash, can lead to different outcomes. For instance;
Linux CMD with OpenSSL
~$ echo "message" | openssl dgst -sha256 -hmac "key"
(stdin)=
62b5378a72e18e8b220382be8a4fce0a341ab06afa6367fe664219713e11bb4d
PowerShell
$message = 'message'
$secret = 'key'
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($signature)
echo $signature
bp7ym3X//Ft6uuUn1Y/a2y/kLnIZARl2kXNDBl9Y7Uo=
Online tool
https://www.freeformatter.com/hmac-generator.html#ad-output
6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a
My scalar function
Got the function from https://gist.github.com/rmalayter/3130462
declare #query nvarchar(4000),
#secret nvarchar(500);
set #query = 'message'
set #secret = 'key'
select dbo.fn_HMAC('SHA2_256',cast(#query as varbinary(max)), CAST(#secret as varbinary(max)))
0x93552E8AB930FFCEE9E158A7EA5926A4F36025A1A0ED538763B26AF74147D299
So these 4 methods return 4 different Hashes. I know I need the Has as returned by OpenSSL ( first example ).
can you maybe help me understand why these options return different hashes and how to obtain the hash as generated by OpenSSL in Linux CMD?
Got it! HABO was right it did have to do with an NVARCHAR variable in my script. Once replaced with a VARCHAR it worked!!!

Unable to access ssl certificate from octopus library as X509Certificate2 object using powershell

I am trying to create a jwt token using a pfx which,I have stored in Octopus library. For this I have to create an object of X509Certificate2, which takes certificate path and password as input. Can someone please suggest a way to do this using powershell?
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certpath,'password')
I have been through some documents as per how to access certificate variables in octopus but how can I use them to create an object of X509Certificate2.
https://octopus.com/docs/deployment-process/variables/certificate-variables
After going through Microsoft and Octopus documentation I have managed to get it to work. Octopus store the certificate as a base64 encoded string in a variable named as Cert.Pfx and constructor of X509Certificate2 takes a byte array as a first parameter. So as a first step I just needed to convert the base64 encoded string to byte array.
$certbytearray=[System.Convert]::FromBase64String($OctopusParameters["Cert.Pfx"])
$CertPassKey="password"
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certbytearray,$CertPassKey)
#Biplab Thanks for posting your solution; it saved me a lot of headache! I had a slightly different situation without a password and I found that the X509Certificate2 constructor interpreted the byte array as a file name when I tried to call without the password even though the documentation indicates it should accept just a byte array.
I got it to work without the password by doing an import instead.
$certbytearray=[System.Convert]::FromBase64String($OctopusParameters["mycert.Pfx"])
$mycert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$mycert.import($certbytearray)
write-host $mycert.ThumbPrint
if ($mycert.HasPrivateKey)
{ write-host "has private key"}

New LDAP User with Powershell

I have the challenge to create new LDAP Users with a Powershell Script.
I have googled a lot but I found no good results...
This is my Code to get Users from the LDAP...
$authenticationType = [System.DirectoryServices.AuthenticationTypes]::ServerBind
$objSearcherRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://***.local.com:777/ou=user,o=company", "uid=testuser,ou=system,o=company", "password" , $authenticationType)
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SizeLimit= 0
$objSearcher.PageSize = 1000
$objSearcher.SearchRoot = $objSearcherRoot
$objSearcher.Filter = "cn=$cn"
$result = $objSearcher.FindAll()
My Problem is, I don't know how to insert a new LDAP User (not Active Directory)
It would be really nice if someone could help me... ;)
Thanks
Yes, it's possible, I've done it. You need to bind to the LDAP server using a System.DirectoryServices.Protocols.LdapConnection object (let's say $c) and then create a System.DirectoryServices.Protocols.AddRequest object and populate its attributes (I'm only showing a couple in this example):
[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
[System.Reflection.Assembly]::LoadWithPartialName("System.Net")
$c = New-Object -TypeName System.DirectoryServices.Protocols.LdapConnection -ArgumentList "***.local.com:777,uid=testuser,ou=system,o=company", "password" , $authenticationType"
$c.Bind()
$r = New-Object -TypeName System.DirectoryServices.Protocols.AddRequest
$r.DistinguishedName = "uid= xxxx, ou=user, o=company"
$r.Attributes.Add((New-Object -TypeName System.DirectoryServices.Protocols.DirectoryAttribute -ArgumentList "objectclass",#("top","organizationalPerson","person","inetorgperson","inetuser","mailrecipient","pwmuser","posixAccount"))) | Out-Null
$r.Attributes.Add((New-Object -TypeName System.DirectoryServices.Protocols.DirectoryAttribute -ArgumentList "cn",($FirstName+" "+$LastName))) | Out-Null
Then just send the request:
$c.SendRequest($r)
LDAP does not support "inserts", but supports "adds". The LDAP client must create an entry and transmit that entry to the directory server using the ADD request. The server returns an ADD result to the LDAP client which contains information about the success or failure of the ADD request. So, check the documentation for information on transmitting an ADD request to the directory server and interpreting the subsequent response.
The LDAP client must have permission to ADD an entry (a user in this case). This involves using the BIND request to change the authorization state of the connection to one which permits adding an entry at the designated place in the directory information tree.
Perhaps this link will help.
You say "create new LDAP Users" but you could create AD users and then they would be available Using LDAP.
I used a script from Microsoft to do something similar.
If you look through the code, you should be able to see how they did it. We used their code with a few tweaks to do what we needed.
-jim

Test remote server cert against locally stored cert before trusting

I have some code that I use to connect to a web server that we own. The server has a self signed certificate. I am currently connecting with the trust any cert
[System.Net.ServicePointManager]::ServerCertificateValidationCallback {$true}.
How can I change this so that I can either test the remote server with custom code or against a cert in my local store? For example can I change the code above to
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$customValidator}
What I would like to do is, instead of trusting any/all certs, I would like to trust it only if it matches with the public key cer file I have or I would like to test the certificate using custom code. The thing I am not very clear on is how to handle the custom verification and at what point in the code. Do I need to write an entire class to handle custom SSL validation or is there a better way to do this?
$url = "https://myWebServer"
$web = New-Object Net.WebClient
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
//[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { customSslCertValidation } #I would like to do something like this
$output = $web.DownloadString($url)
Write-Host $output
Try something like this:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {
$c = [System.Security.Cryptography.X509Certificates.X509Certificate2]$args[1]
($c.Thumbprint -eq "StoredOrRetrievedHash")
}
This checks the Thumbprint of the certificate returned as part of the request against the string "StoredOrRetrievedHash". You would want to replace that with the details from your certificate loaded in dynamically. It is worth noting that the first line of the scriptblock delegate casts the second element of the $args array to be an object of type X509Certificate2, as the bare X509Certificate object doesn't expose the Thumbprint property.
Note: this only works in PowerShell v2 and later - in v1 assigning an arbitrary scriptblock to a delegate was a lot of extra work and quite frankly, just not worth the effort typically.