如何创建一个最小化的虚拟 X509Certificate2?

25

我正在进行一个.NET应用程序的单元测试;其中一些单元测试涉及以编程方式生成X509Certificate2对象。

我不关心实际的签名/私钥/验证等内容,我只想拥有一个在检查其字段时不会抛出异常的对象。我尝试使用无参数构造函数,但是当访问其字段时,一堆字段会抛出异常。如调试器所示:

SubjectName =“(new System.Collections.Generic.Mscorlib_CollectionDebugView(result.Certificates))。Items [0] .SubjectName”引发了“System.Security.Cryptography.CryptographicException”类型的异常

我还尝试传递带有一些随机数字的字节数组,但那甚至没有构造(数组需要特定大小吗?)

所以,问题是:以最简单的方式(最少的代码行)以编程方式生成X509Certificate2对象,这样在访问其字段/属性时不会引发异常?


什么是Moles?您能把最终的解决方案作为答案提供吗?我现在正在尝试做相同的事情。 - Langdon
我认为他指的是微软研究中心用于创建测试存根和拦截的框架。您可以在此处查看:http://research.microsoft.com/en-us/projects/moles/. - Sascha
4个回答

12

我建议如下操作:

  1. 使用makecert生成证书。
  2. 将证书添加到您的项目中,并将其构建操作更改为“嵌入式资源”。
  3. 在单元测试设置中从资源加载证书,如下所示。

代码:

byte[] embeddedCert;
Assembly thisAssembly = Assembly.GetAssembly(typeof(MyType));
using (Stream certStream = thisAssembly.GetManifestResourceStream("YourProjectName.localhost.pfx"))
{
  embeddedCert = new byte[certStream.Length];
  certStream.Read(embeddedCert, 0, (int)certStream.Length);
}

_signingCert = new X509Certificate2(embeddedCert, "password");

目前为止,就与证书进行交互而言,你应该已经可以顺利进行了。如果你的单元测试有不同的需求,你可以创建不同的变体。


1
我想我可以只读取那个字节数组一次,将其作为字符打印到文件中,然后将其复制粘贴到源代码中作为硬编码的byte[]?那不会太糟糕... - Cephron
1
@JasonShantz,我不理解为什么那些提问者在没有解释"我的类型"是什么的情况下就将你的回答标记为“已回答”??? - anhtv13
1
@anhtv13 我相信MyType是项目中C#类/类型的占位符,其中包含自签名证书作为嵌入式资源。这允许引用程序集,从而允许将资源作为流读取到embeddedCert字节数组中。一旦你有了这个字节数组,你就可以使用适当的X509Certificate2构造函数 https://msdn.microsoft.com/en-us/library/ms148413(v=vs.110).aspx - pwdst
1
在这方面,MyType是与资源在同一项目中的任意对象,如果您将证书保留在同一项目中,则可以是UnitTest类本身。 - pwdst
2
注意:在使用此代码时,请将“YourProjectName”替换为命名空间(如果项目名称和命名空间不同)。可以使用thisAssembly.GetManifestResourceNames()来检查。 - heringer
显示剩余2条评论

5
这可能看起来很不专业,具体取决于您想要多实用...我使用的方法是从机器中随机获取一个证书。
这种方法适用于以下情况: - 我知道运行这些测试的每台机器都有有效的证书。 - 我在使用GIT时不想为证书检入二进制文件。 - 我不关心证书内容。 - 我正在使用不能模拟且明确需要非可模拟X509Certificate对象的代码。
绝对不是万无一失的,但它解除了我的阻塞和测试场景的阻塞。
    static X509Certificate2 GetRandomCertificate()
    {
        X509Store st = new X509Store(StoreName.My, StoreLocation.LocalMachine);
        st.Open(OpenFlags.ReadOnly);
        try
        {
            var certCollection = st.Certificates;

            if (certCollection.Count == 0)
            {
                return null;
            }
            return certCollection[0];
        }
        finally
        {
            st.Close();
        }
    }

1
这对于测试非常棒。顺便问一下,我们需要注意密码吗? - Timo

1

根据Langdon的要求,我自己使用的解决方案:

        //Moling test Certificate
        var cert = new MX509Certificate2();
        var subject = new MX500DistinguishedName();
        // hookup
        cert.SubjectNameGet = () => subject;
        cert.ThumbprintGet = () => "foo";
        subject.NameGet = () => "foobar";

这个方法可行是因为我只访问了证书中的SubjectName和Thumbprint字段,而且我只访问了SubjectName字段中的名称。我“moled”了这些字段的getter方法以返回虚拟字符串。如果您要访问其他字段,则可能需要进行模拟。

那么,“Moles”是什么?

“Moles是一个基于委托的.NET轻量级测试存根和重定向框架。 Moles可用于重定向任何.NET方法,包括密封类型中的非虚拟/静态方法。 Moles可以在Visual Studio Gallery上免费获取或与Pex捆绑使用。”

http://research.microsoft.com/en-us/projects/moles/


1
选择接受Jason的答案而不是我的,因为他的更加通用。 - Cephron

0

还有一种更简单的方法。

  1. 使用MakeCert创建证书或导出任何现有证书。
  2. 将证书添加到项目(App_Data文件夹)中。
  3. 按照以下步骤获取证书。

代码:

        string certificate = "cert.pfx";
        string certPath = string.Format("{0}/App_Data/{1}", HttpRuntime.AppDomainAppPath, certificate);
        byte[] bytes = Util.FileToArray(certPath);
        X509Certificate2 publicKey = new X509Certificate2(bytes, "somepassoword");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接