在 C# 中的 Xml 签名中配置 TSA
Configure TSA in Xml Signature in C#
我正在尝试使用 Microsoft 的 Signature Class
库在 C# 中签署 XML 文件。
我做的是这样的-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;
using XMLSigner.Model;
using DataObject = System.Security.Cryptography.Xml.DataObject;
internal static XmlDocument GetSignedXMLDocument(XmlDocument xmlDocument, X509Certificate2 certificate, long procedureSerial = -1, string reason = "")
{
//Check if local time is OK
if(!Ntp.CheckIfLocalTimeIsOk()) {
MessageBox.Show("PC Time is need to be updated before sign !");
return null; //Last Sign Not Verified
}
//Then sign the xml
try
{
//MessageBox.Show(certificate.Subject);
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.SigningKey = certificate.PrivateKey;
// Create a reference to be signed
Reference reference = new Reference();
/////////////////////
reference.Uri = "";//"#" + procedureSerial;
//reference.Type = reason;
//reference.Id = DateTime.UtcNow.Ticks.ToString();
reference.Id = Base64EncodedCurrentTime();
//reference.TransformChain = ;
/////////////////////
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(true);
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
//canonicalize
XmlDsigC14NTransform c14t = new XmlDsigC14NTransform();
reference.AddTransform(c14t);
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data keyInfoData = new KeyInfoX509Data(certificate);
KeyInfoName kin = new KeyInfoName();
//kin.Value = "Public key of certificate";
kin.Value = certificate.FriendlyName;
RSA rsa = (RSA)certificate.PublicKey.Key;
RSAKeyValue rkv = new RSAKeyValue(rsa);
keyInfo.AddClause(rkv);
keyInfo.AddClause(kin);
keyInfo.AddClause(keyInfoData);
signedXml.KeyInfo = keyInfo;
//////////////////////////////////////////Add Other Data as we need////
// Add the data object to the signature.
//CreateMetaDataObject("Name", GetNetworkTime());
signedXml.AddObject(CreateMetaDataObject(procedureSerial, reason));
///////////////////////////////////////////////////////////////////////
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
xmlDocument.DocumentElement.AppendChild(
xmlDocument.ImportNode(xmlDigitalSignature, true)
);
/////////////////////
} catch (Exception exception) {
MessageBox.Show("Internal System Error during sign");
throw exception;
}
return xmlDocument;
}
它工作得很好。但是我对这段代码有疑问。我必须使用 TSA Server
作为 XML Signature
中存储的时间,但时间是从本地 PC 设置的,为避免此问题,我已从 Ntp.CheckIfLocalTimeIsOk()
中定义的函数手动检查时间 here。但我喜欢有时间来自 TSA link like-
- http://ca.signfiles.com/TSAServer.aspx
- http://timestamp.globalsign.com/scripts/timstamp.dll
- https://timestamp.geotrust.com/tsa
是否可以在 C# 中的 XmlSignature
中配置 TSA
?
我自己解决了问题
我所做的是从 XMLDocument
创建一个散列,就像这样-
private static byte[] GetXmlHashByteStream(XmlDocument xmlDoc)
{
byte[] hash;
XmlDsigC14NTransform transformer = new XmlDsigC14NTransform();
transformer.LoadInput(xmlDoc);
using (Stream stream = (Stream)transformer.GetOutput(typeof(Stream)))
{
SHA1 sha1 = SHA1.Create();
hash = sha1.ComputeHash(stream);
stream.Close();
}
return hash;
}
然后像这样得到Timestamp Hash-
string stampURI = "http://timestamp.globalsign.com/scripts/timstamp.dll"
private TimeStampResponse GetSignedHashFromTsa(byte[] hash)
{
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
TimeStampRequest request = reqGen.Generate(
TspAlgorithms.Sha1,
hash,
BigInteger.ValueOf(100)
);
byte[] reqData = request.GetEncoded();
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(stampURI);
httpReq.Method = "POST";
httpReq.ContentType = "application/timestamp-query";
httpReq.ContentLength = reqData.Length;
//Configure Timeout
//httpReq.Timeout = 5000;
//httpReq.ReadWriteTimeout = 32000;
// Write the request content
Stream reqStream = httpReq.GetRequestStream();
reqStream.Write(reqData, 0, reqData.Length);
reqStream.Close();
HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();
// Read the response
Stream respStream = new BufferedStream(httpResp.GetResponseStream());
TimeStampResponse response = new TimeStampResponse(respStream);
respStream.Close();
return response;
}
重新
如果你想从响应中得到签名的Timestamp
字符串,你可以这样做-
internal string GetSignedHashFromTsa(XmlDocument xmlDxocument)
{
byte[] hash = GetXmlHashByteStream(xmlDxocument);
TimeStampResponse timeStampResponse = GetSignedHashFromTsa(hash);
byte[] signedEncodedByteStream = timeStampResponse.GetEncoded();
return Convert.ToBase64String(signedEncodedByteStream);
}
如果你想从哈希字符串中获取时间,那么你必须做这样的事情-
internal static DateTime? GetTsaTimeFromSignedHash(string tsaSignedHashString)
{
try {
byte[] bytes = Convert.FromBase64String(tsaSignedHashString);
TimeStampResponse timeStampResponse = new TimeStampResponse(bytes);
return timeStampResponse.TimeStampToken.TimeStampInfo.GenTime;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
//throw ex;
return null;
}
}
我正在尝试使用 Microsoft 的 Signature Class
库在 C# 中签署 XML 文件。
我做的是这样的-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;
using XMLSigner.Model;
using DataObject = System.Security.Cryptography.Xml.DataObject;
internal static XmlDocument GetSignedXMLDocument(XmlDocument xmlDocument, X509Certificate2 certificate, long procedureSerial = -1, string reason = "")
{
//Check if local time is OK
if(!Ntp.CheckIfLocalTimeIsOk()) {
MessageBox.Show("PC Time is need to be updated before sign !");
return null; //Last Sign Not Verified
}
//Then sign the xml
try
{
//MessageBox.Show(certificate.Subject);
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.SigningKey = certificate.PrivateKey;
// Create a reference to be signed
Reference reference = new Reference();
/////////////////////
reference.Uri = "";//"#" + procedureSerial;
//reference.Type = reason;
//reference.Id = DateTime.UtcNow.Ticks.ToString();
reference.Id = Base64EncodedCurrentTime();
//reference.TransformChain = ;
/////////////////////
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(true);
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
//canonicalize
XmlDsigC14NTransform c14t = new XmlDsigC14NTransform();
reference.AddTransform(c14t);
KeyInfo keyInfo = new KeyInfo();
KeyInfoX509Data keyInfoData = new KeyInfoX509Data(certificate);
KeyInfoName kin = new KeyInfoName();
//kin.Value = "Public key of certificate";
kin.Value = certificate.FriendlyName;
RSA rsa = (RSA)certificate.PublicKey.Key;
RSAKeyValue rkv = new RSAKeyValue(rsa);
keyInfo.AddClause(rkv);
keyInfo.AddClause(kin);
keyInfo.AddClause(keyInfoData);
signedXml.KeyInfo = keyInfo;
//////////////////////////////////////////Add Other Data as we need////
// Add the data object to the signature.
//CreateMetaDataObject("Name", GetNetworkTime());
signedXml.AddObject(CreateMetaDataObject(procedureSerial, reason));
///////////////////////////////////////////////////////////////////////
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
xmlDocument.DocumentElement.AppendChild(
xmlDocument.ImportNode(xmlDigitalSignature, true)
);
/////////////////////
} catch (Exception exception) {
MessageBox.Show("Internal System Error during sign");
throw exception;
}
return xmlDocument;
}
它工作得很好。但是我对这段代码有疑问。我必须使用 TSA Server
作为 XML Signature
中存储的时间,但时间是从本地 PC 设置的,为避免此问题,我已从 Ntp.CheckIfLocalTimeIsOk()
中定义的函数手动检查时间 here。但我喜欢有时间来自 TSA link like-
- http://ca.signfiles.com/TSAServer.aspx
- http://timestamp.globalsign.com/scripts/timstamp.dll
- https://timestamp.geotrust.com/tsa
是否可以在 C# 中的 XmlSignature
中配置 TSA
?
我自己解决了问题
我所做的是从 XMLDocument
创建一个散列,就像这样-
private static byte[] GetXmlHashByteStream(XmlDocument xmlDoc)
{
byte[] hash;
XmlDsigC14NTransform transformer = new XmlDsigC14NTransform();
transformer.LoadInput(xmlDoc);
using (Stream stream = (Stream)transformer.GetOutput(typeof(Stream)))
{
SHA1 sha1 = SHA1.Create();
hash = sha1.ComputeHash(stream);
stream.Close();
}
return hash;
}
然后像这样得到Timestamp Hash-
string stampURI = "http://timestamp.globalsign.com/scripts/timstamp.dll"
private TimeStampResponse GetSignedHashFromTsa(byte[] hash)
{
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
TimeStampRequest request = reqGen.Generate(
TspAlgorithms.Sha1,
hash,
BigInteger.ValueOf(100)
);
byte[] reqData = request.GetEncoded();
HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(stampURI);
httpReq.Method = "POST";
httpReq.ContentType = "application/timestamp-query";
httpReq.ContentLength = reqData.Length;
//Configure Timeout
//httpReq.Timeout = 5000;
//httpReq.ReadWriteTimeout = 32000;
// Write the request content
Stream reqStream = httpReq.GetRequestStream();
reqStream.Write(reqData, 0, reqData.Length);
reqStream.Close();
HttpWebResponse httpResp = (HttpWebResponse)httpReq.GetResponse();
// Read the response
Stream respStream = new BufferedStream(httpResp.GetResponseStream());
TimeStampResponse response = new TimeStampResponse(respStream);
respStream.Close();
return response;
}
重新
如果你想从响应中得到签名的Timestamp
字符串,你可以这样做-
internal string GetSignedHashFromTsa(XmlDocument xmlDxocument)
{
byte[] hash = GetXmlHashByteStream(xmlDxocument);
TimeStampResponse timeStampResponse = GetSignedHashFromTsa(hash);
byte[] signedEncodedByteStream = timeStampResponse.GetEncoded();
return Convert.ToBase64String(signedEncodedByteStream);
}
如果你想从哈希字符串中获取时间,那么你必须做这样的事情-
internal static DateTime? GetTsaTimeFromSignedHash(string tsaSignedHashString)
{
try {
byte[] bytes = Convert.FromBase64String(tsaSignedHashString);
TimeStampResponse timeStampResponse = new TimeStampResponse(bytes);
return timeStampResponse.TimeStampToken.TimeStampInfo.GenTime;
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
//throw ex;
return null;
}
}