241 lines
8.9 KiB
C#
241 lines
8.9 KiB
C#
using CryptoPro.Security.Cryptography;
|
||
using CryptoPro.Security.Cryptography.X509Certificates;
|
||
using CryptoPro.Security.Cryptography.Xml;
|
||
using System.Globalization;
|
||
using System.Numerics;
|
||
using System.Security.Cryptography;
|
||
using System.Security.Cryptography.X509Certificates;
|
||
using System.Xml;
|
||
|
||
namespace Hcs.Broker.Api.Request
|
||
{
|
||
internal class SignedXml(XmlDocument document) : CpXadesSignedXml(document)
|
||
{
|
||
private const string XADES_141_NAMESPACE_URL = "http://uri.etsi.org/01903/v1.4.1#";
|
||
|
||
/// <summary>
|
||
/// OID алгоритма открытого ключа/цифровой подписи ГОСТ Р 34.10-2001
|
||
/// </summary>
|
||
private const string OID_CP_GOST_R3410EL = "1.2.643.2.2.19";
|
||
|
||
/// <summary>
|
||
/// OID алгоритма открытого ключа/цифровой подписи ГОСТ Р 34.10-2012 256
|
||
/// </summary>
|
||
private const string OID_CP_GOST_R3410_12_256 = "1.2.643.7.1.1.1.1";
|
||
|
||
/// <summary>
|
||
/// OID алгоритма открытого ключа/цифровой подписи ГОСТ Р 34.10-2012 512
|
||
/// </summary>
|
||
private const string OID_CP_GOST_R3410_12_512 = "1.2.643.7.1.1.1.2";
|
||
|
||
protected override void AddXadesProperties(
|
||
CpX509Certificate2 signingCertificate,
|
||
DateTime time,
|
||
string signatureId,
|
||
string objectId,
|
||
string? xmlDSigPrefix,
|
||
XadesPropertiesFlags xadesProperties)
|
||
{
|
||
if (_containingDocument == null)
|
||
{
|
||
throw new InvalidOperationException("XmlDocument is null");
|
||
}
|
||
|
||
var useCertificateV2 = xadesProperties.HasFlag(
|
||
XadesPropertiesFlags.UseSigningCertificateV2
|
||
);
|
||
var useCertificateV1 = xadesProperties.HasFlag(
|
||
XadesPropertiesFlags.UseSigningCertificateV1
|
||
);
|
||
if ((useCertificateV2 && useCertificateV1) || (!useCertificateV2 && !useCertificateV1))
|
||
{
|
||
throw new InvalidOperationException("Invalid flags");
|
||
}
|
||
|
||
var document = _containingDocument;
|
||
|
||
var parametersSignature = new CpReference
|
||
{
|
||
Uri = $"#{objectId}",
|
||
Type = XmlDsigSignatureProperties,
|
||
DigestMethod = XmlDsigGost3411_2012_256Url,
|
||
};
|
||
parametersSignature.AddTransform(new CpXmlDsigExcC14NTransform());
|
||
|
||
AddReference(parametersSignature);
|
||
|
||
var qualifyingPropertiesNode = document.CreateElement(
|
||
XadesPrefix,
|
||
"QualifyingProperties",
|
||
XadesNamespaceUrl
|
||
);
|
||
qualifyingPropertiesNode.SetAttribute("xmlns:xades141", XADES_141_NAMESPACE_URL);
|
||
qualifyingPropertiesNode.SetAttribute("Target", $"#{signatureId}");
|
||
|
||
var signedPropertiesNode = document.CreateElement(
|
||
XadesPrefix,
|
||
"SignedProperties",
|
||
XadesNamespaceUrl
|
||
);
|
||
signedPropertiesNode.SetAttribute("Id", objectId);
|
||
|
||
qualifyingPropertiesNode.AppendChild(signedPropertiesNode);
|
||
|
||
var signedSignaturePropertiesNode = document.CreateElement(
|
||
XadesPrefix,
|
||
"SignedSignatureProperties",
|
||
XadesNamespaceUrl
|
||
);
|
||
signedPropertiesNode.AppendChild(signedSignaturePropertiesNode);
|
||
|
||
var signingTime = document.CreateElement(XadesPrefix, "SigningTime", XadesNamespaceUrl);
|
||
signingTime.InnerText = time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
|
||
|
||
signedSignaturePropertiesNode.AppendChild(signingTime);
|
||
|
||
var signingCertificateNodeName = useCertificateV2
|
||
? "SigningCertificateV2"
|
||
: "SigningCertificate";
|
||
var signingCertificateNode = document.CreateElement(
|
||
XadesPrefix,
|
||
signingCertificateNodeName,
|
||
XadesNamespaceUrl
|
||
);
|
||
|
||
signedSignaturePropertiesNode.AppendChild(signingCertificateNode);
|
||
|
||
var certNode = document.CreateElement(XadesPrefix, "Cert", XadesNamespaceUrl);
|
||
signingCertificateNode.AppendChild(certNode);
|
||
|
||
var certDigestNode = document.CreateElement(
|
||
XadesPrefix,
|
||
"CertDigest",
|
||
XadesNamespaceUrl
|
||
);
|
||
certNode.AppendChild(certDigestNode);
|
||
|
||
var algIdString = signingCertificate.GetKeyAlgorithm();
|
||
(bool isGost, string? gostDigestAlgorithm) = algIdString switch
|
||
{
|
||
OID_CP_GOST_R3410EL => (true, XmlDsigGost3411Url),
|
||
OID_CP_GOST_R3410_12_256 => (
|
||
true,
|
||
XmlDsigGost3411_2012_256Url
|
||
),
|
||
OID_CP_GOST_R3410_12_512 => (
|
||
true,
|
||
XmlDsigGost3411_2012_512Url
|
||
),
|
||
_ => (false, null),
|
||
};
|
||
|
||
var digestMethodAlgorithmAtribute = document.CreateAttribute("Algorithm");
|
||
if (isGost)
|
||
{
|
||
digestMethodAlgorithmAtribute.InnerText = gostDigestAlgorithm!;
|
||
}
|
||
else
|
||
{
|
||
digestMethodAlgorithmAtribute.InnerText = XmlDsigSHA1Url;
|
||
}
|
||
|
||
var digestMethod = document.CreateElement(
|
||
xmlDSigPrefix,
|
||
"DigestMethod",
|
||
XmlDsigNamespaceUrl
|
||
);
|
||
digestMethod.Attributes.Append(digestMethodAlgorithmAtribute);
|
||
|
||
certDigestNode.AppendChild(digestMethod);
|
||
|
||
var digestValue = document.CreateElement(
|
||
xmlDSigPrefix,
|
||
"DigestValue",
|
||
XmlDsigNamespaceUrl
|
||
);
|
||
|
||
if (isGost)
|
||
{
|
||
var hash =
|
||
CpCryptoConfig.CreateFromName(gostDigestAlgorithm!) as HashAlgorithm
|
||
?? throw new System.Exception(
|
||
$"Cannot find [{gostDigestAlgorithm}] alg in CpCryptoConfig!"
|
||
);
|
||
using (hash)
|
||
{
|
||
digestValue.InnerText = Convert.ToBase64String(
|
||
hash.ComputeHash(signingCertificate.RawData)
|
||
);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
digestValue.InnerText = Convert.ToBase64String(signingCertificate.GetCertHash());
|
||
}
|
||
|
||
certDigestNode.AppendChild(digestValue);
|
||
|
||
if (useCertificateV2)
|
||
{
|
||
var issuerSerialNode = document.CreateElement(
|
||
XadesPrefix,
|
||
"IssuerSerialV2",
|
||
XadesNamespaceUrl
|
||
);
|
||
issuerSerialNode.InnerText = Convert.ToBase64String(
|
||
X509EncodeIssuerAndSerialHelper.EncodeIssuerAndSerial(signingCertificate)
|
||
);
|
||
|
||
certNode.AppendChild(issuerSerialNode);
|
||
}
|
||
else
|
||
{
|
||
var issuerName = signingCertificate.IssuerName.Decode(X500DistinguishedNameFlags.DoNotUseQuotes);
|
||
var x509IssuerNameNode = document.CreateElement(
|
||
xmlDSigPrefix,
|
||
"X509IssuerName",
|
||
XmlDsigNamespaceUrl
|
||
);
|
||
x509IssuerNameNode.InnerText = GetOidRepresentation(issuerName);
|
||
|
||
var issuerSerialNode = document.CreateElement(
|
||
XadesPrefix,
|
||
"IssuerSerial",
|
||
XadesNamespaceUrl
|
||
);
|
||
issuerSerialNode.AppendChild(x509IssuerNameNode);
|
||
|
||
var x509SerialNumber = document.CreateElement(
|
||
xmlDSigPrefix,
|
||
"X509SerialNumber",
|
||
XmlDsigNamespaceUrl
|
||
);
|
||
x509SerialNumber.InnerText = BigInteger.Parse(signingCertificate.SerialNumber, NumberStyles.HexNumber).ToString();
|
||
|
||
issuerSerialNode.AppendChild(x509SerialNumber);
|
||
certNode.AppendChild(issuerSerialNode);
|
||
}
|
||
|
||
var dataObject = new CpDataObject();
|
||
dataObject.Data = qualifyingPropertiesNode.SelectNodes(".");
|
||
|
||
AddObject(dataObject);
|
||
}
|
||
|
||
private string GetOidRepresentation(string issuerName)
|
||
{
|
||
return issuerName
|
||
.Replace("\"", "\\\"")
|
||
.Replace("E=", "1.2.840.113549.1.9.1=")
|
||
.Replace("unstructuredName=", "1.2.840.113549.1.9.2=")
|
||
.Replace("ОГРН=", "1.2.643.100.1=")
|
||
.Replace("ИНН ЮЛ=", "1.2.643.100.4=")
|
||
.Replace("ИНН ФЛ=", "1.2.643.3.131.1.1=")
|
||
.Replace("ИНН=", "1.2.643.3.131.1.1=")
|
||
.Replace("СНИЛС=", "1.2.643.100.3=")
|
||
.Replace("ОГРНИП=", "1.2.643.100.5=")
|
||
.Replace("S=", "ST=");
|
||
}
|
||
}
|
||
}
|