[C#.NET][XML] 忽略XML宣告及XML命名空間

  • 8236
  • 0
  • 2013-07-05

[C#.NET][XML] 忽略XML宣告及XML命名空間

最近在使用非對稱加密RSACryptoServiceProvider 時,發現RSACryptoServiceProvider類別所產生的Xml字串沒有宣告,也沒有命名空間,要用序列化保存這些資訊時,必需要要動手處理掉它們,否則下次載入給RSACryptoServiceProvider用時會出錯


RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
string publicKey = provider.ToXmlString(false);
string privateKey = provider.ToXmlString(true);

imageimage


publicKey


<RSAKeyValue>  <Modulus>8uifIYGkqxnHpnvY3ltjFp/uNVgtFZ6abv3F2BZIseInWKhYBD9PiIialKypT+HYyb44uWBS0QuVZAKu4eeWHjW3OmLvvfswj3l/xq3qMrWqNWpZfsFPXf9/E3N9GOrpoktW1E73fYakrBngyAj8aaJxi6p00G4Uztf9XDijMUc=</Modulus>
  <Exponent>AQAB</Exponent>
</RSAKeyValue>

privateKey

 


<RSAKeyValue>
 <Modulus>8uifIYGkqxnHpnvY3ltjFp/uNVgtFZ6abv3F2BZIseInWKhYBD9PiIialKypT+HYyb44uWBS0QuVZAKu4eeWHjW3OmLvvfswj3l/xq3qMrWqNWpZfsFPXf9/E3N9GOrpoktW1E73fYakrBngyAj8aaJxi6p00G4Uztf9XDijMUc=</Modulus>
  <Exponent>AQAB</Exponent>
  <P>/jBebYAlBZihNwl8Mj1gkIawGerXWbokEUYmWUZ/Hl93rUfiZeBBFwt0tmx3VvjrWTOkJBCvHIf3tdsKEZbUiQ==</P>
  <Q>9KOtj1eSpOvB06ULUZy/ncX2VyYJW+cN47hpmcGu4X5Y9bnIKkGS7PRkYhAqZFEXtIsMh15kfPpHxb6GH0oDTw==</Q>
  <DP>OX3pLa0pMn3WIOOlUputRqMgG4yRTrsaQ0nxjIm0YMNJB0lV/KLfNf4iVMxtpZ9BY/iZLIsVgEeEkH5NZbMOuQ==</DP>
  <DQ>CYPYO0wHqxx0VHwF3a3AEi3h7+/Ny2JIOwQwL0fGOoUEhsIsE+CrC0ZSJTJFw9MXnfEOkrFMLUQ6yGkppEvnpw==</DQ>
  <InverseQ>sF086K3Tk1ypJ7Inz+OHHrAbVi3i7AvYrGBAmS1HQbBf2vi9YlD0of7osYesOPsTHeNjsvz+3Bx7LdNk54hMpg==</InverseQ>
  <D>cvsmCEhD2DIVzXqmR2re1qDRszKP9MHkvFEny4eQ1ZMFqPPW5fvJ/Akdku7AHm37nlOKqkUsLoPbLUIP4iMrbSlEPHxbNvIM7eNu7QVOIlctNDZP53AD/BjkiFrP/TtRlualmWMZ5GlQ+AiajX8NyXztxLge0SmWToIfQz8RAiE=</D>
</RSAKeyValue>

 

Xml我還是相當習慣用序列化來處理,老樣子,先建立要序列化的Entity類別

 


[Serializable, XmlRoot("RSAKeyValue")]
public class PrivateKeyEntity
{
    [XmlElement("Modulus")]
    public string Modulus { get; set; }
    [XmlElement("Exponent")]
    public string Exponent { get; set; }
    [XmlElement("P")]
    public string P { get; set; }
    [XmlElement("Q")]
    public string Q { get; set; }
    [XmlElement("DP")]
    public string DP { get; set; }
    [XmlElement("DQ")]
    public string DQ { get; set; }
    [XmlElement("InverseQ")]
    public string InverseQ { get; set; }
    [XmlElement("D")]
    public string D { get; set; }
}

[Serializable, XmlRoot("RSAKeyValue")]
public class PublicKeyEntity
{
    [XmlElement("Modulus")]
    public string Modulus { get; set; }
    [XmlElement("Exponent")]
    public string Exponent { get; set; }
}


因為這次所取得的資料是Xml 字串,反序列化時使用MemoryStream 類別處理,


public static T DeserializeFromXml<T>(string XmlString)
{
    Encoding encode = Encoding.UTF8;
    using (MemoryStream ms = new MemoryStream(encode.GetBytes(XmlString)))
    {
        XmlSerializer xs = new XmlSerializer(typeof(T));
        
        object obj = xs.Deserialize(ms);
        if (obj == null)
            return default(T);
        else
            return (T)obj;
    }
}

 

序列化時要清掉Xml宣告跟命名空間,這跟上篇的實作方式不一樣 [C#.NET] 利用 泛型方法 重構 反序列化

 


public static void SerializeToXml(string FileName, object Entity)
{
    Encoding encode = Encoding.UTF8;
    //清掉命名空間
    XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
    namespaces.Add("", "");

    using (FileStream fileStream = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Read))
    using (StreamWriter streamWriter = new StreamWriter(fileStream, encode))
    {
        //忽略Xml宣告
        XmlWriterSettings xmlSetting = new XmlWriterSettings();
        xmlSetting.OmitXmlDeclaration = true;

        XmlWriter xmlWriter = XmlWriter.Create(streamWriter, xmlSetting);
        XmlSerializer xml = new XmlSerializer(Entity.GetType());
        xml.Serialize(xmlWriter, Entity, namespaces);
    }
}

 

單元測試裡的呼叫


[TestMethod()]
public void WriteFileTest()
{
    XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
    namespaces.Add("", "");
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider(1024);
    string publicKey = provider.ToXmlString(false);
    string privateKey = provider.ToXmlString(true);

    PrivateKeyEntity privateEntity = DeserializeFromXml<PrivateKeyEntity>(privateKey);
    PublicKeyEntity publicEntity = DeserializeFromXml<PublicKeyEntity>(publicKey);

    SerializeToXml(@"C:\privateKey.key", privateEntity);
    SerializeToXml(@"C:\publicKey.key", publicEntity);
}

 

image

image

 



測試存放在電腦裡的檔案能不能給 RSACryptoServiceProvider 用

 


[TestMethod()]
public void ReadFileTest()
{
    string publicKey = "";
    string privateKey = "";
    string source = "我是章魚";
    using (StreamReader reader = new StreamReader(@"C:\publicKey.key", Encoding.UTF8))
    {
        publicKey = reader.ReadToEnd();
    }
    using (StreamReader reader = new StreamReader(@"C:\privateKey.key", Encoding.UTF8))
    {
        privateKey = reader.ReadToEnd();
    }
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
    //加密用公開金鑰
    provider.FromXmlString(publicKey);
    byte[] encryptBytes = provider.Encrypt(Encoding.UTF8.GetBytes(source), false);
    string encryptString = BitConverter.ToString(encryptBytes).Replace("-", "");
    //解密用私鑰
    byte[] decryptBytes = new byte[encryptString.Length / 2];
    int j = 0;
    for (int i = 0; i < encryptString.Length / 2; i++)
    {
        decryptBytes[i] = Byte.Parse(encryptString[j].ToString() + encryptString[j + 1].ToString(), System.Globalization.NumberStyles.HexNumber);
        j += 2;
    }
    provider.FromXmlString(privateKey);

    string decryptString = Encoding.UTF8.GetString(provider.Decrypt(decryptBytes, false));
    Assert.AreEqual(source, decryptString);
}



套句91哥的話,看著自己寫的程式,通過測試的程式真爽

image

 


後記:

 

加密後的字串 encryptString 每次都會不一樣,想要比對加密後的資料都會失敗,我在這裡卡到點時間。

image

image

 

若是要完整的處理必需要再把 encryptString 存起來,然後丟給解密方法,再這裡就不多說了,下次再詳細分享非對稱加密的用法。

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo