{"record":{"id":"caa12520501d3b93","repo":"dotnet/wpf","slug":"sr-xmlsignaturemanifest","errorCode":null,"errorMessage":"SR.RelationshipTransformNotFollowedByCanonicalizationTransform","messagePattern":"SR\\.RelationshipTransformNotFollowedByCanonicalizationTransform","errorType":"exception","errorClass":"System.Xml.XmlException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs","lineNumber":394,"sourceCode":"                                continue;   // success\n                            }\n                            else\n                                throw new InvalidOperationException(SR.UnsupportedTransformAlgorithm);\n                        }\n                    }\n                }\n                throw new XmlException(SR.XmlSignatureParseError);\n            }\n\n            if (transforms.Count == 0)\n                throw new XmlException(SR.XmlSignatureParseError);\n            \n            //If we found another transform after the Relationship transform, it will be validated earlier\n            //in this method to make sure that its a supported xml canonicalization algorithm and so we can \n            //simplify this test condition - As per the OPC spec - Relationship transform must be followed\n            //by a canonicalization algorithm.\n            if (relationshipTransformFound && (transforms.Count == transformsCountWhenRelationshipTransformFound))\n                throw new XmlException(SR.RelationshipTransformNotFollowedByCanonicalizationTransform);\n\n            return transforms;\n        }\n\n        /// <summary>\n        /// Parse the Relationship-specific Transform\n        /// </summary>\n        /// <param name=\"reader\"></param>\n        /// <param name=\"partUri\"></param>\n        /// <param name=\"relationshipSelectors\">may be allocated but will never be empty</param>\n        private static void ParseRelationshipsTransform(XmlReader reader, Uri partUri, ref List<PackageRelationshipSelector> relationshipSelectors)\n        {\n            Uri owningPartUri = System.IO.Packaging.PackUriHelper.GetSourcePartUriFromRelationshipPartUri(partUri);\n\n            // find all of the Relationship tags of form:\n            //      <RelationshipReference SourceId=\"abc123\" />\n            // or \n            //      <RelationshipsGroupReference SourceType=\"reference-type-of-the-week\" />","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs#L376-L412","documentation":"Per the OPC spec, a Relationship Transform must be immediately followed by a canonicalization transform. When a Relationship transform was found but no canonicalization transform was appended after it (transform count unchanged since the Relationship entry), the parser throws RelationshipTransformNotFollowedByCanonicalizationTransform.","triggerScenarios":"Verify on a signature whose Reference contains a Relationship Transform as the last (or only) transform, with no canonicalization transform following it.","commonSituations":"Signers implementing an older or partial interpretation of the OPC signature profile; manually assembled manifests omitting the required trailing C14N transform; migrated signatures from other packaging SDKs.","solutions":["Append a canonicalization Transform (e.g. http://www.w3.org/2001/10/xml-exc-c14n#) immediately after the Relationship Transform and re-sign","Re-sign the package with PackageDigitalSignatureManager, which emits the required transform pair","Update the signing tool to the OPC-compliant transform ordering (Relationship then C14N)","Validate manifest transform order against the OPC spec before distribution"],"exampleFix":"// before\n<Transforms>\n  <Transform Algorithm=\"http://schemas.openxmlformats.org/package/2006/RelationshipTransform\"/>\n</Transforms>\n// after\n<Transforms>\n  <Transform Algorithm=\"http://schemas.openxmlformats.org/package/2006/RelationshipTransform\"/>\n  <Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\n</Transforms>","handlingStrategy":"validation","validationCode":"const string relNs = \"http://schemas.openxmlformats.org/package/2006/RelationshipTransform\";\nvar c14nSet = new HashSet<string> { \"http://www.w3.org/2001/10/xml-exc-c14n#\" };\nbool ordered = doc.Descendants(ds + \"Transforms\").All(t =>\n{\n    var list = t.Elements(ds + \"Transform\").Select(x => (string)x.Attribute(\"Algorithm\")).ToList();\n    for (int i = 0; i < list.Count; i++)\n        if (list[i] == relNs && (i + 1 >= list.Count || !c14nSet.Contains(list[i + 1])))\n            return false;\n    return true;\n});\nif (!ordered) throw new InvalidDataException(\"Relationship Transform must be followed by canonicalization\");","typeGuard":null,"tryCatchPattern":"try\n{\n    sigManager.VerifySignatures(true);\n}\ncatch (XmlException ex) when (ex.Message.Contains(\"Canonicalization\"))\n{\n    // wrong transform order: re-sign with Relationship + C14N pair\n}","preventionTips":["Always emit Relationship Transform followed immediately by a canonicalization Transform","Use PackageDigitalSignatureManager for signing to get correct ordering","Validate transform ordering against the OPC spec before shipping packages","Keep signing SDKs updated to OPC-compliant behavior"],"tags":["wpf","packaging","xml-signature","opc"],"backgroundTag":"schema-validation-failed","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}