此 All-In-One XML 加密/解密 MSDN 示例中有什么不正确的地方?
我已合并 XML 加密 和 从 MSDN 解密到下面的同一项目中,并收到错误
“无法检索解密 钥匙”。
最奇怪的是我能够在异常之前和之后写入私钥,所以我不确定问题可能是什么。合并后的代码如下。
public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (ElementToEncrypt == null)
throw new ArgumentNullException("ElementToEncrypt");
if (EncryptionElementID == null)
throw new ArgumentNullException("EncryptionElementID");
if (Alg == null)
throw new ArgumentNullException("Alg");
if (KeyName == null)
throw new ArgumentNullException("KeyName");
////////////////////////////////////////////////
// Find the specified element in the XmlDocument
// object and create a new XmlElemnt object.
////////////////////////////////////////////////
XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
// Throw an XmlException if the element was not found.
if (elementToEncrypt == null)
{
throw new XmlException("The specified element was not found");
}
RijndaelManaged sessionKey = null;
try
{
//////////////////////////////////////////////////
// Create a new instance of the EncryptedXml class
// and use it to encrypt the XmlElement with the
// a new random symmetric key.
//////////////////////////////////////////////////
// Create a 256 bit Rijndael key.
sessionKey = new RijndaelManaged();
sessionKey.KeySize = 256;
EncryptedXml eXml = new EncryptedXml();
byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
////////////////////////////////////////////////
// Construct an EncryptedData object and populate
// it with the desired encryption information.
////////////////////////////////////////////////
EncryptedData edElement = new EncryptedData();
edElement.Type = EncryptedXml.XmlEncElementUrl;
edElement.Id = EncryptionElementID;
// Create an EncryptionMethod element so that the
// receiver knows which algorithm to use for decryption.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
// Encrypt the session key and add it to an EncryptedKey element.
EncryptedKey ek = new EncryptedKey();
byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);
ek.CipherData = new CipherData(encryptedKey);
ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
// Create a new DataReference element
// for the KeyInfo element. This optional
// element specifies which EncryptedData
// uses this key. An XML document can have
// multiple EncryptedData elements that use
// different keys.
DataReference dRef = new DataReference();
// Specify the EncryptedData URI.
dRef.Uri = "#" + EncryptionElementID;
// Add the DataReference to the EncryptedKey.
ek.AddReference(dRef);
// Add the encrypted key to the
// EncryptedData object.
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
// Set the KeyInfo element to specify the
// name of the RSA key.
// Create a new KeyInfo element.
edElement.KeyInfo = new KeyInfo();
// Create a new KeyInfoName element.
KeyInfoName kin = new KeyInfoName();
// Specify a name for the key.
kin.Value = KeyName;
// Add the KeyInfoName element to the
// EncryptedKey object.
ek.KeyInfo.AddClause(kin);
// Add the encrypted element data to the
// EncryptedData object.
edElement.CipherData.CipherValue = encryptedElement;
////////////////////////////////////////////////////
// Replace the element from the original XmlDocument
// object with the EncryptedData element.
////////////////////////////////////////////////////
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
}
catch (Exception e)
{
// re-throw the exception.
throw e;
}
finally
{
if (sessionKey != null)
{
sessionKey.Clear();
}
}
}
public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (Alg == null)
throw new ArgumentNullException("Alg");
if (KeyName == null)
throw new ArgumentNullException("KeyName");
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml(Doc);
// Add a key-name mapping.
// This method can only decrypt documents
// that present the specified key name.
exml.AddKeyNameMapping(KeyName, Alg);
// Decrypt the element throws Exception: <--------------
//
// Unable to retrieve the decryption key".
//
exml.DecryptDocument();
}
static void Main(string[] args)
{
string containerName = "XML_ENC_RSA_KEY";
////////////////////////////////////////////////////////
// Create and persist a key pair
// Save the Public portion of the keypair in a string we will use later
// Create a new CspParameters object to specify
// a key container.
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
// Create a new RSA key and save it in the container. This key will encrypt
// a symmetric key, which will then be encryped in the XML document.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
rsaKey.PersistKeyInCsp = true;
Console.WriteLine(rsaKey.ToXmlString(false));
string PublicKeyTest = rsaKey.ToXmlString(false);
////////////////////////////////////////////////////////
// Encrypt using a new instance of the crypto provider and the public key string
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
var rsaKey2 = new RSACryptoServiceProvider();
rsaKey2.FromXmlString(PublicKeyTest);
Console.WriteLine(rsaKey2.ToXmlString(false));
PublicKeyTest = rsaKey2.ToXmlString(false);
// Create an XmlDocument object.
XmlDocument xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
try
{
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("test.xml");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
try
{
// Encrypt the "creditcard" element.
Encrypt(xmlDoc, "creditcard", "EncryptedElement1", rsaKey2, "rsaKey");
// Save the XML document.
xmlDoc.Save("testOUT.xml");
// Display the encrypted XML to the console.
Console.WriteLine("Encrypted XML:");
Console.WriteLine();
Console.WriteLine(xmlDoc.OuterXml);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
// Clear the RSA key.
rsaKey2.Clear();
}
Console.ReadLine();
////////////////////////////////////////////////////////
// Decrypt the output, using the internal CSP
xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
try
{
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("testOUT.xml");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
// Get the RSA key from the key container. This key will decrypt
// a symmetric key that was imbedded in the XML document.
var rsaKey3 = new RSACryptoServiceProvider(cspParams);
Console.WriteLine(rsaKey3.ToXmlString(true));
try
{
// Decrypt the elements, throws exception
Decrypt(xmlDoc, rsaKey3, "rsaKey");
// Save the XML document.
xmlDoc.Save("test3.xml");
// Display the encrypted XML to the console.
Console.WriteLine();
Console.WriteLine("Decrypted XML:");
Console.WriteLine();
Console.WriteLine(xmlDoc.OuterXml);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
}
Console.ReadLine();
完全例外 (e)
System.Security.Cryptography.CryptographyException: 无法检索解密密钥。 在 System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument() 在 RemoteKey.Decrypt(XmlDocument Doc, RSA Alg,字符串密钥名称)中 C:\Users\me\ClientAgent\Program.cs:行 185 at RemoteKey.Main(字符串[] 参数)中 C:\Users\Me\ClientAgent\Program.cs:行 286
I've merged the XML Encryption and Decryption from MSDN into the same project below, and am getting the error
"Unable to retrieve the decryption
key".
The strangest thing is that I am able to write the private key before and after the exception, so I'm unsure of what the issue may be. The merged code is below.
public static void Encrypt(XmlDocument Doc, string ElementToEncrypt, string EncryptionElementID, RSA Alg, string KeyName)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (ElementToEncrypt == null)
throw new ArgumentNullException("ElementToEncrypt");
if (EncryptionElementID == null)
throw new ArgumentNullException("EncryptionElementID");
if (Alg == null)
throw new ArgumentNullException("Alg");
if (KeyName == null)
throw new ArgumentNullException("KeyName");
////////////////////////////////////////////////
// Find the specified element in the XmlDocument
// object and create a new XmlElemnt object.
////////////////////////////////////////////////
XmlElement elementToEncrypt = Doc.GetElementsByTagName(ElementToEncrypt)[0] as XmlElement;
// Throw an XmlException if the element was not found.
if (elementToEncrypt == null)
{
throw new XmlException("The specified element was not found");
}
RijndaelManaged sessionKey = null;
try
{
//////////////////////////////////////////////////
// Create a new instance of the EncryptedXml class
// and use it to encrypt the XmlElement with the
// a new random symmetric key.
//////////////////////////////////////////////////
// Create a 256 bit Rijndael key.
sessionKey = new RijndaelManaged();
sessionKey.KeySize = 256;
EncryptedXml eXml = new EncryptedXml();
byte[] encryptedElement = eXml.EncryptData(elementToEncrypt, sessionKey, false);
////////////////////////////////////////////////
// Construct an EncryptedData object and populate
// it with the desired encryption information.
////////////////////////////////////////////////
EncryptedData edElement = new EncryptedData();
edElement.Type = EncryptedXml.XmlEncElementUrl;
edElement.Id = EncryptionElementID;
// Create an EncryptionMethod element so that the
// receiver knows which algorithm to use for decryption.
edElement.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES256Url);
// Encrypt the session key and add it to an EncryptedKey element.
EncryptedKey ek = new EncryptedKey();
byte[] encryptedKey = EncryptedXml.EncryptKey(sessionKey.Key, Alg, false);
ek.CipherData = new CipherData(encryptedKey);
ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
// Create a new DataReference element
// for the KeyInfo element. This optional
// element specifies which EncryptedData
// uses this key. An XML document can have
// multiple EncryptedData elements that use
// different keys.
DataReference dRef = new DataReference();
// Specify the EncryptedData URI.
dRef.Uri = "#" + EncryptionElementID;
// Add the DataReference to the EncryptedKey.
ek.AddReference(dRef);
// Add the encrypted key to the
// EncryptedData object.
edElement.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));
// Set the KeyInfo element to specify the
// name of the RSA key.
// Create a new KeyInfo element.
edElement.KeyInfo = new KeyInfo();
// Create a new KeyInfoName element.
KeyInfoName kin = new KeyInfoName();
// Specify a name for the key.
kin.Value = KeyName;
// Add the KeyInfoName element to the
// EncryptedKey object.
ek.KeyInfo.AddClause(kin);
// Add the encrypted element data to the
// EncryptedData object.
edElement.CipherData.CipherValue = encryptedElement;
////////////////////////////////////////////////////
// Replace the element from the original XmlDocument
// object with the EncryptedData element.
////////////////////////////////////////////////////
EncryptedXml.ReplaceElement(elementToEncrypt, edElement, false);
}
catch (Exception e)
{
// re-throw the exception.
throw e;
}
finally
{
if (sessionKey != null)
{
sessionKey.Clear();
}
}
}
public static void Decrypt(XmlDocument Doc, RSA Alg, string KeyName)
{
// Check the arguments.
if (Doc == null)
throw new ArgumentNullException("Doc");
if (Alg == null)
throw new ArgumentNullException("Alg");
if (KeyName == null)
throw new ArgumentNullException("KeyName");
// Create a new EncryptedXml object.
EncryptedXml exml = new EncryptedXml(Doc);
// Add a key-name mapping.
// This method can only decrypt documents
// that present the specified key name.
exml.AddKeyNameMapping(KeyName, Alg);
// Decrypt the element throws Exception: <--------------
//
// Unable to retrieve the decryption key".
//
exml.DecryptDocument();
}
static void Main(string[] args)
{
string containerName = "XML_ENC_RSA_KEY";
////////////////////////////////////////////////////////
// Create and persist a key pair
// Save the Public portion of the keypair in a string we will use later
// Create a new CspParameters object to specify
// a key container.
CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
// Create a new RSA key and save it in the container. This key will encrypt
// a symmetric key, which will then be encryped in the XML document.
RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParams);
rsaKey.PersistKeyInCsp = true;
Console.WriteLine(rsaKey.ToXmlString(false));
string PublicKeyTest = rsaKey.ToXmlString(false);
////////////////////////////////////////////////////////
// Encrypt using a new instance of the crypto provider and the public key string
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
var rsaKey2 = new RSACryptoServiceProvider();
rsaKey2.FromXmlString(PublicKeyTest);
Console.WriteLine(rsaKey2.ToXmlString(false));
PublicKeyTest = rsaKey2.ToXmlString(false);
// Create an XmlDocument object.
XmlDocument xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
try
{
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("test.xml");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
try
{
// Encrypt the "creditcard" element.
Encrypt(xmlDoc, "creditcard", "EncryptedElement1", rsaKey2, "rsaKey");
// Save the XML document.
xmlDoc.Save("testOUT.xml");
// Display the encrypted XML to the console.
Console.WriteLine("Encrypted XML:");
Console.WriteLine();
Console.WriteLine(xmlDoc.OuterXml);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
// Clear the RSA key.
rsaKey2.Clear();
}
Console.ReadLine();
////////////////////////////////////////////////////////
// Decrypt the output, using the internal CSP
xmlDoc = new XmlDocument();
// Load an XML file into the XmlDocument object.
try
{
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load("testOUT.xml");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
// Get the RSA key from the key container. This key will decrypt
// a symmetric key that was imbedded in the XML document.
var rsaKey3 = new RSACryptoServiceProvider(cspParams);
Console.WriteLine(rsaKey3.ToXmlString(true));
try
{
// Decrypt the elements, throws exception
Decrypt(xmlDoc, rsaKey3, "rsaKey");
// Save the XML document.
xmlDoc.Save("test3.xml");
// Display the encrypted XML to the console.
Console.WriteLine();
Console.WriteLine("Decrypted XML:");
Console.WriteLine();
Console.WriteLine(xmlDoc.OuterXml);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
}
Console.ReadLine();
Full exception (e)
System.Security.Cryptography.CryptographicException:
Unable to retrieve the decryption key.
at
System.Security.Cryptography.Xml.EncryptedXml.DecryptDocument()
at RemoteKey.Decrypt(XmlDocument Doc,
RSA Alg, String KeyName) in
C:\Users\me\ClientAgent\Program.cs:line
185 at RemoteKey.Main(String[]
args) in
C:\Users\Me\ClientAgent\Program.cs:line
286
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的 Encrypt 函数正在编写不包含 KeyName 元素的 XML,因此您对 DecryptDocument 的调用无法找到密钥。
我没有进一步研究为什么您的 Encrypt 写入不完整的 XML,但您可以在 MSDN 上找到另一个示例 Encrypt 函数: AddKeyNameMapping 方法。
将您的 Encrypt 函数替换为该 MSDN 示例中的函数,仅将 Alg 的函数参数类型从 SymmetricAlgorithm 更改为 RSA,并从现有代码中对 Encrypt 的调用中删除第三个参数“EncryptedElement1”,以使其编译,它应该运行到解密和工作结束。
一旦运行了该程序,您就可以向后查看您的加密与本示例中的加密的差异,或者,如果它适合您,则保留此加密。
Your Encrypt function is writing XML that does not contain a KeyName element, and so your call to DecryptDocument cannot find the key.
I didn't look any further into why your Encrypt is writing incomplete XML, but you can find another example Encrypt function on MSDN here: AddKeyNameMapping Method.
Replace just your Encrypt function with the function in that MSDN example, changing only the function argument type for Alg from SymmetricAlgorithm to RSA, and remove your third argument "EncryptedElement1" from the call to Encrypt in your existing code, to get it to compile, and it should run to the end of your Decrypt and work.
Once you have that running, you can work backwards looking at the differences in your Encrypt to the one in this example, or, just keep this one if it works for you.