Return messages instead of exit code for msi in powershell - powershell

Is there a way I could make msiexec return the error/success message instead of the default exit code? for instance:
msiexec /i "D:/path/installer.msi"
might return 1603, and referencing the lookup table explains that
A fatal error occurred during installation.
I would like the message, or even the error code ERROR_INSTALL_FAILURE to be returned instead of the exit code 1603.

Note: Not sure if the effort poured into this answer serves a real-world purpose, but it may be of interest for demonstrating various advanced PowerShell techniques: Advanced regex matching using the automatic $matches variable, scraping a web page via Invoke-WebRequest, and calling the Windows API via Add-Type.
Find function friendlyMsiExec below, which
scrapes the MSDN page to get the official table of defined MSI error codes (exit codes)
if that fails - scraping is not the most robust technique (page structures and URLs change) - the function issues a warning and uses a hard-coded table instead.
Invokes msiexec synchronously, looks up the exit code in the scraped/built-in table and outputs the error message corresponding to the exit code.
Once defined, you can invoke it as follows:
> friendlyMsiExec /i "D:/path/installer.msi" # assume the exit code is 1603
A fatal error occurred during installation. (1603)
Note:
The exit code will be available as $LASTEXITCODE after the function returns.
Scraping is not only not the most robust, but the page download and parsing takes a few seconds, although in the context of an MSI installation that probably won't matter much.
An exit code may also be a regular Windows API error code ("any error in Winerror.h"), so, as a fallback, the FormatMessage Windows API function is consulted, courtesy of an adaptation of this helpful C# answer.
function friendlyMsiExec {
# Try to scrape the MSDN page for the complete list of error codes (exit codes).
# Note: This is not the most robust approach as web pages aren't designed for
# programmatic data extraction, and the format of the page may change over time.
$htErrors = #{}
# Table is in a <table> element with attritubte 'summary="table"'.
$tbl = (Invoke-WebRequest 'https://msdn.microsoft.com/en-us/library/windows/desktop/aa376931(v=vs.85).aspx').ParsedHtml.getElementsByTagName('table') | ? { $_.getAttribute('summary') -eq 'table' }
# Parse the text representation (.InnerText) using regular expressions.
$tbl.InnerText -split '\r?\n' | % { if ($_ -match "^(ERROR_.+?)(\d+)(.+)$") { $htErrors.Add([int] $matches[2], #( $matches[1], $matches[3] )) } }
if ($htErrors.Count -eq 0) { # Scraping failed, use hard-coded table (current as of 2 Nov 2016).
Write-Warning "Scraping the MSDN page for error codes failed, falling back to hard-coded table."
$htErrors = #{
0 = 'ERROR_SUCCESS', 'The action completed successfully.'
13 = 'ERROR_INVALID_DATA', 'The data is invalid.'
87 = 'ERROR_INVALID_PARAMETER', 'One of the parameters was invalid.'
120 = 'ERROR_CALL_NOT_IMPLEMENTED', 'This value is returned when a custom action attempts to call a function that cannot be called from custom actions. The function returns the value ERROR_CALL_NOT_IMPLEMENTED. Available beginning with Windows Installer version 3.0.'
1259 = 'ERROR_APPHELP_BLOCK', 'If Windows Installer determines a product may be incompatible with the current operating system, it displays a dialog box informing the user and asking whether to try to install anyway. This error code is returned if the user chooses not to try the installation.'
1601 = 'ERROR_INSTALL_SERVICE_FAILURE', 'The Windows Installer service could not be accessed. Contact your support personnel to verify that the Windows Installer service is properly registered.'
1602 = 'ERROR_INSTALL_USEREXIT', 'The user cancels installation.'
1603 = 'ERROR_INSTALL_FAILURE', 'A fatal error occurred during installation.'
1604 = 'ERROR_INSTALL_SUSPEND', 'Installation suspended, incomplete.'
1605 = 'ERROR_UNKNOWN_PRODUCT', 'This action is only valid for products that are currently installed.'
1606 = 'ERROR_UNKNOWN_FEATURE', 'The feature identifier is not registered.'
1607 = 'ERROR_UNKNOWN_COMPONENT', 'The component identifier is not registered.'
1608 = 'ERROR_UNKNOWN_PROPERTY', 'This is an unknown property.'
1609 = 'ERROR_INVALID_HANDLE_STATE', 'The handle is in an invalid state.'
1610 = 'ERROR_BAD_CONFIGURATION', 'The configuration data for this product is corrupt. Contact your support personnel.'
1611 = 'ERROR_INDEX_ABSENT', 'The component qualifier not present.'
1612 = 'ERROR_INSTALL_SOURCE_ABSENT', 'The installation source for this product is not available. Verify that the source exists and that you can access it.'
1613 = 'ERROR_INSTALL_PACKAGE_VERSION', 'This installation package cannot be installed by the Windows Installer service. You must install a Windows service pack that contains a newer version of the Windows Installer service.'
1614 = 'ERROR_PRODUCT_UNINSTALLED', 'The product is uninstalled.'
1615 = 'ERROR_BAD_QUERY_SYNTAX', 'The SQL query syntax is invalid or unsupported.'
1616 = 'ERROR_INVALID_FIELD', 'The record field does not exist.'
1618 = 'ERROR_INSTALL_ALREADY_RUNNING', 'Another installation is already in progress. Complete that installation before proceeding with this install.'
1619 = 'ERROR_INSTALL_PACKAGE_OPEN_FAILED', 'This installation package could not be opened. Verify that the package exists and is accessible, or contact the application vendor to verify that this is a valid Windows Installer package.'
1620 = 'ERROR_INSTALL_PACKAGE_INVALID', 'This installation package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer package.'
1621 = 'ERROR_INSTALL_UI_FAILURE', 'There was an error starting the Windows Installer service user interface. Contact your support personnel.'
1622 = 'ERROR_INSTALL_LOG_FAILURE', 'There was an error opening installation log file. Verify that the specified log file location exists and is writable.'
1623 = 'ERROR_INSTALL_LANGUAGE_UNSUPPORTED', 'This language of this installation package is not supported by your system.'
1624 = 'ERROR_INSTALL_TRANSFORM_FAILURE', 'There was an error applying transforms. Verify that the specified transform paths are valid.'
1625 = 'ERROR_INSTALL_PACKAGE_REJECTED', 'This installation is forbidden by system policy. Contact your system administrator.'
1626 = 'ERROR_FUNCTION_NOT_CALLED', 'The function could not be executed.'
1627 = 'ERROR_FUNCTION_FAILED', 'The function failed during execution.'
1628 = 'ERROR_INVALID_TABLE', 'An invalid or unknown table was specified.'
1629 = 'ERROR_DATATYPE_MISMATCH', 'The data supplied is the wrong type.'
1630 = 'ERROR_UNSUPPORTED_TYPE', 'Data of this type is not supported.'
1631 = 'ERROR_CREATE_FAILED', 'The Windows Installer service failed to start. Contact your support personnel.'
1632 = 'ERROR_INSTALL_TEMP_UNWRITABLE', 'The Temp folder is either full or inaccessible. Verify that the Temp folder exists and that you can write to it.'
1633 = 'ERROR_INSTALL_PLATFORM_UNSUPPORTED', 'This installation package is not supported on this platform. Contact your application vendor.'
1634 = 'ERROR_INSTALL_NOTUSED', 'Component is not used on this machine.'
1635 = 'ERROR_PATCH_PACKAGE_OPEN_FAILED', 'This patch package could not be opened. Verify that the patch package exists and is accessible, or contact the application vendor to verify that this is a valid Windows Installer patch package.'
1636 = 'ERROR_PATCH_PACKAGE_INVALID', 'This patch package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer patch package.'
1637 = 'ERROR_PATCH_PACKAGE_UNSUPPORTED', 'This patch package cannot be processed by the Windows Installer service. You must install a Windows service pack that contains a newer version of the Windows Installer service.'
1638 = 'ERROR_PRODUCT_VERSION', 'Another version of this product is already installed. Installation of this version cannot continue. To configure or remove the existing version of this product, use Add/Remove Programs in Control Panel.'
1639 = 'ERROR_INVALID_COMMAND_LINE', 'Invalid command line argument. Consult the Windows Installer SDK for detailed command-line help.'
1640 = 'ERROR_INSTALL_REMOTE_DISALLOWED', 'The current user is not permitted to perform installations from a client session of a server running the Terminal Server role service.'
1641 = 'ERROR_SUCCESS_REBOOT_INITIATED', 'The installer has initiated a restart. This message is indicative of a success.'
1642 = 'ERROR_PATCH_TARGET_NOT_FOUND', 'The installer cannot install the upgrade patch because the program being upgraded may be missing or the upgrade patch updates a different version of the program. Verify that the program to be upgraded exists on your computer and that you have the correct upgrade patch.'
1643 = 'ERROR_PATCH_PACKAGE_REJECTED', 'The patch package is not permitted by system policy.'
1644 = 'ERROR_INSTALL_TRANSFORM_REJECTED', 'One or more customizations are not permitted by system policy.'
1645 = 'ERROR_INSTALL_REMOTE_PROHIBITED', 'Windows Installer does not permit installation from a Remote Desktop Connection.'
1646 = 'ERROR_PATCH_REMOVAL_UNSUPPORTED', 'The patch package is not a removable patch package. Available beginning with Windows Installer version 3.0.'
1647 = 'ERROR_UNKNOWN_PATCH', 'The patch is not applied to this product. Available beginning with Windows Installer version 3.0.'
1648 = 'ERROR_PATCH_NO_SEQUENCE', 'No valid sequence could be found for the set of patches. Available beginning with Windows Installer version 3.0.'
1649 = 'ERROR_PATCH_REMOVAL_DISALLOWED', 'Patch removal was disallowed by policy. Available beginning with Windows Installer version 3.0.'
1650 = 'ERROR_INVALID_PATCH_XML', 'The XML patch data is invalid. Available beginning with Windows Installer version 3.0.'
1651 = 'ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT', 'Administrative user failed to apply patch for a per-user managed or a per-machine application that is in advertise state. Available beginning with Windows Installer version 3.0.'
1652 = 'ERROR_INSTALL_SERVICE_SAFEBOOT', 'Windows Installer is not accessible when the computer is in Safe Mode. Exit Safe Mode and try again or try using System Restore to return your computer to a previous state. Available beginning with Windows Installer version 4.0.'
1653 = 'ERROR_ROLLBACK_DISABLED', 'Could not perform a multiple-package transaction because rollback has been disabled. Multiple-Package Installations cannot run if rollback is disabled. Available beginning with Windows Installer version 4.5.'
1654 = 'ERROR_INSTALL_REJECTED', 'The app that you are trying to run is not supported on this version of Windows. A Windows Installer package, patch, or transform that has not been signed by Microsoft cannot be installed on an ARM computer.'
3010 = 'ERROR_SUCCESS_REBOOT_REQUIRED', 'A restart is required to complete the install. This message is indicative of a success. This does not include installs where the ForceReboot action is run. '
}
}
# Execute msiexec synchronously.
$psInfo = Start-Process -PassThru -NoNewWindow -Wait msiexec -ArgumentList $Args
# Get the exit code.
$ec = $psInfo.ExitCode
# Look up the exit code returned in the hashtable and extract the error *message*.
if ($htErrors[$ec]) {
# Use subscript [0] to extract the *symbolic name* instead.
$errMsg = $htErrors[$ec][1] # -replace '\r'
} else { # not found in table
# See if it's a Windows (system) error code.
# Helper type for getting Windows (system) error messages by error code.
# Returns $null if no message is found.
# Example:
# [net.same2u.pshelper.WinErrMsg]::Get(2) # -> "The system cannot find the file specified."
Add-Type -TypeDefinition #'
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace net.same2u.pshelper {
public static class WinErrMsg {
[DllImport("kernel32.dll")]
static extern int FormatMessage(int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, System.Text.StringBuilder lpBuffer, int nSize, IntPtr Arguments);
public static string Get(int errCode) {
const int CAPACITY = 512;
const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
StringBuilder sb = new StringBuilder(CAPACITY);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, errCode, 0, sb, sb.Capacity, IntPtr.Zero);
// Remove trailing CRLF
int i = sb.Length;
if (i>0 && sb[i - 1] == 10) i--;
if (i>0 && sb[i - 1] == 13) i--;
sb.Length = i;
return sb.Length > 0 ? sb.ToString() : null;
}
}
}
'#
$errMsg = [net.same2u.pshelper.WinErrMsg]::Get($ec)
# Determine fallback message, if no match was found.
if (-not $errMsg) { $errMsg = "An unknown exit code was reported."}
}
# Set $LASTEXITCODE, so that the caller can inspect the exit code later.
Set-Variable -Scope 1 LASTEXITCODE $ec
# Output the message, suffixed with the exit code.
"$errMsg ($ec)"
}

No, the msi can't replace the Error Code with the message itself.
Like Ansgar Wiechers said the msi was meant to create an log file on each machine you execute it, on the same folder ( e.g. C:\Windows\Temp). With the /l parameters you can modify the output in that file. If you need an comprehensive log you should start with voicewarmupx.

Well an MSI install doesn't always return 1603. The primary reason for a 1603 is a failing custom action where Windows Installer doesn't know why the custom action code failed, and that custom action code was not robust enough to handle its own errors and provide a message before causing the install to fail (and probably roll back). The verbose log should tell you why it failed.
So if it's your MSI you have the opportunity to correct it, otherwise you are at the mercy of the quality of development of the MSI.
Also, your question contains a contradiction: you say you would like the error code ERROR_INSTALL_FAILURE to be returned instead of the exit code, but the 1603 exit code is ERROR_INSTALL_FAILURE.

Related

Calling Openoffice from Perl throws NoSuchElementexception

I try to convert odt-Files to doc-Files using OpenOffice. Installed Version is 3.1.1 and can't be changed at the moment. Perl Version is 5.18.
The Perl-module OpenOffice::UNO is used for this conversion. Unfortunately in newer Versions of OpenOffice/LibreOffice do not support Perl anymore.
The Script calls OpenOffice headless using xvfb.
Here is the code used:
`# Launch OpenOffice.org as a server
$ ooffice \
"-accept=socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"
use OpenOffice::UNO;
# connect to the OpenOffice.org server
$uno = OpenOffice::UNO->new;
$cxt = $uno->createInitialComponentContext('file:///.../path/perluno');
$sm = $cxt->getServiceManager;
$resolver = $sm->createInstanceWithContext
("com.sun.star.bridge.UnoUrlResolver", $cxt);
$rsm = $resolver->resolve
("uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager");
# get an instance of the Desktop service
$rc = $rsm->getPropertyValue("DefaultContext");
$desktop = $rsm->createInstanceWithContext("com.sun.star.frame.Desktop", $rc);
.....`
On the last included line to create $desktop i get following Error message:
terminate called after throwing an instance of 'com::sun::star::container::NoSuchElementException'
Is there any way to fix this problem? Tried to understand the Code of the UNO-interface, especially UNO.xs but there has not been any information about the call "createInstanceWithContext".
Looking through the OpenOffice-documentation does not provide any information about this either.
It would also help just to get the complete java error message, to make sure what element is missing.
The file "perluno" has the content:
[Bootstrap]
UNO_TYPES=/usr/lib64/openoffice.org//program/types.rdb
UNO_SERVICES=/usr/lib64/openoffice.org//program/services.rdb

Installing an exe with Powershell DSC Package resource gets return code 1619

I'm trying to use Powershell DSC's Package resource to install an exe... Perforce's P4V to be specific. Here's my code:
Configuration PerforceMachine
{
Node "SERVERNAME"
{
Package P4V
{
Ensure = "Present"
Name = "Perforce Visual Components"
Path = "\\nas\share\p4vinst64.exe"
ProductId = ''
Arguments = "/S /V/qn" # args for silent mode
LogPath = "$env:ProgramData\p4v_install.log"
}
}
}
When running this, this is the error Powershell gives me:
PowerShell provider MSFT_PackageResource failed to execute Set-TargetResource functionality with error message: The return code 1619 was not expected. Configuration is likely not
correct
+ CategoryInfo : InvalidOperation: (:) [], CimException
+ FullyQualifiedErrorId : ProviderOperationExecutionFailure
+ PSComputerName : SERVERNAME
According to documentation, return code 1619 means the MSI package couldn't be opened. However, when I manually log in to the machine and run "\\nas\share\p4vinst64.exe /S /V/qn", the install works flawlessly.
Does anyone know why this is failing? Alternately, can anyone tell me how to troubleshoot this? I pasted all the error information I got from the terminal, my log file (p4v_install.log) is a 0 byte file, and there are no events in the event viewer. I don't know how to troubleshoot it any further!
EDIT: I should note that I also tried using the File resource to copy the file locally, and then install it from there. Sadly, that met with the same result.
Daniel over at the Powershell.org forums was able to figure this out for me.
The P4V InstallShield setup wrapper puts the MSI file into wrong path if you execute as LocalSystem.
I’ve managed to develop a Configuration that works, see below. The key is the /b switch here which puts the MSI file into a defined location. I’ve added ALLUSERS=1 to get the shortcuts visible to all users and REBOOT=ReallySuppress to avoid a sudden restart (which will happen otherwise).
Configuration PerforceMachine
{
Package P4V
{
Ensure = "Present"
Name = "Perforce Visual Components"
Path = "C:\My\p4vinst64.exe"
ProductId = ''
Arguments = '/b"C:\Windows\Temp\PerforceClient" /S /V"/qn ALLUSERS=1 REBOOT=ReallySuppress"' # args for silent mode
}
}
Well, what happens here is that the package gets installed (not tested with p4vinst64.exe yet! So, not sure why it says pack cannot be opened as the error) but since you did not specify a ProductID value, the verification at the end of install fails. That is the error you are seeing. The Package resource is no good for installing .exe packages or even MSIs with no ProductID represented as a GUID.
You can use the WindowsProcess resource instead.

Powershell remoting and page file

I wrote a powershell script that connects to a remote machine with the intent of executing a software rollout on said machine. Basically it connects, maps a drive, copies the rollout from the mapped drive to the target machine, then executes a perl script to install the rollout. If I do those steps manually everything works fine. When I try using my script, the perl script fails on the remote machine saying, "The paging file is too small for this operation to complete".
Can someone explain the considerations I need to take into account when operating remotely? I've tried monitoring memory usage and I don't see anything out of the ordinary. Is the page file OS wide or is there some type of per user configuration my script should be setting when it connects?
I can post snippets of my script if needed, but the script is 426 lines so I think it would be overwhelming to post in its entirety.
I found that the remote shells are managed differently than logging onto the box and executing a powershell session. I had to increase the maximum amount of memory available using one of the commands below:
Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 1024
winrm set winrm/config #{MaxMemoryPerShellMB="1024"}
The default is 150MB which didn't cut it in my case. I can't say that I recommend 1GB, I'm just a developer. I tried upping it until I found what worked for me.
I tried this code to run the puppet client as an administrator but the framework still complains with "Access Denied"
Exe (C:\Users\lmo0\AppData\Local\Temp\Microsoft .NET Framework 4 Setup_4.0.30319\Windows6.1-KB958488-v6001-x64.msu) failed with 0x5 - Access is denied. .
using System;
using System.Diagnostics;
namespace RunAsAdmin
{
class Program
{
static void Main(string[] args)
{
Process proc = new Process();
Process p = new Process();
p.StartInfo.FileName = #"powershell.exe";
p.StartInfo.Arguments = #"invoke-command -computername vavt-pmo-sbx24 -ScriptBlock {&'C:\Program Files (x86)\Puppet Labs\Puppet\bin\puppet.bat' agent --test --no-daemonize --verbose --logdest console}";
p.StartInfo.Verb = "runas";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
p.Start();
while (p.HasExited == false) {
Console.WriteLine(p.StandardOutput.ReadLine());
}
Console.ReadLine();
p.WaitForExit();
p.Close();
}
}
}

Firebird custom installation

I want to deploy a firebird installation, and thus will launch it from my installer using command-line parameters. I read Inno Setup's documentation but still can't get it to work.
I just want to install a "Super server" with no documentation or whatsoever.
Here's what I have so far
Firebird-2.1.2.18118_0_Win32.exe /sp- /silent /SUPPRESSMSGBOXES /nocancel /noicons /components="Super Server binary"
But it won't install the server. If I remove the /components it does install the server but install other developer stuff, which customers don't need.
read installation_scripted.txt in C:\Program Files\Firebird\Firebird_2_1\doc
/COMPONENTS="comma separated list of
component names"
Choose from -
ServerComponent\SuperServerComponent,
ServerComponent\ClassicServerComponent,
ServerComponent,
DevAdminComponent and
ClientComponent
Overrides the default components
settings. Using this command line
parameter causes Setup to
automatically select a custom type. A
full install requires combining
components. For example:
/COMPONENTS="ServerComponent\SuperServerComponent,ServerComponent,DevAdminComponent,ClientComponent"
would be required for a full
install.
I use the following and it works fine, however I need to install to a custom directory and also change the server option
string installerFilePath = #"C:\BennaOlivier\Randoms\Delter\Firebird\FirebirdMainInstaller\MainInstaller\MainInstaller\Firebird X64\FirebirdInstallX64\Firebird-2.5x64.exe";
Process installerProcess = new Process();
installerProcess = Process.Start(installerFilePath, Arguments);
while (installerProcess.HasExited == false)
{
//indicate progress to user
Application.DoEvents();
System.Threading.Thread.Sleep(250);
}
}
catch (Exception FBX64)
{
MessageBox.Show(FBX64.Message);
throw;
}public const string comps = #"ServerComponent\ClassicServerComponent,ServerComponent,ClientComponent";
public const string Arguments = "/VERYSILENT /SUPPRESSMSGBOXES";

