1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
<?php
/**
* Helper class for processing signed elements.
*
* Can either be inherited from, or can be used by proxy.
*
* @package simpleSAMLphp
* @version $Id$
*/
class SAML2_SignedElementHelper implements SAML2_SignedElement {
/**
* The private key we should use to sign the message.
*
* The private key can be NULL, in which case the message is sent unsigned.
*
* @var XMLSecurityKey|NULL
*/
private $signatureKey;
/**
* List of certificates that should be included in the message.
*
* @var array
*/
private $certificates;
/**
* Available methods for validating this message.
*
* @var array
*/
private $validators;
/**
* Initialize the helper class.
*
* @param DOMElement|NULL $xml The XML element which may be signed.
*/
protected function __construct(DOMElement $xml = NULL) {
$this->certificates = array();
$this->validators = array();
if ($xml === NULL) {
return;
}
/* Validate the signature element of the message. */
try {
$sig = SAML2_Utils::validateElement($xml);
if ($sig !== FALSE) {
$this->certificates = $sig['Certificates'];
$this->validators[] = array(
'Function' => array('SAML2_Utils', 'validateSignature'),
'Data' => $sig,
);
}
} catch (Exception $e) {
/* Ignore signature validation errors. */
}
}
/**
* Add a method for validating this element.
*
* This function is used for custom validation extensions
*
* @param callback $function The function which should be called.
* @param mixed $data The data that should be included as the first parameter to the function.
*/
public function addValidator($function, $data) {
assert('is_callable($function)');
$this->validators[] = array(
'Function' => $function,
'Data' => $data,
);
}
/**
* Validate this element against a public key.
*
* TRUE is returned on success, FALSE is returned if we don't have any
* signature we can validate. An exception is thrown if the signature
* validation fails.
*
* @param XMLSecurityKey $key The key we should check against.
* @return boolean TRUE on success, FALSE when we don't have a signature.
*/
public function validate(XMLSecurityKey $key) {
if (count($this->validators) === 0) {
return FALSE;
}
$exceptions = array();
foreach ($this->validators as $validator) {
$function = $validator['Function'];
$data = $validator['Data'];
try {
call_user_func($function, $data, $key);
/* We were able to validate the message with this validator. */
return TRUE;
} catch (Exception $e) {
$exceptions[] = $e;
}
}
/* No validators were able to validate the message. */
throw $exceptions[0];
}
/**
* Retrieve the private key we should use to sign the message.
*
* @return XMLSecurityKey|NULL The key, or NULL if no key is specified.
*/
public function getSignatureKey() {
return $this->signatureKey;
}
/**
* Set the private key we should use to sign the message.
*
* If the key is NULL, the message will be sent unsigned.
*
* @param XMLSecurityKey|NULL $key
*/
public function setSignatureKey(XMLsecurityKey $signatureKey = NULL) {
$this->signatureKey = $signatureKey;
}
/**
* Set the certificates that should be included in the message.
*
* The certificates should be strings with the PEM encoded data.
*
* @param array $certificates An array of certificates.
*/
public function setCertificates(array $certificates) {
$this->certificates = $certificates;
}
/**
* Retrieve the certificates that are included in the message.
*
* @return array An array of certificates.
*/
public function getCertificates() {
return $this->certificates;
}
/**
* Retrieve certificates that sign this element.
*
* @return array Array with certificates.
*/
public function getValidatingCertificates() {
$ret = array();
foreach ($this->certificates as $cert) {
/* We have found a matching fingerprint. */
$pemCert = "-----BEGIN CERTIFICATE-----\n" .
chunk_split($cert, 64) .
"-----END CERTIFICATE-----\n";
/* Extract the public key from the certificate for validation. */
$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA1, array('type'=>'public'));
$key->loadKey($pemCert);
try {
/* Check the signature. */
if ($this->validate($key)) {
$ret[] = $cert;
}
} catch (Exception $e) {
/* This certificate does not sign this element. */
}
}
return $ret;
}
/**
* Sign the given XML element.
*
* @param DOMElement $root The element we should sign.
* @param DOMElement|NULL $insertBefore The element we should insert the signature node before.
*/
protected function signElement(DOMElement $root, DOMElement $insertBefore = NULL) {
if ($this->signatureKey === NULL) {
/* We cannot sign this element. */
return;
}
SAML2_Utils::insertSignature($this->signatureKey, $this->certificates, $root, $insertBefore);
return $root;
}
}
|