【问题标题】:Validate certificate password using X509Certificate2使用 X509Certificate2 验证证书密码
【发布时间】:2015-04-12 02:25:22
【问题描述】:

我想验证证书密码。目前我有基于处理 CryptographicException 和检查异常消息的代码。但这种方法依赖于英语文化信息。

    public bool VerifyPassword(byte[] fileContent, string password)
    {
        try
        {
            var certificate = new X509Certificate2(fileContent, password);
        }
        catch (CryptographicException ex)
        {
            if (ex.Message.StartsWith("The specified network password is not correct."))
            {
                return false;
            }

            throw;
        }

        return true;
    }

我一直在寻找如何验证证书密码的其他解决方案,但没有成功。

验证证书密码的正确方法是什么?

我会很感激任何想法...

【问题讨论】:

    标签: .net exception-handling certificate x509certificate2


    【解决方案1】:

    我会使用 PFXVerifyPasswordPFXIsPFXBlob 原生函数。虽然,它需要一个 p/invoke,但它是一个真正的交易。

    C#签名和示例代码:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    
    namespace ClassLibrary1 {
        class CryptoAPI {
            [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern Boolean PFXIsPFXBlob(
                [In]CRYPTOAPI_BLOB pPFX
            );
            [DllImport("Crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern Boolean PFXVerifyPassword(
                [In] CRYPTOAPI_BLOB pPFX,
                [MarshalAs(UnmanagedType.LPWStr)]
                [In] String szPassword,
                [In] UInt32 dwFlags
            );
            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
            public struct CRYPTOAPI_BLOB {
                public UInt32 cbData;
                public IntPtr pbData;
            }
        }
    
        public class Program {
            public static Boolean TestPfxPwd(Byte[] rawData, String password) {
                // check for input data
                if (rawData == null) { throw new ArgumentNullException("rawData"); }
                // allocate a buffer in an unmanaged memory to store PFX content
                IntPtr pbData = Marshal.AllocHGlobal(rawData.Length);
                // copy PFX content to allocated buffer
                Marshal.Copy(rawData, 0, pbData, rawData.Length);
                // instantiate CRYPTOAPI_BLOB structure as it will be used
                // to call both functions
                CryptoAPI.CRYPTOAPI_BLOB blob = new CryptoAPI.CRYPTOAPI_BLOB {
                    cbData = (UInt32)rawData.Length,
                    pbData = pbData
                };
                // determine if input byte array represents a PFX blob:
                if (!CryptoAPI.PFXIsPFXBlob(blob)) {
                    // release unmanaged resources before leaving method
                    Marshal.FreeHGlobal(pbData);
                    throw new InvalidDataException("Input data is not valid PFX message.");
                }
                // call the PFXVerifyPassword function and store results in a temporary variable
                Boolean retValue = CryptoAPI.PFXVerifyPassword(blob, password, 0);
                // release unmanaged resources before leaving method
                Marshal.FreeHGlobal(pbData);
                // return pfx match status
                return retValue;
            }
        }
    }
    

    【讨论】:

    • 感谢您的示例。我不知道本机 Windows api。但是我的公司(我工作的)有限制避免使用本机 Windows api。
    • 我认为原生函数没有什么大问题,因为大约一半的 .NET(高达 90% 的 Cryptography 命名空间)是原生函数的包装器。
    • 我明白了。但特别是 X509Certificate2 类在 Mono 中有自己的实现。而且我们的客户不应该依赖于windows环境。这不是我的想法。这是生意。另一种观点是你有什么保证微软不会改变原生API?
    • 关于保证:您在哪里保证 Mono 或 .NET 不会更改其 API? .NET 和 Mono 更改其 API 的机会比 Microsoft 更改本机功能的可能性更大。
    【解决方案2】:

    由于我工作的公司不允许使用本机 Windows API,我有一个解决方案 -> 使用 InvariantCulture 在新线程上运行验证。它适用于所有 .Net 语言突变。

    下面是代码示例:

        public bool VerifyPassword(byte[] fileContent, string password)
        {
            CheckParameters(fileContent, password);
            var isPasswordVerified = false;
    
            var verificationThread = new Thread(() => isPasswordVerified = VerifyPasswordWithUsCulture(fileContent, password))
            {
                CurrentUICulture =  CultureInfo.InvariantCulture
            };
    
            verificationThread.Start();
            verificationThread.Join();
    
            return isPasswordVerified;
        }
    
        static bool VerifyPasswordWithUsCulture(byte[] fileContent, string password)
        {
            try
            {
                // ReSharper disable once UnusedVariable
                var certificate = new X509Certificate2(fileContent, password);
            }
            catch (CryptographicException ex)
            {
                if (ex.Message.StartsWith("The specified network password is not correct."))
                {
                    return false;
                }
    
                throw;
            }
    
            return true;
        }
    

    【讨论】:

      【解决方案3】:

      几个月后,我找到了更好的解决方案(也许是最好的)。它基于 CryptograhpicExcaption 的 HResult 值。

      static bool VerifyPassword(byte[] fileContent, string password)
      {
          try
          {
              // ReSharper disable once UnusedVariable
              var certificate = new X509Certificate2(fileContent, password);
          }
          catch (CryptographicException ex)
          {
              if ((ex.HResult & 0xFFFF) == 0x56) 
              { 
                  return false;
              };
      
              throw;
          }
      
          return true;
      }
      

      所有 HResults(系统错误代码)文档均可在以下位置获得:https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-07-14
        • 2018-05-02
        • 2020-03-23
        • 1970-01-01
        • 2020-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多