| File: System\Security\Cryptography\Xml\KeyInfoX509Data.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography.Xml\src\System.Security.Cryptography.Xml.csproj (System.Security.Cryptography.Xml) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Globalization; using System.Numerics; using System.Security.Cryptography.X509Certificates; using System.Xml; namespace System.Security.Cryptography.Xml { public class KeyInfoX509Data : KeyInfoClause { // An array of certificates representing the certificate chain private ArrayList? _certificates; // An array of issuer serial structs private ArrayList? _issuerSerials; // An array of SKIs private ArrayList? _subjectKeyIds; // An array of subject names private ArrayList? _subjectNames; // A raw byte data representing a certificate revocation list private byte[]? _CRL; // // public constructors // public KeyInfoX509Data() { } public KeyInfoX509Data(byte[] rgbCert) { // Compat: this accepts null arrays for certificate data and would not throw. X509CertificateLoader throws // for a null input. This uses the X509Certificate2 constructor for null inputs to preserve the existing // behavior. Since the input is null and there is nothing to decode, the input is safe for the constructor. #pragma warning disable SYSLIB0057 X509Certificate2 certificate = rgbCert is null ? new X509Certificate2((byte[])null!) : X509CertificateLoader.LoadCertificate(rgbCert); #pragma warning restore SYSLIB0057 AddCertificate(certificate); } public KeyInfoX509Data(X509Certificate cert) { AddCertificate(cert); } public KeyInfoX509Data(X509Certificate cert, X509IncludeOption includeOption) { ArgumentNullException.ThrowIfNull(cert); X509Certificate2 certificate = new X509Certificate2(cert); X509ChainElementCollection elements; X509Chain chain; switch (includeOption) { case X509IncludeOption.ExcludeRoot: // Build the certificate chain chain = new X509Chain(); chain.Build(certificate); // Can't honor the option if we only have a partial chain. if ((chain.ChainStatus.Length > 0) && ((chain.ChainStatus[0].Status & X509ChainStatusFlags.PartialChain) == X509ChainStatusFlags.PartialChain)) { throw new CryptographicException(SR.Cryptography_Partial_Chain); } elements = (X509ChainElementCollection)chain.ChainElements; for (int index = 0; index < (Utils.IsSelfSigned(chain) ? 1 : elements.Count - 1); index++) { AddCertificate(elements[index].Certificate); } break; case X509IncludeOption.EndCertOnly: AddCertificate(certificate); break; case X509IncludeOption.WholeChain: // Build the certificate chain chain = new X509Chain(); chain.Build(certificate); // Can't honor the option if we only have a partial chain. if ((chain.ChainStatus.Length > 0) && ((chain.ChainStatus[0].Status & X509ChainStatusFlags.PartialChain) == X509ChainStatusFlags.PartialChain)) { throw new CryptographicException(SR.Cryptography_Partial_Chain); } elements = (X509ChainElementCollection)chain.ChainElements; foreach (X509ChainElement element in elements) { AddCertificate(element.Certificate); } break; } } // // public properties // public ArrayList? Certificates { get { return _certificates; } } public void AddCertificate(X509Certificate certificate) { ArgumentNullException.ThrowIfNull(certificate); _certificates ??= new ArrayList(); X509Certificate2 x509 = new X509Certificate2(certificate); _certificates.Add(x509); } public ArrayList? SubjectKeyIds { get { return _subjectKeyIds; } } public void AddSubjectKeyId(byte[] subjectKeyId) { _subjectKeyIds ??= new ArrayList(); _subjectKeyIds.Add(subjectKeyId); } public void AddSubjectKeyId(string subjectKeyId) { _subjectKeyIds ??= new ArrayList(); _subjectKeyIds.Add(Utils.DecodeHexString(subjectKeyId)); } public ArrayList? SubjectNames { get { return _subjectNames; } } public void AddSubjectName(string subjectName) { _subjectNames ??= new ArrayList(); _subjectNames.Add(subjectName); } public ArrayList? IssuerSerials { get { return _issuerSerials; } } public void AddIssuerSerial(string issuerName, string serialNumber) { if (string.IsNullOrEmpty(issuerName)) throw new ArgumentException(SR.Arg_EmptyOrNullString, nameof(issuerName)); if (string.IsNullOrEmpty(serialNumber)) throw new ArgumentException(SR.Arg_EmptyOrNullString, nameof(serialNumber)); BigInteger h; if (!BigInteger.TryParse(serialNumber, NumberStyles.AllowHexSpecifier, NumberFormatInfo.CurrentInfo, out h)) throw new ArgumentException(SR.Cryptography_Xml_InvalidX509IssuerSerialNumber, nameof(serialNumber)); // NetFx compat: .NET Framework treats the input as unsigned and we need to write down the X509SerialNumber // as a positive number. if (h < BigInteger.Zero) { byte[] bytes = h.ToByteArray(); Array.Resize(ref bytes, bytes.Length + 1); h = new BigInteger(bytes); } _issuerSerials ??= new ArrayList(); _issuerSerials.Add(Utils.CreateX509IssuerSerial(issuerName, h.ToString())); } // When we load an X509Data from Xml, we know the serial number is in decimal representation. internal void InternalAddIssuerSerial(string issuerName, string serialNumber) { _issuerSerials ??= new ArrayList(); _issuerSerials.Add(Utils.CreateX509IssuerSerial(issuerName, serialNumber)); } public byte[]? CRL { get { return _CRL; } set { _CRL = value; } } // // private methods // private void Clear() { _CRL = null; _subjectKeyIds?.Clear(); _subjectNames?.Clear(); _issuerSerials?.Clear(); _certificates?.Clear(); } // // public methods // public override XmlElement GetXml() { XmlDocument xmlDocument = new XmlDocument(); xmlDocument.PreserveWhitespace = true; return GetXml(xmlDocument); } internal override XmlElement GetXml(XmlDocument xmlDocument) { XmlElement x509DataElement = xmlDocument.CreateElement("X509Data", SignedXml.XmlDsigNamespaceUrl); if (_issuerSerials != null) { foreach (X509IssuerSerial issuerSerial in _issuerSerials) { XmlElement issuerSerialElement = xmlDocument.CreateElement("X509IssuerSerial", SignedXml.XmlDsigNamespaceUrl); XmlElement issuerNameElement = xmlDocument.CreateElement("X509IssuerName", SignedXml.XmlDsigNamespaceUrl); issuerNameElement.AppendChild(xmlDocument.CreateTextNode(issuerSerial.IssuerName)); issuerSerialElement.AppendChild(issuerNameElement); XmlElement serialNumberElement = xmlDocument.CreateElement("X509SerialNumber", SignedXml.XmlDsigNamespaceUrl); serialNumberElement.AppendChild(xmlDocument.CreateTextNode(issuerSerial.SerialNumber)); issuerSerialElement.AppendChild(serialNumberElement); x509DataElement.AppendChild(issuerSerialElement); } } if (_subjectKeyIds != null) { foreach (byte[] subjectKeyId in _subjectKeyIds) { XmlElement subjectKeyIdElement = xmlDocument.CreateElement("X509SKI", SignedXml.XmlDsigNamespaceUrl); subjectKeyIdElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(subjectKeyId))); x509DataElement.AppendChild(subjectKeyIdElement); } } if (_subjectNames != null) { foreach (string subjectName in _subjectNames) { XmlElement subjectNameElement = xmlDocument.CreateElement("X509SubjectName", SignedXml.XmlDsigNamespaceUrl); subjectNameElement.AppendChild(xmlDocument.CreateTextNode(subjectName)); x509DataElement.AppendChild(subjectNameElement); } } if (_certificates != null) { foreach (X509Certificate certificate in _certificates) { XmlElement x509Element = xmlDocument.CreateElement("X509Certificate", SignedXml.XmlDsigNamespaceUrl); x509Element.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(certificate.GetRawCertData()))); x509DataElement.AppendChild(x509Element); } } if (_CRL != null) { XmlElement crlElement = xmlDocument.CreateElement("X509CRL", SignedXml.XmlDsigNamespaceUrl); crlElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(_CRL))); x509DataElement.AppendChild(crlElement); } return x509DataElement; } public override void LoadXml(XmlElement element) { ArgumentNullException.ThrowIfNull(element); XmlNamespaceManager nsm = new XmlNamespaceManager(element.OwnerDocument.NameTable); nsm.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); XmlNodeList x509IssuerSerialNodes = element.SelectNodes("ds:X509IssuerSerial", nsm)!; XmlNodeList x509SKINodes = element.SelectNodes("ds:X509SKI", nsm)!; XmlNodeList x509SubjectNameNodes = element.SelectNodes("ds:X509SubjectName", nsm)!; XmlNodeList x509CertificateNodes = element.SelectNodes("ds:X509Certificate", nsm)!; XmlNodeList x509CRLNodes = element.SelectNodes("ds:X509CRL", nsm)!; if ((x509CRLNodes.Count == 0 && x509IssuerSerialNodes.Count == 0 && x509SKINodes.Count == 0 && x509SubjectNameNodes.Count == 0 && x509CertificateNodes.Count == 0)) // Bad X509Data tag, or Empty tag throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "X509Data"); // Flush anything in the lists Clear(); if (x509CRLNodes.Count != 0) _CRL = Convert.FromBase64String(Utils.DiscardWhiteSpaces(x509CRLNodes.Item(0)!.InnerText)); foreach (XmlNode issuerSerialNode in x509IssuerSerialNodes) { XmlNode? x509IssuerNameNode = issuerSerialNode.SelectSingleNode("ds:X509IssuerName", nsm); XmlNode? x509SerialNumberNode = issuerSerialNode.SelectSingleNode("ds:X509SerialNumber", nsm); if (x509IssuerNameNode == null || x509SerialNumberNode == null) throw new CryptographicException(SR.Cryptography_Xml_InvalidElement, "IssuerSerial"); InternalAddIssuerSerial(x509IssuerNameNode.InnerText.Trim(), x509SerialNumberNode.InnerText.Trim()); } foreach (XmlNode node in x509SKINodes) { AddSubjectKeyId(Convert.FromBase64String(Utils.DiscardWhiteSpaces(node.InnerText))); } foreach (XmlNode node in x509SubjectNameNodes) { AddSubjectName(node.InnerText.Trim()); } foreach (XmlNode node in x509CertificateNodes) { AddCertificate( X509CertificateLoader.LoadCertificate( Convert.FromBase64String(Utils.DiscardWhiteSpaces(node.InnerText)))); } } } }