internal static Signature SignFile(SigningOption option, string fileName, X509Certificate2 certificate, string timeStampServerUrl, string hashAlgorithm) { bool result = false; Signature signature = null; IntPtr pSignInfo = IntPtr.Zero; DWORD error = 0; string hashOid = null; Utils.CheckArgForNullOrEmpty(fileName, "fileName"); Utils.CheckArgForNull(certificate, "certificate"); // If given, TimeStamp server URLs must begin with http:// if (!String.IsNullOrEmpty(timeStampServerUrl)) { if ((timeStampServerUrl.Length <= 7) || (timeStampServerUrl.IndexOf("http://", StringComparison.OrdinalIgnoreCase) != 0)) { throw PSTraceSource.NewArgumentException( "certificate", Authenticode.TimeStampUrlRequired); } } // Validate that the hash algorithm is valid if (!String.IsNullOrEmpty(hashAlgorithm)) { IntPtr intptrAlgorithm = Marshal.StringToHGlobalUni(hashAlgorithm); IntPtr oidPtr = NativeMethods.CryptFindOIDInfo(NativeConstants.CRYPT_OID_INFO_NAME_KEY, intptrAlgorithm, 0); // If we couldn't find an OID for the hash // algorithm, it was invalid. if (oidPtr == IntPtr.Zero) { throw PSTraceSource.NewArgumentException( "certificate", Authenticode.InvalidHashAlgorithm); } else { NativeMethods.CRYPT_OID_INFO oidInfo = ClrFacade.PtrToStructure <NativeMethods.CRYPT_OID_INFO>(oidPtr); hashOid = oidInfo.pszOID; } } if (!SecuritySupport.CertIsGoodForSigning(certificate)) { throw PSTraceSource.NewArgumentException( "certificate", Authenticode.CertNotGoodForSigning); } SecuritySupport.CheckIfFileExists(fileName); //SecurityUtils.CheckIfFileSmallerThan4Bytes(fileName); try { // CryptUI is not documented either way, but does not // support empty strings for the timestamp server URL. // It expects null, only. Instead, it randomly AVs if you // try. string timeStampServerUrlForCryptUI = null; if (!String.IsNullOrEmpty(timeStampServerUrl)) { timeStampServerUrlForCryptUI = timeStampServerUrl; } // // first initialize the struct to pass to // CryptUIWizDigitalSign() function // NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO si = NativeMethods.InitSignInfoStruct(fileName, certificate, timeStampServerUrlForCryptUI, hashOid, option); pSignInfo = Marshal.AllocCoTaskMem(Marshal.SizeOf(si)); Marshal.StructureToPtr(si, pSignInfo, false); // // sign the file // // The GetLastWin32Error of this is checked, but PreSharp doesn't seem to be // able to see that. #pragma warning disable 56523 result = NativeMethods.CryptUIWizDigitalSign( (DWORD)NativeMethods.CryptUIFlags.CRYPTUI_WIZ_NO_UI, IntPtr.Zero, IntPtr.Zero, pSignInfo, IntPtr.Zero); #pragma warning enable 56523 if (si.pSignExtInfo != null) { ClrFacade.DestroyStructure <NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO>(si.pSignExtInfo); Marshal.FreeCoTaskMem(si.pSignExtInfo); } if (!result) { error = GetLastWin32Error(); // // ISSUE-2004/05/08-kumarp : there seems to be a bug // in CryptUIWizDigitalSign(). // It returns 80004005 or 80070001 // but it signs the file correctly. Mask this error // till we figure out this odd behavior. // if ((error == 0x80004005) || (error == 0x80070001) || // CryptUIWizDigitalSign introduced a breaking change in Win8 to return this // error code (ERROR_INTERNET_NAME_NOT_RESOLVED) when you provide an invalid // timestamp server. It used to be 0x80070001. // Also masking this out so that we don't introduce a breaking change ourselves. (error == 0x80072EE7) ) { result = true; } else { if (error == Win32Errors.NTE_BAD_ALGID) { throw PSTraceSource.NewArgumentException( "certificate", Authenticode.InvalidHashAlgorithm); } s_tracer.TraceError("CryptUIWizDigitalSign: failed: {0:x}", error); } } if (result) { signature = GetSignature(fileName, null); } else { signature = new Signature(fileName, (DWORD)error); } } finally { ClrFacade.DestroyStructure <NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO>(pSignInfo); Marshal.FreeCoTaskMem(pSignInfo); } return(signature); }
private static Signature GetSignatureFromCatalog(string filename) { if (Signature.CatalogApiAvailable.HasValue && !Signature.CatalogApiAvailable.Value) { // Signature.CatalogApiAvailable would be set to false the first time it is detected that // WTGetSignatureInfo API does not exist on the platform, or if the API is not functional on the target platform. // Just return from the function instead of revalidating. return(null); } Signature signature = null; Utils.CheckArgForNullOrEmpty(filename, "fileName"); SecuritySupport.CheckIfFileExists(filename); try { using (FileStream stream = File.OpenRead(filename)) { NativeMethods.SIGNATURE_INFO sigInfo = new NativeMethods.SIGNATURE_INFO(); sigInfo.cbSize = (uint)Marshal.SizeOf(sigInfo); IntPtr ppCertContext = IntPtr.Zero; IntPtr phStateData = IntPtr.Zero; try { int hresult = NativeMethods.WTGetSignatureInfo(filename, stream.SafeFileHandle.DangerousGetHandle(), NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CATALOG_SIGNED | NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CATALOG_FIRST | NativeMethods.SIGNATURE_INFO_FLAGS.SIF_AUTHENTICODE_SIGNED | NativeMethods.SIGNATURE_INFO_FLAGS.SIF_BASE_VERIFICATION | NativeMethods.SIGNATURE_INFO_FLAGS.SIF_CHECK_OS_BINARY, ref sigInfo, ref ppCertContext, ref phStateData); if (Utils.Succeeded(hresult)) { DWORD error = GetErrorFromSignatureState(sigInfo.nSignatureState); X509Certificate2 cert = null; if (ppCertContext != IntPtr.Zero) { cert = new X509Certificate2(ppCertContext); // Get the time stamper certificate if available TryGetProviderSigner(phStateData, out IntPtr pProvSigner, out X509Certificate2 timestamperCert); if (timestamperCert != null) { signature = new Signature(filename, error, cert, timestamperCert); } else { signature = new Signature(filename, error, cert); } switch (sigInfo.nSignatureType) { case NativeMethods.SIGNATURE_INFO_TYPE.SIT_AUTHENTICODE: signature.SignatureType = SignatureType.Authenticode; break; case NativeMethods.SIGNATURE_INFO_TYPE.SIT_CATALOG: signature.SignatureType = SignatureType.Catalog; break; } if (sigInfo.fOSBinary == 1) { signature.IsOSBinary = true; } } else { signature = new Signature(filename, error); } if (!Signature.CatalogApiAvailable.HasValue) { string productFile = Path.Combine(Utils.DefaultPowerShellAppBase, "Modules\\PSDiagnostics\\PSDiagnostics.psm1"); if (signature.Status != SignatureStatus.Valid) { if (string.Equals(filename, productFile, StringComparison.OrdinalIgnoreCase)) { Signature.CatalogApiAvailable = false; } else { // ProductFile has to be Catalog signed. Hence validating // to see if the Catalog API is functional using the ProductFile. Signature productFileSignature = GetSignatureFromCatalog(productFile); Signature.CatalogApiAvailable = (productFileSignature != null && productFileSignature.Status == SignatureStatus.Valid); } } } } else { // If calling NativeMethods.WTGetSignatureInfo failed (returned a non-zero value), we still want to set Signature.CatalogApiAvailable to false. Signature.CatalogApiAvailable = false; } } finally { if (phStateData != IntPtr.Zero) { NativeMethods.FreeWVTStateData(phStateData); } if (ppCertContext != IntPtr.Zero) { NativeMethods.CertFreeCertificateContext(ppCertContext); } } } } catch (TypeLoadException) { // If we don't have WTGetSignatureInfo, don't return a Signature. Signature.CatalogApiAvailable = false; return(null); } return(signature); }
internal static System.Management.Automation.Signature SignFile(SigningOption option, string fileName, X509Certificate2 certificate, string timeStampServerUrl, string hashAlgorithm) { bool flag = false; System.Management.Automation.Signature signature = null; IntPtr zero = IntPtr.Zero; uint error = 0; string pszOID = null; Utils.CheckArgForNullOrEmpty(fileName, "fileName"); Utils.CheckArgForNull(certificate, "certificate"); if (!string.IsNullOrEmpty(timeStampServerUrl) && ((timeStampServerUrl.Length <= 7) || (timeStampServerUrl.IndexOf("http://", StringComparison.OrdinalIgnoreCase) != 0))) { throw PSTraceSource.NewArgumentException("certificate", "Authenticode", "TimeStampUrlRequired", new object[0]); } if (!string.IsNullOrEmpty(hashAlgorithm)) { IntPtr pvKey = Marshal.StringToHGlobalUni(hashAlgorithm); IntPtr ptr = System.Management.Automation.Security.NativeMethods.CryptFindOIDInfo(2, pvKey, 0); if (ptr == IntPtr.Zero) { throw PSTraceSource.NewArgumentException("certificate", "Authenticode", "InvalidHashAlgorithm", new object[0]); } System.Management.Automation.Security.NativeMethods.CRYPT_OID_INFO crypt_oid_info = (System.Management.Automation.Security.NativeMethods.CRYPT_OID_INFO)Marshal.PtrToStructure(ptr, typeof(System.Management.Automation.Security.NativeMethods.CRYPT_OID_INFO)); pszOID = crypt_oid_info.pszOID; } if (!SecuritySupport.CertIsGoodForSigning(certificate)) { throw PSTraceSource.NewArgumentException("certificate", "Authenticode", "CertNotGoodForSigning", new object[0]); } SecuritySupport.CheckIfFileExists(fileName); try { string str2 = null; if (!string.IsNullOrEmpty(timeStampServerUrl)) { str2 = timeStampServerUrl; } System.Management.Automation.Security.NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO structure = System.Management.Automation.Security.NativeMethods.InitSignInfoStruct(fileName, certificate, str2, pszOID, option); zero = Marshal.AllocCoTaskMem(Marshal.SizeOf(structure)); Marshal.StructureToPtr(structure, zero, false); flag = System.Management.Automation.Security.NativeMethods.CryptUIWizDigitalSign(1, IntPtr.Zero, IntPtr.Zero, zero, IntPtr.Zero); Marshal.DestroyStructure(structure.pSignExtInfo, typeof(System.Management.Automation.Security.NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO)); Marshal.FreeCoTaskMem(structure.pSignExtInfo); if (!flag) { error = GetLastWin32Error(); switch (error) { case 0x80004005: case 0x80070001: case 0x80072ee7: flag = true; goto Label_01CF; case 0x80090008: throw PSTraceSource.NewArgumentException("certificate", "Authenticode", "InvalidHashAlgorithm", new object[0]); } tracer.TraceError("CryptUIWizDigitalSign: failed: {0:x}", new object[] { error }); } Label_01CF: if (flag) { return(GetSignature(fileName, null)); } signature = new System.Management.Automation.Signature(fileName, error); } finally { Marshal.DestroyStructure(zero, typeof(System.Management.Automation.Security.NativeMethods.CRYPTUI_WIZ_DIGITAL_SIGN_INFO)); Marshal.FreeCoTaskMem(zero); } return(signature); }
private static Signature GetSignatureFromMSSecurityExtensions(string filename) { #if UNIX return(null); #else if (Signature.CatalogApiAvailable.HasValue && !Signature.CatalogApiAvailable.Value) { return(null); } Utils.CheckArgForNullOrEmpty(filename, "fileName"); SecuritySupport.CheckIfFileExists(filename); Signature signature = null; FileSignatureInfo fileSigInfo; using (FileStream fileStream = File.OpenRead(filename)) { try { fileSigInfo = FileSignatureInfo.GetFromFileStream(fileStream); System.Diagnostics.Debug.Assert(fileSigInfo is not null, "Returned FileSignatureInfo should never be null."); } catch (Exception) { // For any API error, enable fallback to WinVerifyTrust APIs. Signature.CatalogApiAvailable = false; return(null); } } DWORD error = GetErrorFromSignatureState(fileSigInfo.State); if (fileSigInfo.SigningCertificate is null) { signature = new Signature(filename, error); } else { signature = fileSigInfo.TimestampCertificate is null ? new Signature(filename, error, fileSigInfo.SigningCertificate) : new Signature(filename, error, fileSigInfo.SigningCertificate, fileSigInfo.TimestampCertificate); } switch (fileSigInfo.Kind) { case SignatureKind.None: signature.SignatureType = SignatureType.None; break; case SignatureKind.Embedded: signature.SignatureType = SignatureType.Authenticode; break; case SignatureKind.Catalog: signature.SignatureType = SignatureType.Catalog; break; default: System.Diagnostics.Debug.Fail("Signature type can only be None, Authenticode or Catalog."); break; } signature.IsOSBinary = fileSigInfo.IsOSBinary; if (signature.SignatureType == SignatureType.Catalog && !Signature.CatalogApiAvailable.HasValue) { Signature.CatalogApiAvailable = fileSigInfo.State != SignatureState.Invalid; } return(signature); #endif }