如何创建一个最小的虚拟 X509Certificate2?
Posted
技术标签:
【中文标题】如何创建一个最小的虚拟 X509Certificate2?【英文标题】:How to create a minimal dummy X509Certificate2? 【发布时间】:2011-10-13 18:07:48 【问题描述】:我正在对 .NET 应用程序进行单元测试;一些单元测试涉及以编程方式生成 X509Certificate2 对象。
我不关心实际的签名/私钥/验证内容,我只想拥有一个在检查其字段时不会引发异常的对象。我尝试使用无参数构造函数,但随后一大堆字段在访问时抛出异常。在调试器中可以看到:
SubjectName = '(new System.Collections.Generic.Mscorlib_CollectionDebugView(result.Certificates)).Items[0].SubjectName' 引发了“System.Security.Cryptography.CryptographicException”类型的异常
我也尝试传递一个包含一些随机数的字节数组,但它甚至没有构造(数组是否需要特定大小?)
那么,问题:以编程方式生成不会在字段/属性访问时引发异常的 X509Certificate2 对象的最简单(最少的代码行)方法是什么?
【问题讨论】:
什么是痣?您能否提供最终解决方案作为答案?我现在正在尝试做同样的事情。 我认为他的意思是一个用于创建测试存根的框架和 Microsoft 研究中的弯路。你可以看这里:research.microsoft.com/en-us/projects/moles. 【参考方案1】:我建议如下:
-
使用makecert 生成证书。
将证书添加到您的项目并将其构建操作更改为嵌入式资源。
从单元测试设置中的资源加载证书,见下文。
代码:
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");
此时,您应该可以与证书进行交互了。如果您的单元测试有不同的需求,您可以创建不同的变体。
【讨论】:
我想我实际上可以读取该字节数组一次,将其作为字符打印到文件中,然后将其作为硬拷贝粘贴到源代码中编码字节[]?那不会太糟糕... @JasonShantz 我不明白为什么问这个问题的人将您的答案标记为“已回答”而没有解释什么是“MyType”??? @anhtv13 我相信 MyType 是项目中包含自签名证书作为嵌入式资源的 C# 类/类型的占位符。这允许对程序集的引用,这反过来又允许将资源作为流读取到 embeddedCert 字节数组中。一旦你有了这个字节数组,你就可以为 X509Certificate2 msdn.microsoft.com/en-us/library/ms148413(v=vs.110).aspx 使用适当的构造函数 在这方面,MyType 是与资源在同一个项目中的任意对象,如果您将证书保存在同一个项目中,它可能是 UnitTest 类本身。 注意:“YourProjectName”应该是命名空间(以防项目名称和命名空间不同)。使用 thisAssembly.GetManifestResourceNames() 进行检查。【参考方案2】:这可能看起来很老套,这取决于你想变得多么务实……我使用的一种方法是从机器上随机获取一个证书。
这在以下情况下很好: - 我知道每台运行这些测试的机器都有一个有效的证书。 - 我正在使用 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();
【讨论】:
这非常适合测试。顺便说一句,我们必须提防密码吗?【参考方案3】:应兰登的要求,我自己使用的解决方案:
//Moling test Certificate
var cert = new MX509Certificate2();
var subject = new MX500DistinguishedName();
// hookup
cert.SubjectNameGet = () => subject;
cert.ThumbprintGet = () => "foo";
subject.NameGet = () => "foobar";
这很有效,因为我在证书中访问的唯一字段是 SubjectName 和 Thumbprint,而我访问的 SubjectName 的唯一字段是名称。我“模拟”了这些字段的 getter 方法以返回虚拟字符串。如果您要访问其他字段,您可能需要将它们调入。
那么,什么是“痣”?
“Moles 是 .NET 中基于委托的测试存根和绕行的轻量级框架。Moles 可用于绕行任何 .NET 方法,包括密封类型中的非虚拟/静态方法。Moles 可在Visual Studio 库或与 Pex 捆绑在一起。”
http://research.microsoft.com/en-us/projects/moles/
【讨论】:
接受 Jason 的回答胜过我的回答,因为他的回答更笼统。【参考方案4】:还有一种更简单的方法。
-
使用 MakeCert 创建证书或导出任何现有证书。
在项目(App_Data 文件夹)中添加证书。
按照以下步骤获取证书。
代码:
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");
【讨论】:
以上是关于如何创建一个最小的虚拟 X509Certificate2?的主要内容,如果未能解决你的问题,请参考以下文章
如何以编程方式创建有效的自签名 X509Certificate2,而不是从 .NET Core 中的文件加载