更改日志
我有一段代码正在编写用于标记xml的单元测试。
在开发代码时,我注意到.NET 4.7.1+将抛出CryptographicException: Invalid algorithm specified
。为了解决这个问题,我使用了以下AppContext
开关(来源:wiktor zychla,why am i getting invalid algorithm specified...)。
我已将以下代码放入任何调用xml签名代码的应用程序中:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
在野外运行时,此代码正常运行,并且我的xml正按预期方式签名。
我已经为该代码编写了一些单元测试,这是我第一次真正尝试编写单元测试,并且遇到了一些奇怪的情况。
try
{
// Compute the signature.
signedXml.ComputeSignature();
}
catch (CryptographicException cex)
{
//Contains instead of == since this message contains newline characters...
if (cex.Message.Contains("Invalid algorithm specified"))
{
string code = Environment.NewLine + "AppContext.SetSwitch(\"Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms\", true);" +
Environment.NewLine + "AppContext.SetSwitch(\"Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms\", true); ";
throw new Exception($"Cryptographic exception was thrown, ensure that the following switches are set in the executing assembly: {code}", cex);
}
else
{
throw cex;
}
}
因此,如果引发了异常,那么我会显示一条很好的消息,说请在调用此方法之前添加以下代码。
我的测试课看起来像这样:
[TestFixture]
public class XMLSigningTests
{
protected IXMLSigner _xmlSigner;
protected byte[] _publicKey;
protected string _password;
[SetUp]
public void Init()
{
_xmlSigner = new XMLSigner();
_password = "test";
_publicKey = GenerateTestCert(_password);
}
[TearDown]
public void CleanUp()
{
_xmlSigner = null;
_publicKey = null;
_password = null;
}
[Test]
public void SignXML_AppContextSwitchNotSet_Fail()
{
XMLDocument xmlDoc = GenerateXML();
Assert.Throws<Exception>(() => { _xmlSigner.SignXML(xmlDoc, _publicKey, _password); });
}
[Test]
public void SignXML_AppContextSwitchSet_Success()
{
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
XMLDocument xmlDoc = GenerateXML();
var signedXml = _xmlSigner.SignXML(xmlDoc, _publicKey, _password);
Assert.IsFalse(signedXML == null);
//More validation here
}
private XMLDocument GenerateXML()
{
return new XMLDocument("Some fancy xml here...");
}
private byte[] GenerateTestCert(string password)
{
//Use Pluralsight.Crypto to generate a self signed cert & export as .pfx
return cert.Export(X509ContentType.Pfx, password);
}
}
当我运行自己希望通过的SignXML_AppContextSwitchSet_Success
时,每次都会通过测试(只是我反复运行它)。如果我清洗解决方案并运行所有测试,则测试始终会失败。
我的xml生成类没有共享对象,它实际上可能是静态的。
测试失败是因为抛出了加密异常(触发我的代码并抛出异常告诉开发人员(我)添加应用上下文开关)。
有没有办法验证配置开关是否受到尊重?
在我的控制台测试器应用程序和我内置的wpf应用程序中运行代码时,永远不会引发异常。仅当我背对背运行所有单元测试时才抛出该错误。我希望这些测试可以在蔚蓝的管道上运行,因此它必须同时运行也可以分别运行很重要。
我对这个异常的想法是,也许当我同时运行所有测试时,AppContext
开关没有被兑现。我已经尝试在签署xml之前添加检查:
AppContext.TryGetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", out bool xmlSwitch);
AppContext.TryGetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", out bool pkcsSwitch);
if (!xmlSwitch || !pkcsSwitch)
{
throw new NotImplementedException("THESE VALUES SHOULD BE SET WTF");
}
现在,您希望该代码无法通过我的失败测试(未设置开关),并且确实失败了,您可以在测试浏览器中看到我粗略的NotImplementedException
消息。但是,我期望测试会成功再次抛出CryptographicException
...
System.Exception : Cryptographic exception was thrown, ensure that the following switches are set in the executing assembly:
AppContext.SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);
AppContext.SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);
----> System.Security.Cryptography.CryptographicException : Invalid algorithm specified.
正如我之前所说,我对测试非常陌生,我不确定我的测试设计方法是否错误,如果我只是在后台测试意大利面条式代码,那么也许我注定要失败尝试对其进行测试,或者缺少我所允许的正确测试方法。
尽管我最初使用NUnit 3.12.0 + NUnit3TestAdapter 3.15.1
并在两个框架中都经历了相同的行为,但我目前正在使用MSTest 1.3.2
编写测试。
我发现以下测试的执行顺序决定了成功:
我通过阅读(Unit test fails when running all, but passes when running individually)之类的帖子来发现这一点,因为它们在不单独运行时测试失败。
这让我想到了要设置的值,我想要以下几种情况:
//Test I expect to fail
AppContext.SetSwitch("the switch..", false);
//Test I expect to pass
AppContext.SetSwitch("the switch..", true);
尽管我已经测试了这些值的设置是否正确,但是底层开关是否正在更新?如何清除这些基础对象?
我在NUnit's GitHub (appdomain per test (isolation per test), for dealing with static classes)上发现了以下问题。他们在该线程中讨论了每个测试使用新AppDomain
的选项,这正是我所需要的。
在线程jnm2下的一种方式发布了一个util类,以在单独的AppDomain
(code)中运行代码(由于这篇文章已经很大了,所以我不会在这里发布它)。我接受了这个util类,并对其测试进行了修改以使其可以使用(您不能使用任何类变量或方法,这意味着我必须复制我的辅助方法代码)。
一旦我实现了util并调整了我的测试以使其符合标准,当我分别运行它们并一次运行它们时,这些测试就开始通过。
任务完成!
答案 0 :(得分:0)
我遇到了完全相同的问题! 我碰到了这篇文章,这激发了我的解决方案!
我没有在单独的AppDomain中运行测试,而是最终修改了对密码库进行调用的生产代码。 我在单独的AppDomain中运行有问题的呼叫,然后在继续操作之前在其中设置了开关。
这不仅修复了所有测试,而且使我的库的客户端不会弄乱开关。