55namespace SimpleSAML \XMLSecurity \XML ;
66
77use DOMElement ;
8- use DOMNode ;
98use SimpleSAML \Assert \Assert ;
9+ use SimpleSAML \XML \DOMDocumentFactory ;
1010use SimpleSAML \XMLSecurity \Alg \SignatureAlgorithm ;
1111use SimpleSAML \XMLSecurity \Constants as C ;
1212use SimpleSAML \XMLSecurity \Exception \InvalidArgumentException ;
@@ -89,10 +89,69 @@ public function sign(
8989
9090
9191 /**
92- * @param \DOMElement $xml
93- * @throws \Exception
92+ * Get a ds:Reference pointing to this object.
93+ *
94+ * @param string $digestAlg The digest algorithm to use.
95+ * @param \SimpleSAML\XMLSecurity\XML\ds\Transforms $transforms The transforms to apply to the object.
9496 */
95- private function doSign (DOMElement $ xml ): void
97+ private function getReference (
98+ string $ digestAlg ,
99+ Transforms $ transforms ,
100+ DOMElement $ xml ,
101+ string $ canonicalDocument
102+ ): Reference {
103+ $ id = $ this ->getId ();
104+ $ uri = null ;
105+ if (empty ($ id )) { // document reference
106+ Assert::notNull (
107+ $ xml ->ownerDocument ->documentElement ,
108+ 'Cannot create a document reference without a root element in the document. ' ,
109+ RuntimeException::class
110+ );
111+ Assert::true (
112+ $ xml ->isSameNode ($ xml ->ownerDocument ->documentElement ),
113+ 'Cannot create a document reference when signing an object that is not the root of the document. ' .
114+ 'Please give your object an identifier. ' ,
115+ RuntimeException::class
116+ );
117+ if (in_array ($ this ->c14nAlg , [C::C14N_INCLUSIVE_WITH_COMMENTS , C::C14N_EXCLUSIVE_WITH_COMMENTS ])) {
118+ $ uri = '#xpointer(/) ' ;
119+ }
120+ } elseif (in_array ($ this ->c14nAlg , [C::C14N_INCLUSIVE_WITH_COMMENTS , C::C14N_EXCLUSIVE_WITH_COMMENTS ])) {
121+ // regular reference, but must retain comments
122+ $ uri = '#xpointer(id( ' . $ id . ')) ' ;
123+ } else { // regular reference, can ignore comments
124+ $ uri = '# ' . $ id ;
125+ }
126+
127+ return new Reference (
128+ new DigestMethod ($ digestAlg ),
129+ new DigestValue (Security::hash ($ digestAlg , $ canonicalDocument )),
130+ $ transforms ,
131+ null ,
132+ null ,
133+ $ uri
134+ );
135+ }
136+
137+
138+ /**
139+ * Do the actual signing of the document.
140+ *
141+ * Note that this method does not insert the signature in the returned \DOMElement. The signature will be available
142+ * in $this->signature as a \SimpleSAML\XMLSecurity\XML\ds\Signature object, which can then be converted to XML
143+ * calling toXML() on it, passing the \DOMElement value returned here as a parameter. The resulting \DOMElement
144+ * can then be inserted in the position desired.
145+ *
146+ * E.g.:
147+ * $xml = // our XML to sign
148+ * $signedXML = $this->doSign($xml);
149+ * $signedXML->appendChild($this->signature->toXML($signedXML));
150+ *
151+ * @param \DOMElement $xml The element to sign.
152+ * @return \DOMElement The signed element, without the signature attached to it just yet.
153+ */
154+ protected function doSign (DOMElement $ xml ): DOMElement
96155 {
97156 Assert::notNull (
98157 $ this ->signer ,
@@ -108,38 +167,18 @@ private function doSign(DOMElement $xml): void
108167 new Transform ($ this ->c14nAlg )
109168 ]);
110169
111- $ refId = $ this ->getId ();
112- $ reference = new Reference (
113- new DigestMethod ($ digest ),
114- new DigestValue (Security::hash ($ digest , XML ::processTransforms ($ transforms , $ xml ))),
115- $ transforms ,
116- null ,
117- null ,
118- ($ refId !== null ) ? '# ' . $ refId : null
119- );
170+ $ canonicalDocument = XML ::processTransforms ($ transforms , $ xml );
120171
121172 $ signedInfo = new SignedInfo (
122173 new CanonicalizationMethod ($ this ->c14nAlg ),
123174 new SignatureMethod ($ algorithm ),
124- [$ reference ]
175+ [$ this -> getReference ( $ digest , $ transforms , $ xml , $ canonicalDocument ) ]
125176 );
126177
127178 $ signingData = $ signedInfo ->canonicalize ($ this ->c14nAlg );
128179 $ signedData = base64_encode ($ this ->signer ->sign ($ signingData ));
129180
130181 $ this ->signature = new Signature ($ signedInfo , new SignatureValue ($ signedData ), $ this ->keyInfo );
131- }
132-
133-
134- /**
135- * @param DOMElement $root
136- * @param DOMNode $node
137- * @param DOMElement $signature
138- * @return DOMElement
139- */
140- private function insertBefore (DOMElement $ root , DOMNode $ node , DOMElement $ signature ): DOMElement
141- {
142- $ root ->removeChild ($ signature );
143- return $ root ->insertBefore ($ signature , $ node );
182+ return DOMDocumentFactory::fromString ($ canonicalDocument )->documentElement ;
144183 }
145184}
0 commit comments