1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.ws.security.saml;
21
22 import org.apache.ws.security.WSConstants;
23 import org.apache.ws.security.WSDerivedKeyTokenPrincipal;
24 import org.apache.ws.security.WSDocInfo;
25 import org.apache.ws.security.WSPasswordCallback;
26 import org.apache.ws.security.WSSecurityEngine;
27 import org.apache.ws.security.WSSecurityEngineResult;
28 import org.apache.ws.security.WSSecurityException;
29 import org.apache.ws.security.components.crypto.AlgorithmSuite;
30 import org.apache.ws.security.components.crypto.AlgorithmSuiteValidator;
31 import org.apache.ws.security.components.crypto.CryptoType;
32 import org.apache.ws.security.handler.RequestData;
33 import org.apache.ws.security.message.token.SecurityTokenReference;
34 import org.apache.ws.security.processor.EncryptedKeyProcessor;
35 import org.apache.ws.security.processor.Processor;
36 import org.apache.ws.security.saml.ext.AssertionWrapper;
37 import org.apache.ws.security.str.STRParser;
38 import org.apache.ws.security.str.SignatureSTRParser;
39 import org.apache.ws.security.util.Base64;
40 import org.apache.ws.security.util.WSSecurityUtil;
41
42 import org.opensaml.saml2.core.SubjectConfirmationData;
43 import org.w3c.dom.Element;
44 import org.w3c.dom.Node;
45 import org.w3c.dom.Text;
46
47 import javax.security.auth.callback.Callback;
48 import javax.security.auth.callback.CallbackHandler;
49 import javax.xml.crypto.XMLStructure;
50 import javax.xml.crypto.dom.DOMStructure;
51 import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
52 import javax.xml.crypto.dsig.keyinfo.KeyValue;
53 import javax.xml.crypto.dsig.keyinfo.X509Data;
54 import javax.xml.crypto.dsig.keyinfo.X509IssuerSerial;
55 import javax.xml.namespace.QName;
56
57 import java.security.NoSuchProviderException;
58 import java.security.Principal;
59 import java.security.PublicKey;
60 import java.security.cert.X509Certificate;
61 import java.util.HashMap;
62 import java.util.List;
63
64
65
66
67 public final class SAMLUtil {
68
69 private static final QName BINARY_SECRET =
70 new QName(WSConstants.WST_NS, "BinarySecret");
71 private static final QName BINARY_SECRET_05_12 =
72 new QName(WSConstants.WST_NS_05_12, "BinarySecret");
73
74 private SAMLUtil() {
75
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89 public static AssertionWrapper getAssertionFromKeyIdentifier(
90 SecurityTokenReference secRef,
91 Element strElement,
92 RequestData request,
93 WSDocInfo wsDocInfo
94 ) throws WSSecurityException {
95 String keyIdentifierValue = secRef.getKeyIdentifierValue();
96 String type = secRef.getKeyIdentifierValueType();
97 WSSecurityEngineResult result = wsDocInfo.getResult(keyIdentifierValue);
98
99 AssertionWrapper assertion = null;
100 Element token = null;
101 if (result != null) {
102 assertion =
103 (AssertionWrapper)result.get(WSSecurityEngineResult.TAG_SAML_ASSERTION);
104 return assertion;
105 } else {
106 token =
107 secRef.findProcessedTokenElement(
108 strElement.getOwnerDocument(), wsDocInfo,
109 request.getCallbackHandler(),
110 keyIdentifierValue, type
111 );
112 if (token != null) {
113 if (!"Assertion".equals(token.getLocalName())) {
114 throw new WSSecurityException(
115 WSSecurityException.FAILURE, "invalidSAMLsecurity"
116 );
117 }
118 return new AssertionWrapper(token);
119 }
120 token =
121 secRef.findUnprocessedTokenElement(
122 strElement.getOwnerDocument(), wsDocInfo,
123 request.getCallbackHandler(), keyIdentifierValue, type
124 );
125
126 if (token == null || !"Assertion".equals(token.getLocalName())) {
127 throw new WSSecurityException(
128 WSSecurityException.FAILURE, "invalidSAMLsecurity"
129 );
130 }
131 Processor proc = request.getWssConfig().getProcessor(WSSecurityEngine.SAML_TOKEN);
132 List<WSSecurityEngineResult> samlResult =
133 proc.handleToken(token, request, wsDocInfo);
134 return
135 (AssertionWrapper)samlResult.get(0).get(
136 WSSecurityEngineResult.TAG_SAML_ASSERTION
137 );
138 }
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 public static SAMLKeyInfo getCredentialFromSubject(
153 AssertionWrapper assertion,
154 RequestData data,
155 WSDocInfo docInfo,
156 boolean bspCompliant
157 ) throws WSSecurityException {
158 if (assertion.getSaml1() != null) {
159 return getCredentialFromSubject(assertion.getSaml1(), data, docInfo, bspCompliant);
160 } else {
161 return getCredentialFromSubject(assertion.getSaml2(), data, docInfo, bspCompliant);
162 }
163 }
164
165
166
167
168
169
170
171 private static byte[] getSecretKeyFromCallbackHandler(
172 String id,
173 CallbackHandler cb
174 ) throws WSSecurityException {
175 if (cb != null) {
176 WSPasswordCallback pwcb =
177 new WSPasswordCallback(id, WSPasswordCallback.SECRET_KEY);
178 try {
179 cb.handle(new Callback[]{pwcb});
180 } catch (Exception e1) {
181 throw new WSSecurityException(WSSecurityException.FAILURE, "noKey",
182 new Object[] { id }, e1);
183 }
184 return pwcb.getKey();
185 }
186 return null;
187 }
188
189
190
191
192
193
194
195
196
197
198
199 public static SAMLKeyInfo getCredentialFromSubject(
200 org.opensaml.saml1.core.Assertion assertion,
201 RequestData data,
202 WSDocInfo docInfo,
203 boolean bspCompliant
204 ) throws WSSecurityException {
205
206 byte[] key = getSecretKeyFromCallbackHandler(assertion.getID(), data.getCallbackHandler());
207 if (key != null && key.length > 0) {
208 return new SAMLKeyInfo(key);
209 }
210
211 for (org.opensaml.saml1.core.Statement stmt : assertion.getStatements()) {
212 org.opensaml.saml1.core.Subject samlSubject = null;
213 if (stmt instanceof org.opensaml.saml1.core.AttributeStatement) {
214 org.opensaml.saml1.core.AttributeStatement attrStmt =
215 (org.opensaml.saml1.core.AttributeStatement) stmt;
216 samlSubject = attrStmt.getSubject();
217 } else if (stmt instanceof org.opensaml.saml1.core.AuthenticationStatement) {
218 org.opensaml.saml1.core.AuthenticationStatement authStmt =
219 (org.opensaml.saml1.core.AuthenticationStatement) stmt;
220 samlSubject = authStmt.getSubject();
221 } else {
222 org.opensaml.saml1.core.AuthorizationDecisionStatement authzStmt =
223 (org.opensaml.saml1.core.AuthorizationDecisionStatement)stmt;
224 samlSubject = authzStmt.getSubject();
225 }
226
227 if (samlSubject == null) {
228 throw new WSSecurityException(
229 WSSecurityException.FAILURE, "invalidSAMLToken",
230 new Object[] {"for Signature (no Subject)"}
231 );
232 }
233
234 Element sub = samlSubject.getSubjectConfirmation().getDOM();
235 Element keyInfoElement =
236 WSSecurityUtil.getDirectChildElement(sub, "KeyInfo", WSConstants.SIG_NS);
237 if (keyInfoElement != null) {
238 return getCredentialFromKeyInfo(keyInfoElement, data, docInfo, bspCompliant);
239 }
240 }
241
242 return null;
243 }
244
245
246
247
248
249
250
251
252
253
254
255 public static SAMLKeyInfo getCredentialFromSubject(
256 org.opensaml.saml2.core.Assertion assertion,
257 RequestData data,
258 WSDocInfo docInfo,
259 boolean bspCompliant
260 ) throws WSSecurityException {
261
262 byte[] key = getSecretKeyFromCallbackHandler(assertion.getID(), data.getCallbackHandler());
263 if (key != null && key.length > 0) {
264 return new SAMLKeyInfo(key);
265 }
266
267 org.opensaml.saml2.core.Subject samlSubject = assertion.getSubject();
268 if (samlSubject == null) {
269 throw new WSSecurityException(
270 WSSecurityException.FAILURE, "invalidSAMLToken",
271 new Object[]{"for Signature (no Subject)"}
272 );
273 }
274 List<org.opensaml.saml2.core.SubjectConfirmation> subjectConfList =
275 samlSubject.getSubjectConfirmations();
276 for (org.opensaml.saml2.core.SubjectConfirmation subjectConfirmation : subjectConfList) {
277 SubjectConfirmationData subjConfData =
278 subjectConfirmation.getSubjectConfirmationData();
279 Element sub = subjConfData.getDOM();
280 Element keyInfoElement =
281 WSSecurityUtil.getDirectChildElement(sub, "KeyInfo", WSConstants.SIG_NS);
282 if (keyInfoElement != null) {
283 return getCredentialFromKeyInfo(keyInfoElement, data, docInfo, bspCompliant);
284 }
285 }
286
287 return null;
288 }
289
290
291
292
293
294
295
296
297
298
299
300 public static SAMLKeyInfo getCredentialFromKeyInfo(
301 Element keyInfoElement,
302 RequestData data,
303 WSDocInfo docInfo,
304 boolean bspCompliant
305 ) throws WSSecurityException {
306
307
308
309 Node node = keyInfoElement.getFirstChild();
310 while (node != null) {
311 if (Node.ELEMENT_NODE == node.getNodeType()) {
312 QName el = new QName(node.getNamespaceURI(), node.getLocalName());
313 if (el.equals(WSSecurityEngine.ENCRYPTED_KEY)) {
314 EncryptedKeyProcessor proc = new EncryptedKeyProcessor();
315 List<WSSecurityEngineResult> result =
316 proc.handleToken((Element)node, data, docInfo, data.getSamlAlgorithmSuite());
317 byte[] secret =
318 (byte[])result.get(0).get(
319 WSSecurityEngineResult.TAG_SECRET
320 );
321 return new SAMLKeyInfo(secret);
322 } else if (el.equals(BINARY_SECRET) || el.equals(BINARY_SECRET_05_12)) {
323 Text txt = (Text)node.getFirstChild();
324 return new SAMLKeyInfo(Base64.decode(txt.getData()));
325 } else if (SecurityTokenReference.STR_QNAME.equals(el)) {
326 STRParser strParser = new SignatureSTRParser();
327 strParser.parseSecurityTokenReference(
328 (Element)node, data, docInfo, new HashMap<String, Object>()
329 );
330 SAMLKeyInfo samlKeyInfo = new SAMLKeyInfo(strParser.getCertificates());
331 samlKeyInfo.setPublicKey(strParser.getPublicKey());
332 samlKeyInfo.setSecret(strParser.getSecretKey());
333
334 Principal principal = strParser.getPrincipal();
335
336
337 AlgorithmSuite algorithmSuite = data.getSamlAlgorithmSuite();
338 if (algorithmSuite != null && principal instanceof WSDerivedKeyTokenPrincipal) {
339 AlgorithmSuiteValidator algorithmSuiteValidator = new
340 AlgorithmSuiteValidator(algorithmSuite);
341
342 algorithmSuiteValidator.checkDerivedKeyAlgorithm(
343 ((WSDerivedKeyTokenPrincipal)principal).getAlgorithm()
344 );
345 algorithmSuiteValidator.checkSignatureDerivedKeyLength(
346 ((WSDerivedKeyTokenPrincipal)principal).getLength()
347 );
348 }
349
350 return samlKeyInfo;
351 }
352 }
353 node = node.getNextSibling();
354 }
355
356 return getCredentialDirectlyFromKeyInfo(keyInfoElement, data);
357 }
358
359
360
361
362
363
364
365
366
367 public static SAMLKeyInfo getCredentialDirectlyFromKeyInfo(
368 Element keyInfoElement,
369 RequestData data
370 ) throws WSSecurityException {
371
372
373
374
375 X509Certificate[] certs = null;
376 KeyInfoFactory keyInfoFactory = null;
377 try {
378 keyInfoFactory = KeyInfoFactory.getInstance("DOM", "ApacheXMLDSig");
379 } catch (NoSuchProviderException ex) {
380 keyInfoFactory = KeyInfoFactory.getInstance("DOM");
381 }
382 XMLStructure keyInfoStructure = new DOMStructure(keyInfoElement);
383
384 try {
385 javax.xml.crypto.dsig.keyinfo.KeyInfo keyInfo =
386 keyInfoFactory.unmarshalKeyInfo(keyInfoStructure);
387 List<?> list = keyInfo.getContent();
388
389 for (int i = 0; i < list.size(); i++) {
390 XMLStructure xmlStructure = (XMLStructure) list.get(i);
391 if (xmlStructure instanceof KeyValue) {
392 PublicKey publicKey = ((KeyValue)xmlStructure).getPublicKey();
393 return new SAMLKeyInfo(publicKey);
394 } else if (xmlStructure instanceof X509Data) {
395 List<?> x509Data = ((X509Data)xmlStructure).getContent();
396 for (int j = 0; j < x509Data.size(); j++) {
397 Object x509obj = x509Data.get(j);
398 if (x509obj instanceof X509Certificate) {
399 certs = new X509Certificate[1];
400 certs[0] = (X509Certificate)x509obj;
401 return new SAMLKeyInfo(certs);
402 } else if (x509obj instanceof X509IssuerSerial) {
403 if (data.getSigCrypto() == null) {
404 throw new WSSecurityException(
405 WSSecurityException.FAILURE, "noSigCryptoFile"
406 );
407 }
408 CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ISSUER_SERIAL);
409 cryptoType.setIssuerSerial(
410 ((X509IssuerSerial)x509obj).getIssuerName(),
411 ((X509IssuerSerial)x509obj).getSerialNumber()
412 );
413 certs = data.getSigCrypto().getX509Certificates(cryptoType);
414 if (certs == null || certs.length < 1) {
415 throw new WSSecurityException(
416 WSSecurityException.FAILURE, "invalidSAMLsecurity",
417 new Object[]{"cannot get certificate or key"}
418 );
419 }
420 return new SAMLKeyInfo(certs);
421 }
422 }
423 }
424 }
425 } catch (Exception ex) {
426 throw new WSSecurityException(
427 WSSecurityException.FAILURE, "invalidSAMLsecurity",
428 new Object[]{"cannot get certificate or key"}, ex
429 );
430 }
431 return null;
432 }
433
434 }