private PackageDigitalSignature Sign( IEnumerable<Uri> parts, IEnumerable<PackageRelationshipSelector> relationshipSelectors, X509Certificate2 signer, String signatureId, bool embedCertificate, IEnumerable<System.Security.Cryptography.Xml.DataObject> signatureObjects, IEnumerable<System.Security.Cryptography.Xml.Reference> objectReferences) { // don't overwrite Debug.Assert(SignaturePart.GetStream().Length == 0, "Logic Error: Can't sign when signature already exists"); // grab hash algorithm as this may change in the future _hashAlgorithmName = _manager.HashAlgorithm; // keep the signer if indicated if (_manager.CertificateOption == CertificateEmbeddingOption.NotEmbedded) _lookForEmbeddedCert = false; // don't bother parsing else _certificate = signer; // save some parsing // we only release this key if we obtain it AsymmetricAlgorithm key = null; bool ownKey = false; if (signer.HasPrivateKey) { key = signer.PrivateKey; } else { ownKey = true; key = GetPrivateKeyForSigning(signer); } try { _signedXml = new CustomSignedXml(); _signedXml.SigningKey = key; _signedXml.Signature.Id = signatureId; // put it in the XML if (embedCertificate) { _signedXml.KeyInfo = GenerateKeyInfo(key, signer); } // Package object tag // convert from string to class and ensure we dispose using (HashAlgorithm hashAlgorithm = GetHashAlgorithm(_hashAlgorithmName)) { // inform caller if hash algorithm is unknown if (hashAlgorithm == null) throw new InvalidOperationException(SR.Get(SRID.UnsupportedHashAlgorithm)); _signedXml.AddObject(GenerateObjectTag(hashAlgorithm, parts, relationshipSelectors, signatureId)); } // add reference from SignedInfo to Package object tag Reference objectReference = new Reference(XTable.Get(XTable.ID.OpcLinkAttrValue)); objectReference.Type = XTable.Get(XTable.ID.W3CSignatureNamespaceRoot) + "Object"; objectReference.DigestMethod = _hashAlgorithmName; _signedXml.AddReference(objectReference); // add any custom object tags AddCustomObjectTags(signatureObjects, objectReferences); // compute the signature SignedXml xmlSig = _signedXml; (new PermissionSet(PermissionState.Unrestricted)).Assert(); try { xmlSig.ComputeSignature(); } finally { PermissionSet.RevertAssert(); } // persist UpdatePartFromSignature(_signedXml.Signature); } finally { if (key != null && ownKey) ((IDisposable)key).Dispose(); } // create the PackageDigitalSignature object _signature = new PackageDigitalSignature(_manager, this); return _signature; }
//------------------------------------------------------ // // Private Properties // //------------------------------------------------------ /// <summary> /// Helper class /// </summary> private SignedXml EnsureXmlSignatureParsed() { // lazy init if (_signedXml == null) { _signedXml = new CustomSignedXml(); // Load the XML XmlDocument xmlDocument = new XmlDocument(); xmlDocument.PreserveWhitespace = true; using (Stream s = SignaturePart.GetStream()) { using (XmlTextReader xmlReader = new XmlTextReader(s)) { //Prohibit DTD from the markup as per the OPC spec xmlReader.ProhibitDtd = true; //This method expects the reader to be in ReadState.Initial. //It will make the first read call. PackagingUtilities.PerformInitailReadAndVerifyEncoding(xmlReader); //If the reader.ReadState is ReadState.Initial, then XmlDocument with perform the //first xmlReader.Read() and start loading from that node/tag. //If the reader.ReadState is ReadState.Intermediate, then XmlDocument, will start //loading from that location itself. //Note: Since in the above method we perform only the first read and will have not //moved the reader state further down in the markup, we should be okay, and //xmlDocument.Load will load from the very begining as intended. xmlDocument.Load(xmlReader); // W3C spec allows for Signature tag to appear as an island and inherently allows // for multiple Signature tags within the same XML document. // OPC restricts this to a single, root-level Signature tag. However, Signature // tags are allowed to exist within the non-OPC Object tags within an OPC signature. // This is common for XAdES signatures and must be explicitly allowed. XmlNodeList nodeList = xmlDocument.ChildNodes; if (nodeList == null || nodeList.Count == 0 || nodeList.Count > 2) throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); XmlNode node = nodeList[0]; if (nodeList.Count == 2) { // First node must be the XmlDeclaration <?xml...> if (nodeList[0].NodeType != XmlNodeType.XmlDeclaration) throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); // Second node must be in the w3c namespace, and must be the <Signature> tag node = nodeList[1]; } if ((node.NodeType != XmlNodeType.Element) || (String.CompareOrdinal(node.NamespaceURI, SignedXml.XmlDsigNamespaceUrl) != 0) || (String.CompareOrdinal(node.LocalName, XTable.Get(XTable.ID.SignatureTagName)) != 0)) { throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); } // instantiate the SignedXml from the xmlDoc _signedXml.LoadXml((XmlElement)node); } } } // As per the OPC spec, only two Canonicalization methods can be specified if (!IsValidXmlCanonicalizationTransform(_signedXml.SignedInfo.CanonicalizationMethod)) throw new XmlException(SR.Get(SRID.UnsupportedCanonicalizationMethod)); // As per OPC spec, signature ID must be NCName if (_signedXml.Signature.Id != null) { try { System.Xml.XmlConvert.VerifyNCName(_signedXml.Signature.Id); } catch (System.Xml.XmlException) { throw new XmlException(SR.Get(SRID.PackageSignatureCorruption)); } } return _signedXml; }