| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- using System.Collections;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Security.Cryptography.X509Certificates;
- using System.Xml;
- namespace System.Security.Cryptography.Xml {
- public class SignedXml {
- /// <internalonly />
- protected Signature m_signature;
- /// <internalonly />
- protected string m_strSigningKeyName;
- private AsymmetricAlgorithm _signingKey;
- private XmlDocument _containingDocument;
- private IEnumerator _keyInfoEnum;
- private X509Certificate2Collection _x509Collection;
- private IEnumerator _x509Enum;
- private bool[] _refProcessed;
- private int[] _refLevelCache;
- internal XmlResolver _xmlResolver;
- internal XmlElement _context;
- private bool _bResolverSet;
- private Func<SignedXml, bool> _signatureFormatValidator = DefaultSignatureFormatValidator;
- private Collection<string> _safeCanonicalizationMethods;
- // Built in canonicalization algorithm URIs
- private static IList<string> s_knownCanonicalizationMethods;
- // Built in transform algorithm URIs (excluding canonicalization URIs)
- private static IList<string> s_defaultSafeTransformMethods;
- // additional HMAC Url identifiers
- private const string XmlDsigMoreHMACMD5Url = "http://www.w3.org/2001/04/xmldsig-more#hmac-md5";
- private const string XmlDsigMoreHMACSHA256Url = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256";
- private const string XmlDsigMoreHMACSHA384Url = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha384";
- private const string XmlDsigMoreHMACSHA512Url = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512";
- private const string XmlDsigMoreHMACRIPEMD160Url = "http://www.w3.org/2001/04/xmldsig-more#hmac-ripemd160";
- // defines the XML encryption processing rules
- private EncryptedXml _exml;
- //
- // public constant Url identifiers most frequently used within the XML Signature classes
- //
- public const string XmlDsigNamespaceUrl = "http://www.w3.org/2000/09/xmldsig#";
- public const string XmlDsigMinimalCanonicalizationUrl = "http://www.w3.org/2000/09/xmldsig#minimal";
- public const string XmlDsigCanonicalizationUrl = XmlDsigC14NTransformUrl;
- public const string XmlDsigCanonicalizationWithCommentsUrl = XmlDsigC14NWithCommentsTransformUrl;
- public const string XmlDsigSHA1Url = "http://www.w3.org/2000/09/xmldsig#sha1";
- public const string XmlDsigDSAUrl = "http://www.w3.org/2000/09/xmldsig#dsa-sha1";
- public const string XmlDsigRSASHA1Url = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
- public const string XmlDsigHMACSHA1Url = "http://www.w3.org/2000/09/xmldsig#hmac-sha1";
- public const string XmlDsigSHA256Url = "http://www.w3.org/2001/04/xmlenc#sha256";
- public const string XmlDsigRSASHA256Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
- // Yes, SHA384 is in the xmldsig-more namespace even though all the other SHA variants are in xmlenc. That's the standard.
- public const string XmlDsigSHA384Url = "http://www.w3.org/2001/04/xmldsig-more#sha384";
- public const string XmlDsigRSASHA384Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384";
- public const string XmlDsigSHA512Url = "http://www.w3.org/2001/04/xmlenc#sha512";
- public const string XmlDsigRSASHA512Url = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512";
- internal static readonly string XmlDsigDigestDefault = XmlDsigSHA256Url;
- internal static readonly string XmlDsigRSADefault = XmlDsigRSASHA256Url;
- public const string XmlDsigC14NTransformUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
- public const string XmlDsigC14NWithCommentsTransformUrl = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
- public const string XmlDsigExcC14NTransformUrl = "http://www.w3.org/2001/10/xml-exc-c14n#";
- public const string XmlDsigExcC14NWithCommentsTransformUrl = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments";
- public const string XmlDsigBase64TransformUrl = "http://www.w3.org/2000/09/xmldsig#base64";
- public const string XmlDsigXPathTransformUrl = "http://www.w3.org/TR/1999/REC-xpath-19991116";
- public const string XmlDsigXsltTransformUrl = "http://www.w3.org/TR/1999/REC-xslt-19991116";
- public const string XmlDsigEnvelopedSignatureTransformUrl = "http://www.w3.org/2000/09/xmldsig#enveloped-signature";
- public const string XmlDecryptionTransformUrl = "http://www.w3.org/2002/07/decrypt#XML";
- public const string XmlLicenseTransformUrl = "urn:mpeg:mpeg21:2003:01-REL-R-NS:licenseTransform";
- //
- // public constructors
- //
- public SignedXml ()
- {
- Initialize (null);
- }
- public SignedXml (XmlDocument document)
- {
- if (document == null)
- throw new ArgumentNullException (nameof (document));
-
- Initialize (document.DocumentElement);
- }
- public SignedXml (XmlElement elem)
- {
- if (elem == null)
- throw new ArgumentNullException (nameof (elem));
-
- Initialize (elem);
- }
- private void Initialize (XmlElement element)
- {
- _containingDocument = (element == null ? null : element.OwnerDocument);
- _context = element;
- m_signature = new Signature ();
- m_signature.SignedXml = this;
- m_signature.SignedInfo = new SignedInfo ();
- _signingKey = null;
- _safeCanonicalizationMethods = new Collection<string> (KnownCanonicalizationMethods);
- }
- //
- // public properties
- //
- /// <internalonly />
- public string SigningKeyName
- {
- get { return m_strSigningKeyName; }
- set { m_strSigningKeyName = value; }
- }
- public XmlResolver Resolver
- {
- // This property only has a setter. The rationale for this is that we don't have a good value
- // to return when it has not been explicitely set, as we are using XmlSecureResolver by default
- set
- {
- _xmlResolver = value;
- _bResolverSet = true;
- }
- }
- internal bool ResolverSet
- {
- get { return _bResolverSet; }
- }
- public Func<SignedXml, bool> SignatureFormatValidator
- {
- get { return _signatureFormatValidator; }
- set { _signatureFormatValidator = value; }
- }
- public Collection<string> SafeCanonicalizationMethods
- {
- get { return _safeCanonicalizationMethods; }
- }
- public AsymmetricAlgorithm SigningKey
- {
- get { return _signingKey; }
- set { _signingKey = value; }
- }
- public EncryptedXml EncryptedXml
- {
- get
- {
- if (_exml == null)
- _exml = new EncryptedXml (_containingDocument); // default processing rules
-
- return _exml;
- }
- set { _exml = value; }
- }
- public Signature Signature
- {
- get { return m_signature; }
- }
- public SignedInfo SignedInfo
- {
- get { return m_signature.SignedInfo; }
- }
- public string SignatureMethod
- {
- get { return m_signature.SignedInfo.SignatureMethod; }
- }
- public string SignatureLength
- {
- get { return m_signature.SignedInfo.SignatureLength; }
- }
- public byte[] SignatureValue
- {
- get { return m_signature.SignatureValue; }
- }
- public KeyInfo KeyInfo
- {
- get { return m_signature.KeyInfo; }
- set { m_signature.KeyInfo = value; }
- }
- public XmlElement GetXml ()
- {
- // If we have a document context, then return a signature element in this context
- if (_containingDocument != null)
- return m_signature.GetXml (_containingDocument);
- else
- return m_signature.GetXml ();
- }
- public void LoadXml (XmlElement value)
- {
- if (value == null)
- throw new ArgumentNullException (nameof (value));
- m_signature.LoadXml (value);
- if (_context == null)
- _context = value;
- _bCacheValid = false;
- }
- //
- // public methods
- //
- public void AddReference (Reference reference)
- {
- m_signature.SignedInfo.AddReference (reference);
- }
- public void AddObject (DataObject dataObject)
- {
- m_signature.AddObject (dataObject);
- }
- public bool CheckSignature ()
- {
- AsymmetricAlgorithm signingKey;
- return CheckSignatureReturningKey (out signingKey);
- }
- public bool CheckSignatureReturningKey (out AsymmetricAlgorithm signingKey)
- {
- SignedXmlDebugLog.LogBeginSignatureVerification (this, _context);
- signingKey = null;
- bool bRet = false;
- AsymmetricAlgorithm key = null;
- if (!CheckSignatureFormat ())
- return false;
- do {
- key = GetPublicKey ();
- if (key != null) {
- bRet = CheckSignature (key);
- SignedXmlDebugLog.LogVerificationResult (this, key, bRet);
- }
- } while (key != null && bRet == false);
- signingKey = key;
- return bRet;
- }
- public bool CheckSignature (AsymmetricAlgorithm key)
- {
- if (!CheckSignatureFormat ())
- return false;
- if (!CheckSignedInfo (key)) {
- SignedXmlDebugLog.LogVerificationFailure (this, SR.Log_VerificationFailed_SignedInfo);
- return false;
- }
- // Now is the time to go through all the references and see if their DigestValues are good
- if (!CheckDigestedReferences ()) {
- SignedXmlDebugLog.LogVerificationFailure (this, SR.Log_VerificationFailed_References);
- return false;
- }
- SignedXmlDebugLog.LogVerificationResult (this, key, true);
- return true;
- }
- public bool CheckSignature (KeyedHashAlgorithm macAlg)
- {
- if (!CheckSignatureFormat ())
- return false;
- if (!CheckSignedInfo (macAlg)) {
- SignedXmlDebugLog.LogVerificationFailure (this, SR.Log_VerificationFailed_SignedInfo);
- return false;
- }
- if (!CheckDigestedReferences ()) {
- SignedXmlDebugLog.LogVerificationFailure (this, SR.Log_VerificationFailed_References);
- return false;
- }
- SignedXmlDebugLog.LogVerificationResult (this, macAlg, true);
- return true;
- }
- public bool CheckSignature (X509Certificate2 certificate, bool verifySignatureOnly)
- {
- if (!verifySignatureOnly) {
- // Check key usages to make sure it is good for signing.
- foreach (X509Extension extension in certificate.Extensions) {
- if (string.Compare (extension.Oid.Value, "2.5.29.15" /* szOID_KEY_USAGE */, StringComparison.OrdinalIgnoreCase) == 0) {
- X509KeyUsageExtension keyUsage = new X509KeyUsageExtension ();
- keyUsage.CopyFrom (extension);
- SignedXmlDebugLog.LogVerifyKeyUsage (this, certificate, keyUsage);
- bool validKeyUsage = (keyUsage.KeyUsages & X509KeyUsageFlags.DigitalSignature) != 0 ||
- (keyUsage.KeyUsages & X509KeyUsageFlags.NonRepudiation) != 0;
- if (!validKeyUsage) {
- SignedXmlDebugLog.LogVerificationFailure (this, SR.Log_VerificationFailed_X509KeyUsage);
- return false;
- }
- break;
- }
- }
- // Do the chain verification to make sure the certificate is valid.
- X509Chain chain = new X509Chain ();
- chain.ChainPolicy.ExtraStore.AddRange (BuildBagOfCerts());
- bool chainVerified = chain.Build (certificate);
- SignedXmlDebugLog.LogVerifyX509Chain (this, chain, certificate);
- if (!chainVerified) {
- SignedXmlDebugLog.LogVerificationFailure (this, SR.Log_VerificationFailed_X509Chain);
- return false;
- }
- }
- using (AsymmetricAlgorithm publicKey = Utils.GetAnyPublicKey (certificate)) {
- if (!CheckSignature (publicKey))
- return false;
- }
- SignedXmlDebugLog.LogVerificationResult (this, certificate, true);
- return true;
- }
- public void ComputeSignature ()
- {
- SignedXmlDebugLog.LogBeginSignatureComputation (this, _context);
- BuildDigestedReferences ();
- // Load the key
- AsymmetricAlgorithm key = SigningKey;
- if (key == null)
- throw new CryptographicException (SR.Cryptography_Xml_LoadKeyFailed);
- // Check the signature algorithm associated with the key so that we can accordingly set the signature method
- if (SignedInfo.SignatureMethod == null) {
- if (key is DSA) {
- SignedInfo.SignatureMethod = XmlDsigDSAUrl;
- } else if (key is RSA) {
- if (SignedInfo.SignatureMethod == null)
- SignedInfo.SignatureMethod = XmlDsigRSADefault;
- } else {
- throw new CryptographicException (SR.Cryptography_Xml_CreatedKeyFailed);
- }
- }
- // See if there is a signature description class defined in the Config file
- SignatureDescription signatureDescription = CryptoHelpers.CreateFromName<SignatureDescription> (SignedInfo.SignatureMethod);
- if (signatureDescription == null)
- throw new CryptographicException (SR.Cryptography_Xml_SignatureDescriptionNotCreated);
- HashAlgorithm hashAlg = signatureDescription.CreateDigest ();
- if (hashAlg == null)
- throw new CryptographicException (SR.Cryptography_Xml_CreateHashAlgorithmFailed);
- byte[] hashvalue = GetC14NDigest (hashAlg);
- AsymmetricSignatureFormatter asymmetricSignatureFormatter = signatureDescription.CreateFormatter (key);
- SignedXmlDebugLog.LogSigning (this, key, signatureDescription, hashAlg, asymmetricSignatureFormatter);
- m_signature.SignatureValue = asymmetricSignatureFormatter.CreateSignature (hashAlg);
- }
- public void ComputeSignature (KeyedHashAlgorithm macAlg)
- {
- if (macAlg == null)
- throw new ArgumentNullException (nameof (macAlg));
- HMAC hash = macAlg as HMAC;
- if (hash == null)
- throw new CryptographicException (SR.Cryptography_Xml_SignatureMethodKeyMismatch);
- int signatureLength;
- if (m_signature.SignedInfo.SignatureLength == null)
- signatureLength = hash.HashSize;
- else
- signatureLength = Convert.ToInt32 (m_signature.SignedInfo.SignatureLength, null);
- // signatureLength should be less than hash size
- if (signatureLength < 0 || signatureLength > hash.HashSize)
- throw new CryptographicException (SR.Cryptography_Xml_InvalidSignatureLength);
- if (signatureLength % 8 != 0)
- throw new CryptographicException (SR.Cryptography_Xml_InvalidSignatureLength2);
- BuildDigestedReferences ();
- switch (hash.HashName) {
- case "SHA1":
- SignedInfo.SignatureMethod = SignedXml.XmlDsigHMACSHA1Url;
- break;
- case "SHA256":
- SignedInfo.SignatureMethod = SignedXml.XmlDsigMoreHMACSHA256Url;
- break;
- case "SHA384":
- SignedInfo.SignatureMethod = SignedXml.XmlDsigMoreHMACSHA384Url;
- break;
- case "SHA512":
- SignedInfo.SignatureMethod = SignedXml.XmlDsigMoreHMACSHA512Url;
- break;
- case "MD5":
- SignedInfo.SignatureMethod = SignedXml.XmlDsigMoreHMACMD5Url;
- break;
- case "RIPEMD160":
- SignedInfo.SignatureMethod = SignedXml.XmlDsigMoreHMACRIPEMD160Url;
- break;
- default:
- throw new CryptographicException (SR.Cryptography_Xml_SignatureMethodKeyMismatch);
- }
- byte[] hashValue = GetC14NDigest (hash);
- SignedXmlDebugLog.LogSigning (this, hash);
- m_signature.SignatureValue = new byte [signatureLength / 8];
- Buffer.BlockCopy (hashValue, 0, m_signature.SignatureValue, 0, signatureLength / 8);
- }
- //
- // virtual methods
- //
- protected virtual AsymmetricAlgorithm GetPublicKey ()
- {
- if (KeyInfo == null)
- throw new CryptographicException (SR.Cryptography_Xml_KeyInfoRequired);
- if (_x509Enum != null) {
- AsymmetricAlgorithm key = GetNextCertificatePublicKey ();
- if (key != null)
- return key;
- }
- if (_keyInfoEnum == null)
- _keyInfoEnum = KeyInfo.GetEnumerator ();
- // In our implementation, we move to the next KeyInfo clause which is an RSAKeyValue, DSAKeyValue or KeyInfoX509Data
- while (_keyInfoEnum.MoveNext()) {
- RSAKeyValue rsaKeyValue = _keyInfoEnum.Current as RSAKeyValue;
- if (rsaKeyValue != null)
- return rsaKeyValue.Key;
- DSAKeyValue dsaKeyValue = _keyInfoEnum.Current as DSAKeyValue;
- if (dsaKeyValue != null)
- return dsaKeyValue.Key;
- KeyInfoX509Data x509Data = _keyInfoEnum.Current as KeyInfoX509Data;
- if (x509Data != null) {
- _x509Collection = Utils.BuildBagOfCerts (x509Data, CertUsageType.Verification);
- if (_x509Collection.Count > 0) {
- _x509Enum = _x509Collection.GetEnumerator ();
- AsymmetricAlgorithm key = GetNextCertificatePublicKey ();
- if (key != null)
- return key;
- }
- }
- }
- return null;
- }
- private X509Certificate2Collection BuildBagOfCerts ()
- {
- X509Certificate2Collection collection = new X509Certificate2Collection ();
- if (KeyInfo != null) {
- foreach (KeyInfoClause clause in KeyInfo) {
- KeyInfoX509Data x509Data = clause as KeyInfoX509Data;
- if (x509Data != null)
- collection.AddRange (Utils.BuildBagOfCerts (x509Data, CertUsageType.Verification));
- }
- }
- return collection;
- }
- private AsymmetricAlgorithm GetNextCertificatePublicKey ()
- {
- while (_x509Enum.MoveNext ()) {
- X509Certificate2 certificate = (X509Certificate2)_x509Enum.Current;
- if (certificate != null)
- return Utils.GetAnyPublicKey (certificate);
- }
- return null;
- }
- public virtual XmlElement GetIdElement (XmlDocument document, string idValue)
- {
- return DefaultGetIdElement (document, idValue);
- }
- internal static XmlElement DefaultGetIdElement (XmlDocument document, string idValue)
- {
- if (document == null)
- return null;
- try {
- XmlConvert.VerifyNCName (idValue);
- } catch (XmlException) {
- // Identifiers are required to be an NCName
- // (xml:id version 1.0, part 4, paragraph 2, bullet 1)
- //
- // If it isn't an NCName, it isn't allowed to match.
- return null;
- }
- // Get the element with idValue
- XmlElement elem = document.GetElementById (idValue);
- if (elem != null) {
- // Have to check for duplicate ID values from the DTD.
- XmlDocument docClone = (XmlDocument)document.CloneNode (true);
- XmlElement cloneElem = docClone.GetElementById (idValue);
- // If it's null here we want to know about it, because it means that
- // GetElementById failed to work across the cloning, and our uniqueness
- // test is invalid.
- System.Diagnostics.Debug.Assert (cloneElem != null);
- // Guard against null anyways
- if (cloneElem != null) {
- cloneElem.Attributes.RemoveAll ();
- XmlElement cloneElem2 = docClone.GetElementById (idValue);
- if (cloneElem2 != null)
- throw new CryptographicException (SR.Cryptography_Xml_InvalidReference);
- }
- return elem;
- }
- elem = GetSingleReferenceTarget (document, "Id", idValue);
- if (elem != null)
- return elem;
- elem = GetSingleReferenceTarget (document, "id", idValue);
- if (elem != null)
- return elem;
- elem = GetSingleReferenceTarget (document, "ID", idValue);
- return elem;
- }
- //
- // private methods
- //
- private bool _bCacheValid;
- private byte[] _digestedSignedInfo;
- private static bool DefaultSignatureFormatValidator (SignedXml signedXml)
- {
- // Reject the signature if it uses a truncated HMAC
- if (signedXml.DoesSignatureUseTruncatedHmac ())
- return false;
- // Reject the signature if it uses a canonicalization algorithm other than
- // one of the ones explicitly allowed
- if (!signedXml.DoesSignatureUseSafeCanonicalizationMethod ())
- return false;
- // Otherwise accept it
- return true;
- }
- // Validation function to see if the current signature is signed with a truncated HMAC - one which
- // has a signature length of fewer bits than the whole HMAC output.
- private bool DoesSignatureUseTruncatedHmac ()
- {
- // If we're not using the SignatureLength property, then we're not truncating the signature length
- if (SignedInfo.SignatureLength == null)
- return false;
- // See if we're signed witn an HMAC algorithm
- HMAC hmac = CryptoHelpers.CreateFromName<HMAC> (SignatureMethod);
- if (hmac == null)
- return false; // We aren't signed with an HMAC algorithm, so we cannot have a truncated HMAC
- // Figure out how many bits the signature is using
- int actualSignatureSize = 0;
- if (!int.TryParse (SignedInfo.SignatureLength, out actualSignatureSize))
- return true; // If the value wasn't a valid integer, then we'll conservatively reject it all together
- // Make sure the full HMAC signature size is the same size that was specified in the XML
- // signature. If the actual signature size is not exactly the same as the full HMAC size, then
- // reject the signature.
- return actualSignatureSize != hmac.HashSize;
- }
- // Validation function to see if the signature uses a canonicalization algorithm from our list
- // of approved algorithm URIs.
- private bool DoesSignatureUseSafeCanonicalizationMethod ()
- {
- foreach (string safeAlgorithm in SafeCanonicalizationMethods) {
- if (string.Equals (safeAlgorithm, SignedInfo.CanonicalizationMethod, StringComparison.OrdinalIgnoreCase))
- return true;
- }
- SignedXmlDebugLog.LogUnsafeCanonicalizationMethod (this, SignedInfo.CanonicalizationMethod, SafeCanonicalizationMethods);
- return false;
- }
- private bool ReferenceUsesSafeTransformMethods (Reference reference)
- {
- TransformChain transformChain = reference.TransformChain;
- int transformCount = transformChain.Count;
- for (int i = 0; i < transformCount; i++) {
- Transform transform = transformChain [i];
- if (!IsSafeTransform (transform.Algorithm))
- return false;
- }
- return true;
- }
- private bool IsSafeTransform (string transformAlgorithm)
- {
- // All canonicalization algorithms are valid transform algorithms.
- foreach (string safeAlgorithm in SafeCanonicalizationMethods) {
- if (string.Equals (safeAlgorithm, transformAlgorithm, StringComparison.OrdinalIgnoreCase))
- return true;
- }
- foreach (string safeAlgorithm in DefaultSafeTransformMethods) {
- if (string.Equals (safeAlgorithm, transformAlgorithm, StringComparison.OrdinalIgnoreCase))
- return true;
- }
- SignedXmlDebugLog.LogUnsafeTransformMethod (
- this,
- transformAlgorithm,
- SafeCanonicalizationMethods,
- DefaultSafeTransformMethods);
- return false;
- }
- // Get a list of the built in canonicalization algorithms, as well as any that the machine admin has
- // added to the valid set.
- private static IList<string> KnownCanonicalizationMethods {
- get {
- if (s_knownCanonicalizationMethods == null) {
- // Start with the list that the machine admin added, if any
- List<string> safeAlgorithms = new List<string> ();
- // Built in algorithms
- safeAlgorithms.Add (XmlDsigC14NTransformUrl);
- safeAlgorithms.Add (XmlDsigC14NWithCommentsTransformUrl);
- safeAlgorithms.Add (XmlDsigExcC14NTransformUrl);
- safeAlgorithms.Add (XmlDsigExcC14NWithCommentsTransformUrl);
- s_knownCanonicalizationMethods = safeAlgorithms;
- }
- return s_knownCanonicalizationMethods;
- }
- }
- private static IList<string> DefaultSafeTransformMethods {
- get {
- if (s_defaultSafeTransformMethods == null) {
- List<string> safeAlgorithms = new List<string> ();
- // Built in algorithms
- // KnownCanonicalizationMethods don't need to be added here, because
- // the validator will automatically accept those.
- //
- // xmldsig 6.6.1:
- // Any canonicalization algorithm that can be used for
- // CanonicalizationMethod can be used as a Transform.
- safeAlgorithms.Add (XmlDsigEnvelopedSignatureTransformUrl);
- safeAlgorithms.Add (XmlDsigBase64TransformUrl);
- safeAlgorithms.Add (XmlLicenseTransformUrl);
- safeAlgorithms.Add (XmlDecryptionTransformUrl);
- s_defaultSafeTransformMethods = safeAlgorithms;
- }
- return s_defaultSafeTransformMethods;
- }
- }
- private byte[] GetC14NDigest (HashAlgorithm hash)
- {
- bool isKeyedHashAlgorithm = hash is KeyedHashAlgorithm;
- if (isKeyedHashAlgorithm || !_bCacheValid || !SignedInfo.CacheValid) {
- string baseUri = (_containingDocument == null ? null : _containingDocument.BaseURI);
- XmlResolver resolver = (_bResolverSet ? _xmlResolver : new XmlSecureResolver (new XmlUrlResolver (), baseUri));
- XmlDocument doc = Utils.PreProcessElementInput (SignedInfo.GetXml (), resolver, baseUri);
- // Add non default namespaces in scope
- CanonicalXmlNodeList namespaces = (_context == null ? null : Utils.GetPropagatedAttributes (_context));
- SignedXmlDebugLog.LogNamespacePropagation (this, namespaces);
- Utils.AddNamespaces (doc.DocumentElement, namespaces);
- Transform c14nMethodTransform = SignedInfo.CanonicalizationMethodObject;
- c14nMethodTransform.Resolver = resolver;
- c14nMethodTransform.BaseURI = baseUri;
- SignedXmlDebugLog.LogBeginCanonicalization (this, c14nMethodTransform);
- c14nMethodTransform.LoadInput (doc);
- SignedXmlDebugLog.LogCanonicalizedOutput (this, c14nMethodTransform);
- _digestedSignedInfo = c14nMethodTransform.GetDigestedOutput (hash);
- _bCacheValid = !isKeyedHashAlgorithm;
- }
- return _digestedSignedInfo;
- }
- private int GetReferenceLevel (int index, ArrayList references)
- {
- if (_refProcessed [index]) return _refLevelCache [index];
- _refProcessed [index] = true;
- Reference reference = (Reference)references [index];
- if (reference.Uri == null || reference.Uri.Length == 0 || (reference.Uri.Length > 0 && reference.Uri [0] != '#')) {
- _refLevelCache [index] = 0;
- return 0;
- }
- if (reference.Uri.Length > 0 && reference.Uri [0] == '#')
- {
- string idref = Utils.ExtractIdFromLocalUri (reference.Uri);
- if (idref == "xpointer(/)") {
- _refLevelCache [index] = 0;
- return 0;
- }
- // If this is pointing to another reference
- for (int j = 0; j < references.Count; ++j) {
- if (((Reference)references [j]).Id == idref) {
- _refLevelCache [index] = GetReferenceLevel (j, references) + 1;
- return (_refLevelCache [index]);
- }
- }
- // Then the reference points to an object tag
- _refLevelCache [index] = 0;
- return 0;
- }
- // Malformed reference
- throw new CryptographicException (SR.Cryptography_Xml_InvalidReference);
- }
- private class ReferenceLevelSortOrder : IComparer
- {
- private ArrayList _references;
- public ReferenceLevelSortOrder ()
- {
- }
- public ArrayList References
- {
- get { return _references; }
- set { _references = value; }
- }
- public int Compare (object a, object b)
- {
- Reference referenceA = a as Reference;
- Reference referenceB = b as Reference;
- // Get the indexes
- int iIndexA = 0;
- int iIndexB = 0;
- int i = 0;
- foreach (Reference reference in References) {
- if (reference == referenceA) iIndexA = i;
- if (reference == referenceB) iIndexB = i;
- i++;
- }
- int iLevelA = referenceA.SignedXml.GetReferenceLevel (iIndexA, References);
- int iLevelB = referenceB.SignedXml.GetReferenceLevel (iIndexB, References);
- return iLevelA.CompareTo (iLevelB);
- }
- }
- private void BuildDigestedReferences ()
- {
- // Default the DigestMethod and Canonicalization
- ArrayList references = SignedInfo.References;
- // Reset the cache
- _refProcessed = new bool [references.Count];
- _refLevelCache = new int [references.Count];
- ReferenceLevelSortOrder sortOrder = new ReferenceLevelSortOrder ();
- sortOrder.References = references;
- // Don't alter the order of the references array list
- ArrayList sortedReferences = new ArrayList ();
- foreach (Reference reference in references)
- sortedReferences.Add (reference);
- sortedReferences.Sort (sortOrder);
- CanonicalXmlNodeList nodeList = new CanonicalXmlNodeList ();
- foreach (DataObject obj in m_signature.ObjectList)
- nodeList.Add (obj.GetXml ());
- foreach (Reference reference in sortedReferences) {
- if (reference.DigestMethod == null)
- reference.DigestMethod = XmlDsigDigestDefault;
- SignedXmlDebugLog.LogSigningReference (this, reference);
- reference.UpdateHashValue (_containingDocument, nodeList);
- // If this reference has an Id attribute, add it
- if (reference.Id != null)
- nodeList.Add (reference.GetXml ());
- }
- }
- private bool CheckDigestedReferences ()
- {
- ArrayList references = m_signature.SignedInfo.References;
- for (int i = 0; i < references.Count; ++i) {
- Reference digestedReference = (Reference)references [i];
- if (!ReferenceUsesSafeTransformMethods (digestedReference))
- return false;
- SignedXmlDebugLog.LogVerifyReference (this, digestedReference);
- byte[] calculatedHash = null;
- try {
- calculatedHash = digestedReference.CalculateHashValue (_containingDocument, m_signature.ReferencedItems);
- } catch (CryptoSignedXmlRecursionException) {
- SignedXmlDebugLog.LogSignedXmlRecursionLimit (this, digestedReference);
- return false;
- }
- // Compare both hashes
- SignedXmlDebugLog.LogVerifyReferenceHash (this, digestedReference, calculatedHash, digestedReference.DigestValue);
- if (!CryptographicEquals (calculatedHash, digestedReference.DigestValue))
- return false;
- }
- return true;
- }
- // Methods _must_ be marked both No Inlining and No Optimization to be fully opted out of optimization.
- // This is because if a candidate method is inlined, its method level attributes, including the NoOptimization
- // attribute, are lost.
- // This method makes no attempt to disguise the length of either of its inputs. It is assumed the attacker has
- // knowledge of the algorithms used, and thus the output length. Length is difficult to properly blind in modern CPUs.
- [MethodImpl (MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- private static bool CryptographicEquals (byte[] a, byte[] b)
- {
- System.Diagnostics.Debug.Assert (a != null);
- System.Diagnostics.Debug.Assert (b != null);
- int result = 0;
- // Short cut if the lengths are not identical
- if (a.Length != b.Length)
- return false;
- unchecked
- {
- // Normally this caching doesn't matter, but with the optimizer off, this nets a non-trivial speedup.
- int aLength = a.Length;
- for (int i = 0; i < aLength; i++) {
- // We use subtraction here instead of XOR because the XOR algorithm gets ever so
- // slightly faster as more and more differences pile up.
- // This cannot overflow more than once (and back to 0) because bytes are 1 byte
- // in length, and result is 4 bytes. The OR propagates all set bytes, so the differences
- // can't add up and overflow a second time.
- result = result | (a [i] - b [i]);
- }
- }
- return (0 == result);
- }
- // If we have a signature format validation callback, check to see if this signature's format (not
- // the signature itself) is valid according to the validator. A return value of true indicates that
- // the signature format is acceptable, false means that the format is not valid.
- private bool CheckSignatureFormat ()
- {
- if (_signatureFormatValidator == null) {
- // No format validator means that we default to accepting the signature. (This is
- // effectively compatibility mode with v3.5).
- return true;
- }
- SignedXmlDebugLog.LogBeginCheckSignatureFormat (this, _signatureFormatValidator);
- bool formatValid = _signatureFormatValidator (this);
- SignedXmlDebugLog.LogFormatValidationResult (this, formatValid);
- return formatValid;
- }
- private bool CheckSignedInfo (AsymmetricAlgorithm key)
- {
- if (key == null)
- throw new ArgumentNullException (nameof (key));
- SignedXmlDebugLog.LogBeginCheckSignedInfo (this, m_signature.SignedInfo);
- SignatureDescription signatureDescription = CryptoHelpers.CreateFromName<SignatureDescription> (SignatureMethod);
- if (signatureDescription == null)
- throw new CryptographicException (SR.Cryptography_Xml_SignatureDescriptionNotCreated);
- // Let's see if the key corresponds with the SignatureMethod
- Type ta = Type.GetType (signatureDescription.KeyAlgorithm);
- if (!IsKeyTheCorrectAlgorithm (key, ta))
- return false;
- HashAlgorithm hashAlgorithm = signatureDescription.CreateDigest ();
- if (hashAlgorithm == null)
- throw new CryptographicException (SR.Cryptography_Xml_CreateHashAlgorithmFailed);
- byte[] hashval = GetC14NDigest (hashAlgorithm);
- AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = signatureDescription.CreateDeformatter (key);
- SignedXmlDebugLog.LogVerifySignedInfo (this,
- key,
- signatureDescription,
- hashAlgorithm,
- asymmetricSignatureDeformatter,
- hashval,
- m_signature.SignatureValue);
- return asymmetricSignatureDeformatter.VerifySignature (hashval, m_signature.SignatureValue);
- }
- private bool CheckSignedInfo (KeyedHashAlgorithm macAlg)
- {
- if (macAlg == null)
- throw new ArgumentNullException (nameof (macAlg));
- SignedXmlDebugLog.LogBeginCheckSignedInfo (this, m_signature.SignedInfo);
- int signatureLength;
- if (m_signature.SignedInfo.SignatureLength == null)
- signatureLength = macAlg.HashSize;
- else
- signatureLength = Convert.ToInt32 (m_signature.SignedInfo.SignatureLength, null);
- // signatureLength should be less than hash size
- if (signatureLength < 0 || signatureLength > macAlg.HashSize)
- throw new CryptographicException (SR.Cryptography_Xml_InvalidSignatureLength);
- if (signatureLength % 8 != 0)
- throw new CryptographicException (SR.Cryptography_Xml_InvalidSignatureLength2);
- if (m_signature.SignatureValue == null)
- throw new CryptographicException (SR.Cryptography_Xml_SignatureValueRequired);
- if (m_signature.SignatureValue.Length != signatureLength / 8)
- throw new CryptographicException (SR.Cryptography_Xml_InvalidSignatureLength);
- // Calculate the hash
- byte[] hashValue = GetC14NDigest (macAlg);
- SignedXmlDebugLog.LogVerifySignedInfo (this, macAlg, hashValue, m_signature.SignatureValue);
- for (int i = 0; i < m_signature.SignatureValue.Length; i++)
- if (m_signature.SignatureValue [i] != hashValue [i]) return false;
- return true;
- }
- private static XmlElement GetSingleReferenceTarget (XmlDocument document, string idAttributeName, string idValue)
- {
- // idValue has already been tested as an NCName (unless overridden for compatibility), so there's no
- // escaping that needs to be done here.
- string xPath = "//*[@" + idAttributeName + "=\"" + idValue + "\"]";
- // http://www.w3.org/TR/xmldsig-core/#sec-ReferenceProcessingModel says that for the form URI="#chapter1":
- //
- // Identifies a node-set containing the element with ID attribute value 'chapter1' ...
- //
- // Note that it uses the singular. Therefore, if the match is ambiguous, we should consider the document invalid.
- //
- // In this case, we'll treat it the same as having found nothing across all fallbacks (but shortcut so that we don't
- // fall into a trap of finding a secondary element which wasn't the originally signed one).
- XmlNodeList nodeList = document.SelectNodes (xPath);
- if (nodeList == null || nodeList.Count == 0)
- return null;
- if (nodeList.Count == 1)
- return nodeList [0] as XmlElement;
- throw new CryptographicException (SR.Cryptography_Xml_InvalidReference);
- }
- private static bool IsKeyTheCorrectAlgorithm (AsymmetricAlgorithm key, Type expectedType)
- {
- Type actualType = key.GetType ();
- if (actualType == expectedType)
- return true;
- // This check exists solely for compatibility with 4.6. Normally, we would expect "expectedType" to be the superclass type and
- // the actualType to be the subclass.
- if (expectedType.IsSubclassOf (actualType))
- return true;
- //
- // "expectedType" comes from the KeyAlgorithm property of a SignatureDescription. The BCL SignatureDescription classes have historically
- // denoted provider-specific implementations ("RSACryptoServiceProvider") rather than the base class for the algorithm ("RSA"). We could
- // change those (at the risk of creating other compat problems) but we have no control over third party SignatureDescriptions.
- //
- // So, in the absence of a better approach, walk up the parent hierarchy until we find the ancestor that's a direct subclass of
- // AsymmetricAlgorithm and treat that as the algorithm identifier.
- //
- while (expectedType != null && expectedType.BaseType != typeof (AsymmetricAlgorithm))
- expectedType = expectedType.BaseType;
- if (expectedType == null)
- return false; // SignatureDescription specified something that isn't even a subclass of AsymmetricAlgorithm. For compatibility with 4.6, return false rather throw.
- if (actualType.IsSubclassOf (expectedType))
- return true;
- return false;
- }
- }
- }
|