File: System\Security\Cryptography\Xml\DSAKeyValue.cs
Web Access
Project: src\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.Runtime.Versioning;
using System.Xml;
 
#pragma warning disable CA5384 // Do Not Use Digital Signature Algorithm (DSA)
 
namespace System.Security.Cryptography.Xml
{
    public class DSAKeyValue : KeyInfoClause
    {
        private DSA _key;
 
        //
        // public constructors
        //
 
        [UnsupportedOSPlatform("ios")]
        [UnsupportedOSPlatform("tvos")]
        public DSAKeyValue()
        {
            _key = DSA.Create();
        }
 
        public DSAKeyValue(DSA key)
        {
            if (key is null)
            {
                throw new ArgumentNullException(nameof(key));
            }
 
            _key = key;
        }
 
        //
        // public properties
        //
 
        public DSA Key
        {
            get => _key;
            set
            {
                if (value is null)
                {
                    throw new ArgumentNullException(nameof(value));
                }
 
                _key = value;
            }
        }
 
        //
        // public methods
        //
 
        /// <summary>
        /// Create an XML representation.
        /// </summary>
        /// <remarks>
        /// Based upon https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue.
        /// </remarks>
        /// <returns>
        /// An <see cref="XmlElement"/> containing the XML representation.
        /// </returns>
        public override XmlElement GetXml()
        {
            XmlDocument xmlDocument = new XmlDocument();
            xmlDocument.PreserveWhitespace = true;
            return GetXml(xmlDocument);
        }
 
        private const string KeyValueElementName = "KeyValue";
        private const string DSAKeyValueElementName = "DSAKeyValue";
 
        //Optional {P,Q}-Sequence
        private const string PElementName = "P";
        private const string QElementName = "Q";
 
        //Optional Members
        private const string GElementName = "G";
        private const string JElementName = "J";
 
        //Mandatory Members
        private const string YElementName = "Y";
 
        //Optional {Seed,PgenCounter}-Sequence
        private const string SeedElementName = "Seed";
        private const string PgenCounterElementName = "PgenCounter";
 
