1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package dk.sosi.seal.transform.internal;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.ws.security.WSConstants;
25 import org.apache.ws.security.WSDocInfo;
26 import org.apache.ws.security.WSPasswordCallback;
27 import org.apache.ws.security.WSSecurityException;
28 import org.apache.ws.security.components.crypto.Crypto;
29 import org.apache.ws.security.message.token.BinarySecurity;
30 import org.apache.ws.security.message.token.Reference;
31 import org.apache.ws.security.message.token.X509Security;
32 import org.apache.ws.security.util.Base64;
33 import org.apache.ws.security.util.DOM2Writer;
34 import org.apache.ws.security.util.WSSecurityUtil;
35 import org.apache.xml.security.exceptions.XMLSecurityException;
36 import org.apache.xml.security.keys.content.X509Data;
37 import org.apache.xml.security.keys.content.x509.XMLX509IssuerSerial;
38 import org.apache.xml.security.utils.Constants;
39 import org.w3c.dom.*;
40
41 import javax.security.auth.callback.Callback;
42 import javax.security.auth.callback.CallbackHandler;
43 import java.security.MessageDigest;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.cert.CertificateEncodingException;
46 import java.security.cert.X509Certificate;
47
48
49
50
51
52
53 public class SecurityTokenReference {
54 private static Log log =
55 LogFactory.getLog(SecurityTokenReference.class.getName());
56 public static final String SECURITY_TOKEN_REFERENCE = "SecurityTokenReference";
57 public static final String KEY_NAME = "KeyName";
58 public static final String SKI_URI =
59 WSConstants.X509TOKEN_NS + "#X509SubjectKeyIdentifier";
60 public static final String THUMB_URI =
61 WSConstants.SOAPMESSAGE_NS11 + "#" + WSConstants.THUMBPRINT;
62 public static final String SAML_ID_URI =
63 WSConstants.SAMLTOKEN_NS + "#" + WSConstants.SAML_ASSERTION_ID;
64 public static final String ENC_KEY_SHA1_URI =
65 WSConstants.SOAPMESSAGE_NS11 + "#" + WSConstants.ENC_KEY_SHA1_URI;
66 protected Element element = null;
67 private XMLX509IssuerSerial issuerSerial = null;
68 private byte[] skiBytes = null;
69 private static boolean doDebug = false;
70
71
72
73
74
75
76
77 public SecurityTokenReference(Element elem) throws WSSecurityException {
78 doDebug = log.isDebugEnabled();
79 this.element = elem;
80 boolean goodElement = false;
81 if (SECURITY_TOKEN_REFERENCE.equals(element.getLocalName())) {
82 goodElement = WSConstants.WSSE_NS.equals(element.getNamespaceURI());
83
84
85 }
86 if (!goodElement) {
87 throw new WSSecurityException(WSSecurityException.FAILURE, "badElement", null);
88 }
89 }
90
91
92
93
94
95
96 public SecurityTokenReference(Document doc) {
97 doDebug = log.isDebugEnabled();
98 this.element =
99 doc.createElementNS(WSConstants.WSSE_NS, "wsse:SecurityTokenReference");
100 WSSecurityUtil.setNamespace(this.element, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX);
101 }
102
103
104
105
106
107
108 public void setReference(Reference ref) {
109 Element elem = getFirstElement();
110 if (elem != null) {
111 this.element.replaceChild(ref.getElement(), elem);
112 } else {
113 this.element.appendChild(ref.getElement());
114 }
115 }
116
117
118
119
120
121
122
123
124 public Reference getReference() throws WSSecurityException {
125 Element elem = getFirstElement();
126 return new Reference(elem);
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145 public Element getTokenElement(Document doc, WSDocInfo docInfo, CallbackHandler cb)
146 throws WSSecurityException {
147 Reference ref = getReference();
148 String uri = ref.getURI();
149 if (doDebug) {
150 log.debug("Token reference uri: " + uri);
151 }
152 if (uri == null) {
153 throw new WSSecurityException(
154 WSSecurityException.INVALID_SECURITY, "badReferenceURI"
155 );
156 }
157
158 Element tokElement = findTokenElement(doc, docInfo, cb, uri, ref.getValueType());
159
160 if (tokElement == null) {
161 throw new WSSecurityException(
162 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
163 "noToken",
164 new Object[]{uri}
165 );
166 }
167 return tokElement;
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 public Element getKeyIdentifierTokenElement(
186 Document doc, WSDocInfo docInfo, CallbackHandler cb
187 ) throws WSSecurityException {
188 String value = getKeyIdentifierValue();
189 String type = getKeyIdentifierValueType();
190 if (doDebug) {
191 log.debug("Token reference uri: " + value);
192 }
193 if (value == null) {
194 throw new WSSecurityException(
195 WSSecurityException.INVALID_SECURITY, "badReferenceURI"
196 );
197 }
198
199 Element tokElement = findTokenElement(doc, docInfo, cb, value, type);
200
201 if (tokElement == null) {
202 throw new WSSecurityException(
203 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
204 "noToken",
205 new Object[]{value}
206 );
207 }
208 return tokElement;
209 }
210
211
212 private Element findTokenElement(
213 Document doc,
214 WSDocInfo docInfo,
215 CallbackHandler cb,
216 String uri,
217 String type
218 ) {
219 Element tokElement = null;
220 String id = uri;
221 if (id.charAt(0) == '#') {
222 id = id.substring(1);
223 }
224
225
226
227
228 String assertionStr = WSConstants.WSS_SAML_NS + WSConstants.ASSERTION_LN;
229 if (WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(type)
230
231 || WSConstants.WSS_SAML2_KI_VALUE_TYPE.equals(type)
232
233 || assertionStr.equals(type)) {
234 Element sa = docInfo.getAssertion();
235 if (sa != null) {
236 String saID = sa.getAttribute("AssertionID");
237 if (doDebug) {
238 log.debug("SAML token ID: " + saID);
239 }
240 if (saID.equals(id)) {
241 tokElement = sa;
242 }
243 }
244 if (tokElement == null) {
245 Node assertion =
246 WSSecurityUtil.findSAMLAssertionElementById(
247 doc.getDocumentElement(),
248 id
249 );
250 if (assertion != null) {
251 tokElement = (Element)assertion;
252 }
253 }
254 }
255
256
257
258
259 if (tokElement == null && cb != null && (WSConstants.WSC_SCT.equals(type) ||
260 WSConstants.WSS_SAML_KI_VALUE_TYPE.equals(type) || assertionStr.equals(type))) {
261
262 WSPasswordCallback pwcb =
263 new WSPasswordCallback(id, WSPasswordCallback.CUSTOM_TOKEN);
264 try {
265 cb.handle(new Callback[]{pwcb});
266 Element assertionElem = pwcb.getCustomToken();
267 if (assertionElem != null) {
268 tokElement = (Element)doc.importNode(assertionElem, true);
269 }
270 } catch (Exception e) {
271 log.debug(e.getMessage(), e);
272
273 }
274 }
275
276
277
278
279 if (tokElement == null) {
280 tokElement = WSSecurityUtil.getElementByWsuId(doc, uri);
281
282
283 if (tokElement == null) {
284 tokElement = WSSecurityUtil.getElementByGenId(doc, uri);
285 }
286 }
287
288 return tokElement;
289 }
290
291
292
293
294
295
296
297
298
299
300 public void setKeyIdentifier(X509Certificate cert)
301 throws WSSecurityException {
302 Document doc = this.element.getOwnerDocument();
303 byte data[] = null;
304 try {
305 data = cert.getEncoded();
306 } catch (CertificateEncodingException e) {
307 throw new WSSecurityException(
308 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError", null, e
309 );
310 }
311 Text text = doc.createTextNode(Base64.encode(data));
312
313 createKeyIdentifier(doc, X509Security.X509_V3_TYPE, text, true);
314 }
315
316
317
318
319
320
321
322
323
324
325 public void setKeyIdentifierSKI(X509Certificate cert, Crypto crypto)
326 throws WSSecurityException {
327
328
329
330 if (cert.getVersion() != 3) {
331 throw new WSSecurityException(
332 WSSecurityException.UNSUPPORTED_SECURITY_TOKEN,
333 "invalidCertForSKI",
334 new Object[]{new Integer(cert.getVersion())}
335 );
336 }
337
338 Document doc = this.element.getOwnerDocument();
339 byte data[] = crypto.getSKIBytesFromCert(cert);
340
341 org.w3c.dom.Text text = doc.createTextNode(Base64.encode(data));
342 createKeyIdentifier(doc, SKI_URI, text, true);
343 }
344
345
346
347
348
349
350
351
352
353
354
355 public void setKeyIdentifierThumb(X509Certificate cert) throws WSSecurityException {
356 Document doc = this.element.getOwnerDocument();
357 MessageDigest sha = null;
358 try {
359 sha = MessageDigest.getInstance("SHA-1");
360 } catch (NoSuchAlgorithmException e1) {
361 throw new WSSecurityException(
362 WSSecurityException.FAILURE, "noSHA1availabe", null, e1
363 );
364 }
365 sha.reset();
366 try {
367 sha.update(cert.getEncoded());
368 } catch (CertificateEncodingException e1) {
369 throw new WSSecurityException(
370 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE, "encodeError", null, e1
371 );
372 }
373 byte[] data = sha.digest();
374
375 org.w3c.dom.Text text = doc.createTextNode(Base64.encode(data));
376 createKeyIdentifier(doc, THUMB_URI, text, true);
377 }
378
379
380 public void setKeyIdentifierEncKeySHA1(String value) throws WSSecurityException {
381 Document doc = this.element.getOwnerDocument();
382 org.w3c.dom.Text text = doc.createTextNode(value);
383 createKeyIdentifier(doc, ENC_KEY_SHA1_URI, text, true);
384 }
385
386 public void setSAMLKeyIdentifier(String keyIdVal) throws WSSecurityException {
387 Document doc = this.element.getOwnerDocument();
388 createKeyIdentifier(doc, SAML_ID_URI, doc.createTextNode(keyIdVal), false);
389 }
390 public void setKeyIdentifier(String valueType, String keyIdVal) throws WSSecurityException {
391 Document doc = this.element.getOwnerDocument();
392 createKeyIdentifier(doc, valueType, doc.createTextNode(keyIdVal), false);
393 }
394
395 private void createKeyIdentifier(Document doc, String uri, Node node, boolean base64) {
396 Element keyId = doc.createElementNS(WSConstants.WSSE_NS, "wsse:KeyIdentifier");
397 keyId.setAttributeNS(null, "ValueType", uri);
398 if (base64) {
399 keyId.setAttributeNS(null, "EncodingType", BinarySecurity.BASE64_ENCODING);
400 }
401
402 keyId.appendChild(node);
403 Element elem = getFirstElement();
404 if (elem != null) {
405 this.element.replaceChild(keyId, elem);
406 } else {
407 this.element.appendChild(keyId);
408 }
409 }
410
411
412
413
414
415
416
417 public Element getFirstElement() {
418 for (Node currentChild = this.element.getFirstChild();
419 currentChild != null;
420 currentChild = currentChild.getNextSibling()
421 ) {
422 if (currentChild instanceof Element) {
423 return (Element) currentChild;
424 }
425 }
426 return null;
427 }
428
429
430
431
432
433
434
435 public X509Certificate[] getKeyIdentifier(Crypto crypto) throws WSSecurityException {
436 Element elem = getFirstElement();
437 String value = elem.getAttribute("ValueType");
438 String alias = null;
439
440 if (X509Security.X509_V3_TYPE.equals(value)) {
441 X509Security token = new X509Security(elem);
442 if (token != null) {
443 X509Certificate cert = token.getX509Certificate(crypto);
444 X509Certificate[] certs = new X509Certificate[1];
445 certs[0] = cert;
446 return certs;
447 }
448 } else if (SKI_URI.equals(value)) {
449 alias = getX509SKIAlias(crypto);
450 } else if (THUMB_URI.equals(value)) {
451 Node node = getFirstElement().getFirstChild();
452 if (node == null) {
453 return null;
454 }
455 if (node.getNodeType() == Node.TEXT_NODE) {
456 byte[] thumb = Base64.decode(((Text) node).getData());
457 alias = crypto.getAliasForX509CertThumb(thumb);
458 }
459 }
460
461 if (alias != null) {
462 return crypto.getCertificates(alias);
463 }
464 return null;
465 }
466
467 public String getKeyIdentifierValue() {
468 if (containsKeyIdentifier()) {
469 Node node = getFirstElement().getFirstChild();
470 if (node == null) {
471 return null;
472 }
473 if (node.getNodeType() == Node.TEXT_NODE) {
474 return ((Text) node).getData();
475 }
476 }
477 return null;
478 }
479
480 public String getKeyIdentifierValueType() {
481 if (containsKeyIdentifier()) {
482 Element elem = getFirstElement();
483 return elem.getAttribute("ValueType");
484 }
485 return null;
486 }
487
488
489 public String getX509SKIAlias(Crypto crypto) throws WSSecurityException {
490 if (skiBytes == null) {
491 skiBytes = getSKIBytes();
492 if (skiBytes == null) {
493 return null;
494 }
495 }
496 String alias = crypto.getAliasForX509Cert(skiBytes);
497 if (doDebug) {
498 log.info("X509 SKI alias: " + alias);
499 }
500 return alias;
501 }
502
503 public byte[] getSKIBytes() {
504 if (skiBytes != null) {
505 return skiBytes;
506 }
507 Node node = getFirstElement().getFirstChild();
508 if (node == null) {
509 return null;
510 }
511 if (node.getNodeType() == Node.TEXT_NODE) {
512 try {
513 skiBytes = Base64.decode(((Text) node).getData());
514 } catch (WSSecurityException e) {
515 return null;
516 }
517 }
518 return skiBytes;
519 }
520
521
522
523
524
525
526
527
528 public void setX509IssuerSerial(X509Data ref) {
529 Element elem = getFirstElement();
530 if (elem != null) {
531 this.element.replaceChild(ref.getElement(), elem);
532 } else {
533 this.element.appendChild(ref.getElement());
534 }
535 }
536
537
538
539
540
541
542
543
544
545 public X509Certificate[] getX509IssuerSerial(Crypto crypto) throws WSSecurityException {
546 String alias = getX509IssuerSerialAlias(crypto);
547 if (alias != null) {
548 return crypto.getCertificates(alias);
549 }
550 return null;
551 }
552
553
554
555
556
557
558
559 public String getX509IssuerSerialAlias(Crypto crypto) throws WSSecurityException {
560 if (issuerSerial == null) {
561 issuerSerial = getIssuerSerial();
562 if (issuerSerial == null) {
563 return null;
564 }
565 }
566
567 String alias =
568 crypto.getAliasForX509Cert(issuerSerial.getIssuerName(), issuerSerial.getSerialNumber());
569 if (doDebug) {
570 log.info("X509IssuerSerial alias: " + alias);
571 }
572 return alias;
573 }
574
575 private XMLX509IssuerSerial getIssuerSerial() throws WSSecurityException {
576 if (issuerSerial != null) {
577 return issuerSerial;
578 }
579 Element elem = getFirstElement();
580 if (elem == null) {
581 return null;
582 }
583 try {
584 if (Constants._TAG_X509DATA.equals(elem.getLocalName())) {
585 elem =
586 (Element)WSSecurityUtil.findElement(
587 elem, Constants._TAG_X509ISSUERSERIAL, Constants.SignatureSpecNS
588 );
589 }
590 issuerSerial = new XMLX509IssuerSerial(elem, "");
591 } catch (XMLSecurityException e) {
592 throw new WSSecurityException(
593 WSSecurityException.SECURITY_TOKEN_UNAVAILABLE,
594 "noToken",
595 new Object[]{"Issuer/Serial data element missing"},
596 e
597 );
598 }
599 return issuerSerial;
600 }
601
602
603
604
605
606
607
608 public boolean containsReference() {
609 return this.lengthReference() > 0;
610 }
611
612
613
614
615
616
617
618 public int lengthReference() {
619 return this.length(WSConstants.WSSE_NS, "Reference");
620 }
621
622
623
624
625
626
627
628 public boolean containsX509IssuerSerial() {
629 return this.lengthX509IssuerSerial() > 0;
630 }
631
632
633
634
635
636
637
638 public boolean containsX509Data() {
639 return this.lengthX509Data() > 0;
640 }
641
642
643
644
645
646
647
648 public int lengthX509IssuerSerial() {
649 return this.length(WSConstants.SIG_NS, Constants._TAG_X509ISSUERSERIAL);
650 }
651
652
653
654
655
656
657
658 public int lengthX509Data() {
659 return this.length(WSConstants.SIG_NS, Constants._TAG_X509DATA);
660 }
661
662
663
664
665
666
667
668 public boolean containsKeyIdentifier() {
669 return this.lengthKeyIdentifier() > 0;
670 }
671
672
673
674
675
676
677
678 public int lengthKeyIdentifier() {
679 return this.length(WSConstants.WSSE_NS, "KeyIdentifier");
680 }
681
682
683
684
685
686
687
688
689 public int length(String namespace, String localname) {
690 NodeList childNodes = this.element.getChildNodes();
691 int result = 0;
692 for (int i = 0; i < childNodes.getLength(); i++) {
693 Node n = childNodes.item(i);
694 if (n.getNodeType() == Node.ELEMENT_NODE) {
695 String ns = n.getNamespaceURI();
696 String name = n.getLocalName();
697 if ((((namespace != null) && namespace.equals(ns))
698 || ((namespace == null) && (ns == null)))
699 && (localname.equals(name))
700 ) {
701 result++;
702 }
703 }
704 }
705 return result;
706 }
707
708
709
710
711
712
713 public Element getElement() {
714 return this.element;
715 }
716
717
718
719
720
721
722 public void setID(String id) {
723 String prefix =
724 WSSecurityUtil.setNamespace(
725 this.element, WSConstants.WSU_NS, WSConstants.WSU_PREFIX
726 );
727 this.element.setAttributeNS(WSConstants.WSU_NS, prefix + ":Id", id);
728 }
729
730
731
732
733
734
735 public String toString() {
736 return DOM2Writer.nodeToString(this.element);
737 }
738 }