I want to test if our web server (located on the intranet) is online (1) or offline (0) from the server containing it using PowerShell 2.0. For this I have a script that navigates to the page and check if a html-string is available on the page.
function Get-HeartBeat {
$isAlive = $false
try {
$webclient = New-Object WebClient
$userName = "xxx"
$password = "xxx"
$domain = "xxx"
$url = "ourUrl.com"
$html = '<input type="submit">'
$webclient.Credentials = New-Object System.Net.NetworkCredential($userName, $password, $domain)
$webpage = $webclient.DownloadString($url)
$isAlive = $webpage.Contains($html)
} catch {
# A WebException is thrown if the status code is anything but 200 (OK)
Write-Host $_
$isAlive = $false
}
return "$([Int32]$isAlive)"
}
Unfortunately this returns an error:
Exception calling "DownloadString" with "1" argument(s): "The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel."
A way to trust our certificate is to create a type with a certificate policy as follows (modification of this answer):
Add-Type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustOurCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint servicePoint, X509Certificate certificate,
WebRequest request, int certificateProblem)
{
return certificate.Issuer.Equals("OUR ISSUER")
&& certificate.Subject.Contains("our application");
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustOurCertsPolicy
This still feels a bit "stringly typed" instead of 100% secure.
Is this safe? Is there a better way to create a WebClient that has one certificate accepted? The certificate we should trust is available in cert:LocalMachine\My.
The correct way to handle certificate errors is to import the certificate chain (root and intermediat CA certificates) of the webserver's certificate into the local certificate store (as Trusted Root CA and Intermediate CA respectively). If the server certificate is self-signed you need to import the server certificate as a Trusted Root CA.
Another option, depending on what you actually need to check, might be to modify your algorithm. For instance, if you really need just a heartbeat (not verify that an actual request returns a specific result) you could simply try to establish a TCP connection to the port:
function Get-HeartBeat {
$url = 'ourUrl.com'
$port = 443
$clnt = New-Object Net.Sockets.TcpClient
try {
$clnt.Connect($url, $port)
1
} catch {
0
} finally {
$clnt.Dispose()
}
}
On more recent Windows versions you could use the Test-NetConnection cmdlet to the same end:
function Get-HeartBeat {
$url = 'ourUrl.com'
$port = 443
[int](Test-Net-Connection -Computer $url -Port $port -InformationLevel Quiet)
}
Related
I'm trying to automate the transfer of files with WinSCP using PowerShell. I've installed .NET assembly. In the code I've added, I've taken out credentials and hostname for privacy and I'm also using the full path to WinSCPnet.dll. Here is the error I'm getting:
Error: The value supplied is not valid, or the property is read-only. Change the value, and then try again.
I'm getting this where $sessionOptions is defined on 5th line. Any ideas of what I could do to get this running?
try
{
Add-Type -Path "WinSCPnet.dll"
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "HostName"
UserName = "UserName"
Password = "Password"
SshHostKeyFingerprint = "ssh-rsa 2048 Fingerprint here"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult =
$session.PutFiles("Source", "Destination", $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host ("Upload of {0} succeeded" -f $transfer.FileName)
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host ("Error: {0}" -f $_.Exception.Message)
exit 1
}
The most probable explanation is that the value of SessionOptions.SshHostKeyFingerprint is invalid, no other property in your code is validated. See https://winscp.net/eng/docs/message_key_fingerprint_does_not_match
Use WinSCP GUI function to generate a code template to get the correct value.
Read also about Automatic host key verification.
If you need more help, you will have to show us the real value you are using (it's a fingerprint of a server public key, hence it is a public information, no need to hide it).
I've run into this recently and the issue turned out to be how the host key is shown in the connection profile. It was stripping off the trailing "=" character
I am updating my current scripts from the AzureAD module and want to update a script which deletes expired app registration certificates.
I can remove expired secrets using the new module, however the new command Remove-MgApplicationKey requires proof as per Microsoft document: https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.applications/remove-mgapplicationkey?view=graph-powershell-1.0. (As part of the request validation for this method, a proof of possession of an existing key is verified before the action can be performed).
`$params = #{
KeyId = "f0b0b335-1d71-4883-8f98-567911bfdca6"
Proof = "eyJ0eXAiOiJ..."
}
Remove-MgApplicationKey -ApplicationId $applicationId -BodyParameter $params`
Any suggestions on how to code this in PowerShell?
Thanks.
C# example from Microsoft doc: https://learn.microsoft.com/en-us/graph/application-rollkey-prooftoken
Code would look something like this
using assembly System;
using assembly System.Security.Cryptography.X509Certificates;
# Configure the following
$pfxFilePath = "<Path to your certificate file";
$password = "<Certificate password>";
$objectId = "<id of the application or servicePrincipal object>";
# Get signing certificate
#$signingCert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new([string]$pfxFilePath, [string]$password);
#$signingCert = [System.Security.Cryptography.X509Certificates]::X509Certificate2($pfxFilePath, $password);
$signingCert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new();
$signingCert.CreateFromEncryptedPemFile($pfxFilePath, $password, $null)
#$signingCert | Format-Table
#$signingCert.filename = $pfxFilePath;
#$signingCert.password = $password;
# audience
$aud = "00000002-0000-0000-c000-000000000000";
#aud and iss are the only required claims.
$claims = [System.Collections.Generic.Dictionary[string, object]]::new()
$claims.Add("aud", $aud)
$claims.Add("iss", $objectId)
#token validity should not be more than 10 minutes
$now = [DateTime]::UtcNow;
$securityTokenDescriptor = New-Object [System.Security.Cryptography.X509Certificates.SecurityTokenDescriptor]::new()
$securityTokenDescriptor.Claims = $claims,
$securityTokenDescriptor.NotBefore = $now,
$securityTokenDescriptor.Expires = $now.AddMinutes(10),
$securityTokenDescriptor.SigningCredentials = New-Object X509SigningCredentials($signingCert);
$handler = [Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler]::new();
$x = handler.CreateToken($securityTokenDescriptor);
Write-Host x;
I am loocking for an option to disable the SSL cert validation for a single websocket connection. My server in question uses a self-signed certificate only.
This is the Powershell code that I have so far:
$server = 'server1'
$cred = Get-Credential -Message "Credentials to access '$server'"
# setting credentials for any required proxy-authentication:
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
# define settings for http-client:
Add-Type -AssemblyName System.Net.Http
$handler = [System.Net.Http.HttpClientHandler]::new()
$ignoreCerts = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$handler.ServerCertificateCustomValidationCallback = $ignoreCerts
$handler.Credentials = $cred
$handler.PreAuthenticate = $true
$client = [System.Net.Http.HttpClient]::new($handler)
# generate websocket-key:
$init = [guid]::NewGuid().Guid.Substring(0,16)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($init)
$key = [Convert]::ToBase64String($bytes)
# request protocol switch:
$url = [string]::Concat('https://', $server)
$client.DefaultRequestHeaders.Add('Connection', 'Upgrade,Keep-Alive')
$client.DefaultRequestHeaders.Add('Upgrade', 'WebSocket')
$client.DefaultRequestHeaders.Add('Sec-WebSocket-Version', '13')
$client.DefaultRequestHeaders.Add('Sec-WebSocket-Key', $key)
$client.DefaultRequestHeaders.Add('Origin', "wss://$server")
$response = $client.GetAsync($url).result
if ($response.StatusCode -eq 'BadGateway'){break}
Here I need a better solution than this "big bang" approach. In best case I am able to disable the cert validation only for this single websocket client/connection like I could do it for the above http-handler:
# skip SSL-Validation for websocket communication:
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
If cert-validation is disabled, then we can do a quick connection-test to the server:
$ws = [System.Net.WebSockets.ClientWebSocket]::new()
$ct = [System.Threading.CancellationToken]::new($false)
$url = [string]::Concat('wss://', $server)
# connect to server:
$conn = $ws.ConnectAsync($url, $ct)
$null = [Threading.Tasks.Task]::WaitAll($conn)
# check websocket-status:
$ws | ft -AutoSize
I complete steps 1-4 of this answer, which adds my certificate to the "Trusted Root Certification Authorities" > "Certificates," and the certificate is granted <All> intended purposes.
Executing the below PowerShell code fails with The remote certificate is invalid according to the validation procedure when $ftp_request.EnableSsl = $true. It succeeds when $ftp_request.EnableSsl = $false.
$file_folder = "C:\Users\username\Desktop"
$file_name = "test.txt"
$file_path = "$file_folder\$file_name"
$ftp_path = "ftp://127.0.0.1/$file_name"
$username = "user"
$pwd = "pass"
# Create a FTPWebRequest object to handle the connection to the ftp server
$ftp_request = [System.Net.FtpWebRequest]::Create($ftp_path)
# set the request's network credentials for an authenticated connection
$ftp_request.Credentials =
New-Object System.Net.NetworkCredential($username, $pwd)
$ftp_request.UseBinary = $true
$ftp_request.UsePassive = $true
$ftp_request.KeepAlive = $false
$ftp_request.EnableSsl = $true
$ftp_request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$file_contents = Get-Content -en byte $file_path
$ftp_request.ContentLength = $file_contents.Length
$ftp_stream = $ftp_request.GetRequestStream()
$ftp_stream.Write($file_contents, 0, $file_contents.Length)
$ftp_stream.Close()
$ftp_stream.Dispose()
I know that it's possible to manually handle this by writing a handler to ServicePointManager.ServerCertificateValidationCallback, but I would like to have SSL certificates handled automatically by the Windows cert manager.
$ftp_path = "ftp://127.0.0.1/$file_name"
Adding a certificate as trusted for all purposes does not mean that a certificate is trusted for all hosts. The hostname you use to connect still has to match the subject of the certificate. And while you don't provide any information about the certificate itself my guess is that your certificate is not issued for the subject "127.0.0.1".
I am currently trying to send an email through powershell with a populated mileage field in order for outlook to pick up on.
The email goes through fine but I am unable to get a value through for the 'Mileage' Field.
$SMTPName = ''
$EmailMessage = new-object Net.Mail.MailMessage
$SMTPServer = new-object Net.Mail.SmtpClient($SMTPName)
$EmailMessage.Headers.Add("Mileage", "HB")
$EmailMessage.From = ''
$EmailMessage.To.Add('')
$EmailMessage.Subject = $sub
$EmailMessage.Body = $body
$EmailMessage.Priority = [System.Net.Mail.MailPriority]::High
$SMTPServer.Send($EmailMessage)
Mileage is a MAPI property so you will need to send the message with either Outlook or EWS eg the following should work to send a message with that property set in EWS
## Get the Mailbox to Access from the 1st commandline argument
$MailboxName = $args[0]
## Load Managed API dll
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
$EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
$EmailMessage.Subject = "Message Subject"
#Add Recipients
$EmailMessage.ToRecipients.Add("user#domain.com")
$EmailMessage.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody
$EmailMessage.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML
$EmailMessage.Body.Text = "Body"
$Mileage = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition([Microsoft.Exchange.WebServices.Data.DefaultExtendedPropertySet]::Common,34100,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String);
$EmailMessage.SetExtendedProperty($Mileage,"HB")
$EmailMessage.SendAndSaveCopy() enter code here