1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.ws.security.message.token;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.apache.ws.security.WSConstants;
23 import org.apache.ws.security.WSDocInfo;
24 import org.apache.ws.security.WSSConfig;
25 import org.apache.ws.security.WSSecurityException;
26 import org.apache.ws.security.components.crypto.Crypto;
27 import org.apache.ws.security.util.DOM2Writer;
28 import org.apache.ws.security.util.WSSecurityUtil;
29 import org.apache.xml.security.exceptions.XMLSecurityException;
30 import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial;
31 import org.apache.xml.security.utils.Base64;
32 import org.w3c.dom.*;
33
34 import java.security.cert.CertificateEncodingException;
35 import java.security.cert.X509Certificate;
36
37 /***
38 * Security Token Reference.
39 * <p/>
40 *
41 * @author Davanum Srinivas (dims@yahoo.com).
42 */
43 public class SecurityTokenReference {
44 private static Log log =
45 LogFactory.getLog(SecurityTokenReference.class.getName());
46 private static Log tlog = LogFactory.getLog("org.apache.ws.security.TIME");
47 public static final String SECURITY_TOKEN_REFERENCE = "SecurityTokenReference";
48 public static final String KEY_NAME = "KeyName";
49 public static final String SKI_URI =
50 WSConstants.X509TOKEN_NS + "#X509SubjectKeyIdentifier";
51 protected Element element = null;
52 private XMLX509IssuerSerial issuerSerial = null;
53 private byte[] skiBytes = null;
54 protected WSSConfig wssConfig = WSSConfig.getDefaultWSConfig();
55
56 private static boolean doDebug = false;
57
58 /***
59 * Constructor.
60 * <p/>
61 *
62 * @param wssConfig
63 * @param elem
64 * @throws WSSecurityException
65 */
66 public SecurityTokenReference(WSSConfig wssConfig, Element elem) throws WSSecurityException {
67 doDebug = log.isDebugEnabled();
68 this.element = elem;
69 this.wssConfig = wssConfig;
70 boolean goodElement = false;
71 if (SECURITY_TOKEN_REFERENCE.equals(element.getLocalName())) {
72 if (wssConfig.getProcessNonCompliantMessages()) {
73 for (int i = 0; !goodElement && i < WSConstants.WSSE_NS_ARRAY.length; ++i) {
74 goodElement = WSConstants.WSSE_NS_ARRAY[i].equals(element.getNamespaceURI());
75 }
76 } else {
77 goodElement = wssConfig.getWsseNS().equals(element.getNamespaceURI());
78 }
79 } else if (KEY_NAME.equals(element.getLocalName())) {
80 goodElement = WSConstants.SIG_NS.equals(element.getNamespaceURI());
81 }
82 if (!goodElement) {
83 throw new WSSecurityException(WSSecurityException.FAILURE,
84 "badElement",
85 null);
86 }
87 }
88
89 /***
90 * Constructor.
91 * <p/>
92 *
93 * @param wssConfig
94 * @param doc
95 */
96 public SecurityTokenReference(WSSConfig wssConfig, Document doc) {
97 doDebug = log.isDebugEnabled();
98 this.wssConfig = wssConfig;
99 this.element =
100 doc.createElementNS(wssConfig.getWsseNS(),
101 "wsse:SecurityTokenReference");
102 }
103
104
105
106
107
108
109 /***
110 * set the reference.
111 * <p/>
112 *
113 * @param ref
114 */
115 public void setReference(Reference ref) {
116 Element elem = getFirstElement();
117 if (elem != null) {
118 this.element.replaceChild(ref.getElement(), elem);
119 } else {
120 this.element.appendChild(ref.getElement());
121 }
122 }
123
124 /***
125 * Gets the Reference.
126 *
127 * @return the <code>Reference</code> element contained in this
128 * SecurityTokeneReference
129 * @throws WSSecurityException
130 */
131 public Reference getReference() throws WSSecurityException {
132 Element elem = getFirstElement();
133 return new Reference(wssConfig, elem);
134 }
135
136 /***
137 * Gets the signing token element, which maybe a <code>BinarySecurityToken
138 * </code> or a SAML token .
139 * The method gets the URI attribute of the {@link Reference} contained in
140 * the {@link SecurityTokenReference} and tries to find the referenced
141 * Element in the document.
142 *
143 * @param doc the document that contains the binary security token
144 * element. This could be different from the document
145 * that contains the SecurityTokenReference (STR). See
146 * STRTransform.derefenceBST() method
147 * @return Element containing the signing token, must be a BinarySecurityToken
148 * @throws WSSecurityException When either no <code>Reference</code> element, or the found
149 * reference contains no URI, or the referenced signing not found.
150 */
151 public Element getTokenElement(Document doc, WSDocInfo docInfo)
152 throws WSSecurityException {
153 Reference ref = getReference();
154 String uri = ref.getURI();
155 if (doDebug) {
156 log.debug("Token reference uri: " + uri);
157 }
158 if (uri == null) {
159 throw new WSSecurityException(WSSecurityException.INVALID_SECURITY,
160 "badReferenceURI");
161 }
162 Element tokElement = null;
163 String tmpS = WSConstants.WSS_SAML_NS + WSConstants.WSS_SAML_ASSERTION;
164 if (tmpS.equals(ref.getValueType())) {
165 Element sa = docInfo.getAssertion();
166 String saID = null;
167 if (sa != null) {
168 saID = sa.getAttribute("AssertionID");
169 }
170 if (doDebug) {
171 log.debug("SAML token ID: " + saID);
172 }
173 String id = uri.substring(1);
174 if (saID == null || !saID.equals(id)) {
175 throw new WSSecurityException(WSSecurityException.INVALID_SECURITY,
176 "badReferenceURI",
177 new Object[]{"uri:" + uri + ", saID: " + saID});
178 }
179 tokElement = sa;
180 } else {
181 tokElement = WSSecurityUtil.getElementByWsuId(wssConfig, doc, uri);
182 }
183 if (tokElement == null) {
184 throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
185 "noToken",
186 new Object[]{uri});
187 }
188 return tokElement;
189 }
190
191
192
193
194
195
196 /***
197 * Sets the KeyIdentifer Element as a X509 certificate.
198 * Takes a X509 certificate, converts its data into base 64 and inserts
199 * it into a <code>wsse:KeyIdentifier</code> element, which is placed
200 * in the <code>wsse:SecurityTokenReference</code> element.
201 *
202 * @param cert is the X509 certficate to be inserted as key identifier
203 */
204 public void setKeyIdentifier(X509Certificate cert)
205 throws WSSecurityException {
206 Document doc = this.element.getOwnerDocument();
207 byte data[] = null;
208 try {
209 data = cert.getEncoded();
210 } catch (CertificateEncodingException e) {
211 throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
212 "encodeError");
213 }
214 Text certText = doc.createTextNode(Base64.encode(data));
215 Element keyId =
216 doc.createElementNS(wssConfig.getWsseNS(), "wsse:KeyIdentifier");
217 if (wssConfig.isBSTAttributesQualified()) {
218 keyId.setAttributeNS(wssConfig.getWsseNS(),
219 WSConstants.WSSE_PREFIX + ":ValueType",
220 X509Security.getType(wssConfig));
221 keyId.setAttributeNS(wssConfig.getWsseNS(),
222 WSConstants.WSSE_PREFIX + ":EncodingType",
223 BinarySecurity.getBase64EncodingValue(wssConfig));
224 } else {
225 keyId.setAttributeNS(null, "ValueType", X509Security.getType(wssConfig));
226 keyId.setAttributeNS(null,
227 "EncodingType",
228 BinarySecurity.getBase64EncodingValue(wssConfig));
229 }
230 keyId.appendChild(certText);
231 Element elem = getFirstElement();
232 if (elem != null) {
233 this.element.replaceChild(keyId, elem);
234 } else {
235 this.element.appendChild(keyId);
236 }
237 }
238
239 /***
240 * Sets the KeyIdentifer Element as a X509 Subject-Key-Identifier (SKI).
241 * Takes a X509 certificate, gets it SKI data, converts into base 64 and
242 * inserts it into a <code>wsse:KeyIdentifier</code> element, which is placed
243 * in the <code>wsse:SecurityTokenReference</code> element.
244 *
245 * @param cert is the X509 certficate to get the SKI
246 * @param crypto is the Crypto implementation. Used to read SKI info bytes from certificate
247 */
248 public void setKeyIdentifierSKI(X509Certificate cert, Crypto crypto)
249 throws WSSecurityException {
250 Document doc = this.element.getOwnerDocument();
251 byte data[] = crypto.getSKIBytesFromCert(cert);
252 org.w3c.dom.Text skiText = doc.createTextNode(Base64.encode(data));
253 Element keyId =
254 doc.createElementNS(wssConfig.getWsseNS(), "wsse:KeyIdentifier");
255 if (wssConfig.isBSTAttributesQualified()) {
256 keyId.setAttributeNS(wssConfig.getWsseNS(),
257 WSConstants.WSSE_PREFIX + ":ValueType",
258 SKI_URI);
259 keyId.setAttributeNS(wssConfig.getWsseNS(),
260 WSConstants.WSSE_PREFIX + ":EncodingType",
261 BinarySecurity.getBase64EncodingValue(wssConfig));
262 } else {
263 keyId.setAttributeNS(null, "ValueType", SKI_URI);
264 keyId.setAttributeNS(null,
265 "EncodingType",
266 BinarySecurity.getBase64EncodingValue(wssConfig));
267 }
268 keyId.appendChild(skiText);
269 Element elem = getFirstElement();
270 if (elem != null) {
271 this.element.replaceChild(keyId, elem);
272 } else {
273 this.element.appendChild(keyId);
274 }
275 }
276
277 public void setSAMLKeyIdentifier(String keyIdVal)
278 throws WSSecurityException {
279 Document doc = this.element.getOwnerDocument();
280 Element keyId =
281 doc.createElementNS(wssConfig.getWsseNS(), "wsse:KeyIdentifier");
282 keyId.setAttributeNS(wssConfig.getWsseNS(),
283 "ValueType",
284 "http://docs.oasis-open.org/wss/2004/XX/oasis-2004XX-wss-saml-token-profile-1.0#SAMLAssertionID");
285 keyId.appendChild(doc.createTextNode(keyIdVal));
286 Element elem = getFirstElement();
287 if (elem != null) {
288 this.element.replaceChild(keyId, elem);
289 } else {
290 this.element.appendChild(keyId);
291 }
292 }
293
294 /***
295 * Gets the KeyIdentifer.
296 *
297 * @return the {@link BinarySecurity} containing the X509
298 * certificate or zero if a unknown key identifier
299 * type was detected.
300 */
301 public X509Certificate[] getKeyIdentifier(Crypto crypto)
302 throws WSSecurityException {
303 X509Security token = null;
304 Element elem = getFirstElement();
305 String value = elem.getAttribute("ValueType");
306
307
308 if (value.length() == 0 &&
309 (wssConfig.getProcessNonCompliantMessages() || wssConfig.isBSTAttributesQualified())) {
310 value = WSSecurityUtil.getAttributeValueWSSE(elem, "ValueType", null);
311 }
312 if (value.endsWith(X509Security.X509_V3)) {
313 token = new X509Security(wssConfig, elem);
314 if (token != null) {
315 X509Certificate cert = token.getX509Certificate(crypto);
316 X509Certificate[] certs = new X509Certificate[1];
317 certs[0] = cert;
318 return certs;
319 }
320 } else if (value.equals(SKI_URI)) {
321 String alias = getX509SKIAlias(crypto);
322 if (alias != null) {
323 return crypto.getCertificates(alias);
324 }
325 }
326 return null;
327 }
328
329 public String getX509SKIAlias(Crypto crypto) throws WSSecurityException {
330 if (skiBytes == null) {
331 skiBytes = getSKIBytes();
332 if (skiBytes == null) {
333 return null;
334 }
335 }
336 String alias = crypto.getAliasForX509Cert(skiBytes);
337 if (doDebug) {
338 log.info("X509 SKI alias: " + alias);
339 }
340 return alias;
341 }
342
343 public byte[] getSKIBytes() {
344 if (skiBytes != null) {
345 return skiBytes;
346 }
347 Node node = getFirstElement().getFirstChild();
348 if (node == null) {
349 return null;
350 }
351 if (node.getNodeType() == Node.TEXT_NODE) {
352 try {
353 skiBytes = Base64.decode(((Text) node).getData());
354 } catch (Exception e) {
355 return null;
356 }
357 }
358 return skiBytes;
359 }
360
361
362
363
364
365 /***
366 * Sets the X509 IssuerSerial data.
367 *
368 * @param ref the {@link XMLX509IssuerSerial} to put into this
369 * SecurityTokenReference
370 */
371 public void setX509IssuerSerial(XMLX509IssuerSerial ref) {
372 Element elem = getFirstElement();
373 if (elem != null) {
374 this.element.replaceChild(ref.getElement(), elem);
375 } else {
376 this.element.appendChild(ref.getElement());
377 }
378 }
379
380 /***
381 * Gets the certificate identified with X509 issuerSerial data.
382 * This method first tries to get the embedded certificate.
383 * If this fails it checks if the certificate is in the
384 * keystore.
385 *
386 * @return a certificate array or null if nothing found
387 */
388 public X509Certificate[] getX509IssuerSerial(Crypto crypto)
389 throws WSSecurityException {
390 String alias = getX509IssuerSerialAlias(crypto);
391 if (alias != null) {
392 return crypto.getCertificates(alias);
393 }
394 return null;
395 }
396
397 /***
398 * Gets the alias name of the certificate identified with X509 issuerSerial data.
399 * The keystore identifies the certificate and the key with this alias name.
400 *
401 * @return the alias name for the certificate or null if nothing found
402 */
403 public String getX509IssuerSerialAlias(Crypto crypto)
404 throws WSSecurityException {
405 if (issuerSerial == null) {
406 issuerSerial = getIssuerSerial();
407 if (issuerSerial == null) {
408 return null;
409 }
410 }
411
412 String alias = crypto.getAliasForX509Cert(issuerSerial.getIssuerName(),
413 issuerSerial.getSerialNumber());
414
415 if (doDebug) {
416 log.info("X509IssuerSerial alias: " + alias);
417 }
418 return alias;
419 }
420
421 private XMLX509IssuerSerial getIssuerSerial() throws WSSecurityException {
422 if (issuerSerial != null) {
423 return issuerSerial;
424 }
425 Element elem = getFirstElement();
426 if (elem == null) {
427 return null;
428 }
429 try {
430 issuerSerial = new XMLX509IssuerSerial(elem, "");
431 } catch (XMLSecurityException e) {
432 throw new WSSecurityException(WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
433 "noToken",
434 new Object[]{"Issuer/Serial data element missing"});
435 }
436 return issuerSerial;
437 }
438
439
440
441
442
443 /***
444 * get the first child element.
445 *
446 * @return the first <code>Element</code> child node
447 */
448 public Element getFirstElement() {
449 for (Node currentChild = this.element.getFirstChild();
450 currentChild != null;
451 currentChild = currentChild.getNextSibling()) {
452 if (currentChild instanceof Element) {
453 return (Element) currentChild;
454 }
455 }
456 return null;
457 }
458
459 /***
460 * Method containsKeyName
461 *
462 * @return true if the <code>SecurtityTokenReference</code> contains
463 * a <code>wsse:KeyName</code> element
464 */
465 public boolean containsKeyName() {
466 return element.getLocalName().equals(KEY_NAME);
467 }
468
469 public String getKeyNameValue() {
470 return element.getFirstChild().getNodeValue();
471 }
472
473 /***
474 * Method containsReference
475 *
476 * @return true if the <code>SecurtityTokenReference</code> contains
477 * a <code>wsse:Reference</code> element
478 */
479 public boolean containsReference() {
480 return this.lengthReference() > 0;
481 }
482
483 /***
484 * Method lengthReference.
485 *
486 * @return number of <code>wsse:Reference</code> elements in
487 * the <code>SecurtityTokenReference</code>
488 */
489 public int lengthReference() {
490 if (wssConfig.getProcessNonCompliantMessages()) {
491 int length = 0;
492 for (int i = 0; length == 0 && i < WSConstants.WSSE_NS_ARRAY.length; ++i) {
493 length = this.length(WSConstants.WSSE_NS_ARRAY[i], "Reference");
494 }
495 return length;
496 } else {
497 return this.length(wssConfig.getWsseNS(), "Reference");
498 }
499 }
500
501 /***
502 * Method containsX509IssuerSerial
503 *
504 * @return true if the <code>SecurtityTokenReference</code> contains
505 * a <code>ds:IssuerSerial</code> element
506 */
507 public boolean containsX509IssuerSerial() {
508 return this.lengthX509IssuerSerial() > 0;
509 }
510
511 /***
512 * Method lengthX509IssuerSerial.
513 *
514 * @return number of <code>ds:IssuerSerial</code> elements in
515 * the <code>SecurtityTokenReference</code>
516 */
517 public int lengthX509IssuerSerial() {
518 return this.length(WSConstants.SIG_NS, "X509IssuerSerial");
519 }
520
521 /***
522 * Method containsKeyIdentifier.
523 *
524 * @return true if the <code>SecurtityTokenReference</code> contains
525 * a <code>wsse:KeyIdentifier</code> element
526 */
527 public boolean containsKeyIdentifier() {
528 return this.lengthKeyIdentifier() > 0;
529 }
530
531 /***
532 * Method lengthKeyIdentifier.
533 *
534 * @return number of <code>wsse:KeyIdentifier</code> elements in
535 * the <code>SecurtityTokenReference</code>
536 */
537 public int lengthKeyIdentifier() {
538 if (wssConfig.getProcessNonCompliantMessages()) {
539 for (int i = 0; i < WSConstants.WSSE_NS_ARRAY.length; ++i) {
540 int len = this.length(WSConstants.WSSE_NS_ARRAY[i], "KeyIdentifier");
541 if (len > 0) {
542 return len;
543 }
544 }
545 } else {
546 return this.length(wssConfig.getWsseNS(), "KeyIdentifier");
547 }
548 return 0;
549 }
550
551 /***
552 * Method length.
553 *
554 * @param namespace
555 * @param localname
556 * @return number of elements with matching localname and namespace
557 */
558 public int length(String namespace, String localname) {
559 NodeList childNodes = this.element.getChildNodes();
560 int maxLength = childNodes.getLength();
561 int result = 0;
562 for (int i = 0; i < maxLength; i++) {
563 Node n = childNodes.item(i);
564 if (n.getNodeType() == Node.ELEMENT_NODE) {
565 String ns = n.getNamespaceURI();
566 String name = n.getLocalName();
567 if (((namespace != null)
568 && (ns != null)
569 && namespace.equals(ns))
570 || ((namespace == null) && (ns == null))) {
571 if (localname.equals(name)) {
572 result++;
573 }
574 }
575 }
576 }
577 return result;
578 }
579
580 /***
581 * get the dom element.
582 * <p/>
583 *
584 * @return
585 */
586 public Element getElement() {
587 return this.element;
588 }
589
590 /***
591 * set the id.
592 * <p/>
593 *
594 * @param id
595 */
596 public void setID(String id) {
597 String prefix =
598 WSSecurityUtil.setNamespace(this.element,
599 wssConfig.getWsuNS(),
600 WSConstants.WSU_PREFIX);
601 this.element.setAttributeNS(wssConfig.getWsuNS(), prefix + ":Id", id);
602 }
603
604 /***
605 * return the string representation.
606 * <p/>
607 *
608 * @return
609 */
610 public String toString() {
611 return DOM2Writer.nodeToString((Node) this.element);
612 }
613 }