        internal override XmlElement GetXml(XmlDocument xmlDocument)
        {
            DSAParameters dsaParams = _key.ExportParameters(false);
 
            XmlElement keyValueElement = xmlDocument.CreateElement(KeyValueElementName, SignedXml.XmlDsigNamespaceUrl);
            XmlElement dsaKeyValueElement = xmlDocument.CreateElement(DSAKeyValueElementName, SignedXml.XmlDsigNamespaceUrl);
 
            XmlElement pElement = xmlDocument.CreateElement(PElementName, SignedXml.XmlDsigNamespaceUrl);
            pElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.P!)));
            dsaKeyValueElement.AppendChild(pElement);
 
            XmlElement qElement = xmlDocument.CreateElement(QElementName, SignedXml.XmlDsigNamespaceUrl);
            qElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.Q!)));
            dsaKeyValueElement.AppendChild(qElement);
 
            XmlElement gElement = xmlDocument.CreateElement(GElementName, SignedXml.XmlDsigNamespaceUrl);
            gElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.G!)));
            dsaKeyValueElement.AppendChild(gElement);
 
            XmlElement yElement = xmlDocument.CreateElement(YElementName, SignedXml.XmlDsigNamespaceUrl);
            yElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.Y!)));
            dsaKeyValueElement.AppendChild(yElement);
 
            // Add optional components if present
            if (dsaParams.J != null)
            {
                XmlElement jElement = xmlDocument.CreateElement(JElementName, SignedXml.XmlDsigNamespaceUrl);
                jElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.J)));
                dsaKeyValueElement.AppendChild(jElement);
            }
 
            if (dsaParams.Seed != null)
            {  // note we assume counter is correct if Seed is present
                XmlElement seedElement = xmlDocument.CreateElement(SeedElementName, SignedXml.XmlDsigNamespaceUrl);
                seedElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(dsaParams.Seed)));
                dsaKeyValueElement.AppendChild(seedElement);
 
                XmlElement counterElement = xmlDocument.CreateElement(PgenCounterElementName, SignedXml.XmlDsigNamespaceUrl);
                counterElement.AppendChild(xmlDocument.CreateTextNode(Convert.ToBase64String(Utils.ConvertIntToByteArray(dsaParams.Counter))));
                dsaKeyValueElement.AppendChild(counterElement);
            }
 
            keyValueElement.AppendChild(dsaKeyValueElement);
 
            return keyValueElement;
        }
 
        /// <summary>
        /// Deserialize from the XML representation.
        /// </summary>
        /// <remarks>
        /// Based upon https://www.w3.org/TR/xmldsig-core/#sec-DSAKeyValue.
        /// </remarks>
        /// <param name="value">
        /// An <see cref="XmlElement"/> containing the XML representation. This cannot be null.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> cannot be null.
        /// </exception>
        /// <exception cref="CryptographicException">
        /// The XML has the incorrect schema or the DSA parameters are invalid.
        /// </exception>
        public override void LoadXml(XmlElement value)
        {
            if (value is null)
            {
                throw new ArgumentNullException(nameof(value));
            }
 
            if (value.Name != KeyValueElementName
                || value.NamespaceURI != SignedXml.XmlDsigNamespaceUrl)
            {
                throw new CryptographicException(SR.Format(SR.WrongRootElement, KeyValueElementName, SignedXml.XmlDsigNamespaceUrl));
            }
 
            const string xmlDsigNamespacePrefix = "dsig";
            XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(value.OwnerDocument.NameTable);
            xmlNamespaceManager.AddNamespace(xmlDsigNamespacePrefix, SignedXml.XmlDsigNamespaceUrl);
 
            XmlNode? dsaKeyValueElement = value.SelectSingleNode($"{xmlDsigNamespacePrefix}:{DSAKeyValueElementName}", xmlNamespaceManager);
            if (dsaKeyValueElement == null)
            {
                throw new CryptographicException(SR.Format(SR.MustContainChildElement, KeyValueElementName, DSAKeyValueElementName));
            }
 
            XmlNode? yNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{YElementName}", xmlNamespaceManager);
            if (yNode == null)
                throw new CryptographicException(SR.Format(SR.ElementMissing, YElementName));
 
            XmlNode? pNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{PElementName}", xmlNamespaceManager);
            XmlNode? qNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{QElementName}", xmlNamespaceManager);
 
            if ((pNode == null && qNode != null) || (pNode != null && qNode == null))
                throw new CryptographicException(SR.Format(SR.ElementCombinationMissing, PElementName, QElementName));
 
 
            XmlNode? gNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{GElementName}", xmlNamespaceManager);
            XmlNode? jNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{JElementName}", xmlNamespaceManager);
 
            XmlNode? seedNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{SeedElementName}", xmlNamespaceManager);
            XmlNode? pgenCounterNode = dsaKeyValueElement.SelectSingleNode($"{xmlDsigNamespacePrefix}:{PgenCounterElementName}", xmlNamespaceManager);
            if ((seedNode == null && pgenCounterNode != null) || (seedNode != null && pgenCounterNode == null))
                throw new CryptographicException(SR.Format(SR.ElementCombinationMissing, SeedElementName, PgenCounterElementName));
 
            try
            {
                Key.ImportParameters(new DSAParameters
                {
                    P = (pNode != null) ? Convert.FromBase64String(pNode.InnerText) : null,
                    Q = (qNode != null) ? Convert.FromBase64String(qNode.InnerText) : null,
                    G = (gNode != null) ? Convert.FromBase64String(gNode.InnerText) : null,
                    Y = Convert.FromBase64String(yNode.InnerText),
                    J = (jNode != null) ? Convert.FromBase64String(jNode.InnerText) : null,
                    Seed = (seedNode != null) ? Convert.FromBase64String(seedNode.InnerText) : null,
                    Counter = (pgenCounterNode != null) ? Utils.ConvertByteArrayToInt(Convert.FromBase64String(pgenCounterNode.InnerText)) : 0
                });
            }
            catch (Exception ex)
            {
                throw new CryptographicException($"An error occurred parsing the key components", ex);
            }
        }
    }
}