99use SimpleSAML \{Configuration , Error , Logger };
1010use SimpleSAML \HTTP \RunnableResponse ;
1111use SimpleSAML \Metadata \MetaDataStorageHandler ;
12- use SimpleSAML \Module \saml \Message ;;
1312use SimpleSAML \SAML2 \Binding ;
1413use SimpleSAML \SAML2 \Binding \HTTPPost ;
1514use SimpleSAML \SAML2 \Constants as C ;
16- use SimpleSAML \SAML2 \Utils ;
15+ use SimpleSAML \SAML2 \Utils as SAML2_Utils ;
1716use SimpleSAML \SAML2 \XML \saml \{
1817 Assertion ,
1918 Attribute ,
2322 AudienceRestriction ,
2423 Conditions ,
2524 Issuer ,
26- Status ,
27- StatusCode ,
2825 Subject ,
2926 SubjectConfirmation ,
3027 SubjectConfirmationData ,
3128};
32- use SimpleSAML \SAML2 \XML \samlp \{AttributeQuery , Response };
29+ use SimpleSAML \SAML2 \XML \samlp \{AttributeQuery , Response , Status , StatusCode };
30+ use SimpleSAML \Utils ;
3331use SimpleSAML \XML \Utils \Random ;
32+ use SimpleSAML \XMLSecurity \Alg \Signature \SignatureAlgorithmFactory ;
33+ use SimpleSAML \XMLSecurity \CryptoEncoding \PEM ;
34+ use SimpleSAML \XMLSecurity \Key \PrivateKey ;
35+ use SimpleSAML \XMLSecurity \XML \ds \{KeyInfo , X509Certificate , X509Data };
36+ use SimpleSAML \XMLSecurity \XML \SignableElementInterface ;
3437use Symfony \Bridge \PsrHttpMessage \Factory \{HttpFoundationFactory , PsrHttpFactory };
3538use Symfony \Component \HttpFoundation \Request ;
3639
40+ use function array_map ;
41+
3742/**
3843 * Controller class for the exampleattributeserver module.
3944 *
@@ -118,7 +123,7 @@ public function main(/** @scrutinizer ignore-unused */ Request $request): Runnab
118123 new AttributeValue ('value1 ' ),
119124 new AttributeValue ('value2 ' ),
120125 new AttributeValue ('value3 ' ),
121- ]
126+ ],
122127 ),
123128 new Attribute (
124129 'test ' ,
@@ -131,21 +136,33 @@ public function main(/** @scrutinizer ignore-unused */ Request $request): Runnab
131136 ];
132137
133138 // Determine which attributes we will return
134- $ returnAttributes = [];
139+ $ returnAttributes = array_map (
140+ function ($ x ) {
141+ return $ x ->getName ();
142+ },
143+ $ attributes ,
144+ );
135145
136146 if (count ($ returnAttributes ) === 0 ) {
137147 Logger::debug ('No attributes requested - return all attributes. ' );
138148 $ returnAttributes = $ attributes ;
139149 } else {
140150 foreach ($ message ->getAttributes () as $ reqAttr ) {
141151 foreach ($ attributes as $ attr ) {
142- if ($ attr ->getName () === $ reqAttr ->getName () && $ attr ->getNameFormat () === $ reqAttr ->getNameFormat ()) {
152+ if (
153+ $ attr ->getName () === $ reqAttr ->getName ()
154+ && $ attr ->getNameFormat () === $ reqAttr ->getNameFormat ()
155+ ) {
143156 // The requested attribute is available
144157 if ($ reqAttr ->getAttributeValues () === []) {
145158 // If no specific values are requested, return all
146159 $ returnAttributes [] = $ attr ;
147160 } else {
148- $ returnValues = $ this ->filterAttributeValues ($ reqAttr ->getAttributeValues (), $ attr ->getAttributeValues ());
161+ $ returnValues = $ this ->filterAttributeValues (
162+ $ reqAttr ->getAttributeValues (),
163+ $ attr ->getAttributeValues (),
164+ );
165+
149166 $ returnAttributes [] = new Attribute (
150167 $ attr ->getName (),
151168 $ attr ->getNameFormat (),
@@ -159,7 +176,7 @@ public function main(/** @scrutinizer ignore-unused */ Request $request): Runnab
159176 }
160177
161178 // $returnAttributes contains the attributes we should return. Send them
162- $ clock = Utils ::getContainer ()->getClock ();
179+ $ clock = SAML2_Utils ::getContainer ()->getClock ();
163180
164181 $ assertion = new Assertion (
165182 issuer: new Issuer ($ idpEntityId ),
@@ -192,24 +209,22 @@ public function main(/** @scrutinizer ignore-unused */ Request $request): Runnab
192209 ],
193210 );
194211
195- // TODO: Fix signing; should use xml-security lib
196- Message::addSign ($ idpMetadata , $ spMetadata , $ assertion );
212+ self ::addSign ($ idpMetadata , $ spMetadata , $ assertion );
197213
198214 $ response = new Response (
199215 status: new Status (
200216 new StatusCode (C::STATUS_SUCCESS ),
201217 ),
202218 issueInstant: $ clock ->now (),
203- issuer: new Issuer ( $ issuer) ,
219+ issuer: $ issuer ,
204220 id: (new Random ())->generateID (),
205221 version: '2.0 ' ,
206222 inResponseTo: $ message ->getId (),
207223 destination: $ endpoint ,
208224 assertions: [$ assertion ],
209225 );
210226
211- // TODO: Fix signing; should use xml-security lib
212- Message::addSign ($ idpMetadata , $ spMetadata , $ response );
227+ self ::addSign ($ idpMetadata , $ spMetadata , $ response );
213228
214229 $ httpPost = new HTTPPost ();
215230 $ httpPost ->setRelayState ($ binding ->getRelayState ());
@@ -238,4 +253,60 @@ private function filterAttributeValues(array $reqValues, array $values): array
238253
239254 return $ result ;
240255 }
256+
257+
258+ /**
259+ * @deprecated This method is a modified version of \SimpleSAML\Module\saml\Message::addSign and
260+ * should be replaced with a call to a future ServiceProvider-class in the saml2-library
261+ *
262+ * Add signature key and sender certificate to an element (Message or Assertion).
263+ *
264+ * @param \SimpleSAML\Configuration $srcMetadata The metadata of the sender.
265+ * @param \SimpleSAML\Configuration $dstMetadata The metadata of the recipient.
266+ * @param \SimpleSAML\XMLSecurity\XML\SignableElementInterface $element The element we should add the data to.
267+ */
268+ private static function addSign (
269+ Configuration $ srcMetadata ,
270+ Configuration $ dstMetadata ,
271+ SignableElementInterface &$ element ,
272+ ): void {
273+ $ dstPrivateKey = $ dstMetadata ->getOptionalString ('signature.privatekey ' , null );
274+ $ cryptoUtils = new Utils \Crypto ();
275+
276+ if ($ dstPrivateKey !== null ) {
277+ /** @var array $keyArray */
278+ $ keyArray = $ cryptoUtils ->loadPrivateKey ($ dstMetadata , true , 'signature. ' );
279+ $ certArray = $ cryptoUtils ->loadPublicKey ($ dstMetadata , false , 'signature. ' );
280+ } else {
281+ /** @var array $keyArray */
282+ $ keyArray = $ cryptoUtils ->loadPrivateKey ($ srcMetadata , true );
283+ $ certArray = $ cryptoUtils ->loadPublicKey ($ srcMetadata , false );
284+ }
285+
286+ $ algo = $ dstMetadata ->getOptionalString ('signature.algorithm ' , null );
287+ if ($ algo === null ) {
288+ $ algo = $ srcMetadata ->getOptionalString ('signature.algorithm ' , C::SIG_RSA_SHA256 );
289+ }
290+
291+ $ privateKey = PrivateKey::fromFile ($ keyArray ['PEM ' ], $ keyArray ['password ' ]);
292+
293+ $ keyInfo = null ;
294+ if ($ certArray !== null ) {
295+ $ certificate = new X509Certificate (PEM ::fromString ($ keyArray ['PEM ' ]));
296+ $ keyInfo = new KeyInfo ([
297+ new X509Data (
298+ [
299+ new X509Certificate ($ certArray ['PEM ' ]),
300+ ],
301+ ),
302+ ]);
303+ }
304+
305+ $ signer = (new SignatureAlgorithmFactory ())->getAlgorithm (
306+ $ algo ,
307+ $ privateKey ,
308+ );
309+
310+ $ element ->sign ($ signer , C::C14N_EXCLUSIVE_WITHOUT_COMMENTS , $ keyInfo );
311+ }
241312}
0 commit comments