How to check if a file has a digital signature

I'd like to check programatically if a file has been digitally signed or not.
For the moment, I found a rather obscure Microsoft code, that doesn't compile...
Any idea on the subject?
An external tool with command line would also be great, by the way.
The important missing part of the answer mentioning signtool is:
Yes, with the well known signtool.exe you can also find out, if a file is signed. No need to download another tool!
E.g. with the simple line:
signtool verify /pa myfile.exe
if %ERRORLEVEL% GEQ 1 echo This file is not signed.
(For verbose output, add a /v after /pa.)
One may ask: Why this is important? I just sign the files (again) which shall be signed and it works.
My objective is to keep builds clean, and don't sign files a second time because not only the date is changed, but the is binary different after that.
Business example:
My client has a streamlined automated "dev ops" kind build and post build process. There are multiple sources for different file sets, and at the end all is build, tested and bundled to distribution- and for that some files have to be signed. To guarantee that some files don't leave the unit without being signed, we used to sign all important files found on the media, even if they were already signed.
But this hasn´t been clean enough ! Generally:
If we sign a file again, which is already signed, the file date and binary fingerprint changes, and the file looses comparability with it's sources, if it was simply copied.
(At least if you sign with a timestamp, which we always do and I think is highly recommended.)
This is a severe quality loss, because this file is no longer identical to it's predecessors although the file itself has not changed.
If we sign a file again, this also could be a fault when it is a third party file which shouldn't be signed by our company.
You can avoid both by making the signing itself conditional depending on the return code of the preceding signtool verify call mentioned.
Download Sigcheck and use the following command.
sigcheck.exe -a -u -e
An example of a signed dll
File version: 0.0.0.0
Strong Name: Signed
An example of an unsigned dll
File version: 0.0.0.0
Strong Name: Unsigned
Sigcheck is a command-line utility that shows file version number. Good Luck
I found another option (pure .NET code) on the web here.
The code is very simple and works.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
internal class Program
{
private static void Main(string[] args)
{
string filePath = args[0];
if (!File.Exists(filePath))
{
Console.WriteLine("File not found");
return;
}
X509Certificate2 theCertificate;
try
{
X509Certificate theSigner = X509Certificate.CreateFromSignedFile(filePath);
theCertificate = new X509Certificate2(theSigner);
}
catch (Exception ex)
{
Console.WriteLine("No digital signature found: " + ex.Message);
return;
}
bool chainIsValid = false;
/*
*
* This section will check that the certificate is from a trusted authority IE
* not self-signed.
*
*/
var theCertificateChain = new X509Chain();
theCertificateChain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
/*
*
* Using .Online here means that the validation WILL CALL OUT TO THE INTERNET
* to check the revocation status of the certificate. Change to .Offline if you
* don't want that to happen.
*/
theCertificateChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
theCertificateChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
theCertificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
chainIsValid = theCertificateChain.Build(theCertificate);
if (chainIsValid)
{
Console.WriteLine("Publisher Information : " + theCertificate.SubjectName.Name);
Console.WriteLine("Valid From: " + theCertificate.GetEffectiveDateString());
Console.WriteLine("Valid To: " + theCertificate.GetExpirationDateString());
Console.WriteLine("Issued By: " + theCertificate.Issuer);
}
else
{
Console.WriteLine("Chain Not Valid (certificate is self-signed)");
}
}
}
Since PowerShell 5.1, you can use Get-AuthenticodeSignature to verify the signature of a binary or a PowerShell script.
> Get-AuthenticodeSignature -FilePath .\MyFile.exe
SignerCertificate Status Path
----------------- ------ ----
A59E92E31475F813DDAF41C3CCBC8B78 Valid MyFile.exe
Or
> (Get-AuthenticodeSignature -FilePath .\MyFile.exe).Status
Valid
If you need an external tool, you can use signtool.exe. It is part of the Windows SDK, it takes command line arguments, and you can find out more about it here, http://msdn.microsoft.com/en-us/library/aa387764.aspx
Also you can try to use npm package sign-check for that purposes.
This package implements WinVerifyTrust API and has simple usage:
npm install -g sign-check
sign-check 'path/to/file'
Select the <*>.exe rightclick >properties. if the file is signed then you will get this tab on the property windows of that file.