001: /*
002: * UsernameToken.java
003: *
004: * Created on September 6, 2006, 10:02 AM
005: *
006: * The contents of this file are subject to the terms
007: * of the Common Development and Distribution License
008: * (the License). You may not use this file except in
009: * compliance with the License.
010: *
011: * You can obtain a copy of the license at
012: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
013: * See the License for the specific language governing
014: * permissions and limitations under the License.
015: *
016: * When distributing Covered Code, include this CDDL
017: * Header Notice in each file and include the License file
018: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
019: * If applicable, add the following below the CDDL Header,
020: * with the fields enclosed by brackets [] replaced by
021: * you own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
025: */
026:
027: package com.sun.xml.ws.security.opt.impl.tokens;
028:
029: import com.sun.xml.ws.security.opt.api.SecurityElementWriter;
030: import com.sun.xml.ws.security.opt.api.SecurityHeaderElement;
031: import com.sun.xml.ws.security.opt.impl.util.JAXBUtil;
032: import com.sun.xml.ws.security.secext10.AttributedString;
033: import com.sun.xml.ws.security.secext10.UsernameTokenType;
034: import java.io.OutputStream;
035: import java.io.UnsupportedEncodingException;
036: import java.security.MessageDigest;
037: import java.security.NoSuchAlgorithmException;
038: import java.security.SecureRandom;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.Map;
042: import java.util.logging.Logger;
043: import java.util.logging.Level;
044: import javax.xml.bind.JAXBElement;
045: import javax.xml.bind.Marshaller;
046: import javax.xml.bind.JAXBException;
047: import javax.xml.namespace.QName;
048: import com.sun.xml.wss.impl.MessageConstants;
049: import com.sun.xml.stream.buffer.XMLStreamBufferResult;
050: import com.sun.xml.ws.security.secext10.ObjectFactory;
051: import javax.xml.stream.XMLStreamException;
052: import com.sun.xml.wss.impl.SecurityTokenException;
053: import com.sun.xml.wss.XWSSecurityException;
054: import com.sun.xml.wss.logging.LogDomainConstants;
055: import com.sun.xml.wss.impl.misc.Base64;
056: import com.sun.xml.ws.api.SOAPVersion;
057:
058: /**
059: * Representation of UsernameToken SecurityHeaderElement
060: * @author Ashutosh.Shahi@sun.com
061: */
062: public class UsernameToken extends UsernameTokenType implements
063: com.sun.xml.ws.security.opt.api.tokens.UsernameToken,
064: SecurityHeaderElement, SecurityElementWriter {
065:
066: public static final long MAX_NONCE_AGE = 900000; //milliseconds
067:
068: // password type
069: private String passwordType = MessageConstants.PASSWORD_TEXT_NS;
070:
071: private String usernameValue = null;
072:
073: private String passwordValue = null;
074:
075: // password Digest value
076: private String passwordDigestValue = null;
077:
078: private byte[] decodedNonce = null;
079:
080: // specifies a cryptographically random sequence
081: private String nonceValue = null;
082:
083: // default nonce encoding
084: private String nonceEncodingType = MessageConstants.BASE64_ENCODING_NS;
085:
086: // time stamp to indicate creation time
087: private String createdValue = null;
088:
089: // flag to indicate whether BSP checks should be made or not.
090: private boolean bsp = false;
091:
092: private boolean valuesSet = false;
093: private SOAPVersion soapVersion = SOAPVersion.SOAP_11;
094: private ObjectFactory objFac = new ObjectFactory();
095:
096: private static Logger log = Logger.getLogger(
097: LogDomainConstants.WSS_API_DOMAIN,
098: LogDomainConstants.WSS_API_DOMAIN_BUNDLE);
099:
100: /** Creates a new instance of UsernameToken */
101: public UsernameToken(SOAPVersion sv) {
102: this .soapVersion = sv;
103: }
104:
105: /**
106: * @return Returns the username.
107: */
108: public String getUsernameValue() {
109: //AttributedString userName
110: return usernameValue;
111: }
112:
113: public void setUsernameValue(String username) {
114: /*AttributedString ut = objFac.createAttributedString();
115: ut.setValue(username);
116: setUsername(ut);*/
117: this .usernameValue = username;
118: }
119:
120: /**
121: * @return Returns the password which may be null meaning no password.
122: */
123: public String getPasswordValue() {
124: return passwordValue;
125: }
126:
127: /**
128: * Sets the password.
129: * @param passwd
130: */
131: public void setPasswordValue(String passwd) {
132: /*AttributedString password = objFac.createAttributedString();
133: password.setValue(passwd);
134: setPassword(password);*/
135: passwordValue = passwd;
136: }
137:
138: /**
139: * @return Returns the passwordType.
140: */
141: public String getPasswordType() {
142: return passwordType;
143: }
144:
145: private void setPasswordType(String passwordType)
146: throws SecurityTokenException {
147: if (MessageConstants.PASSWORD_TEXT_NS.equals(passwordType)) {
148: this .passwordType = MessageConstants.PASSWORD_TEXT_NS;
149: } else if (MessageConstants.PASSWORD_DIGEST_NS
150: .equals(passwordType)) {
151: this .passwordType = MessageConstants.PASSWORD_DIGEST_NS;
152: } else {
153: log.log(Level.SEVERE, "WSS0306.invalid.passwd.type",
154: new Object[] { MessageConstants.PASSWORD_TEXT_NS,
155: MessageConstants.PASSWORD_DIGEST_NS });
156: throw new SecurityTokenException(
157: "Invalid password type. Must be one of "
158: + MessageConstants.PASSWORD_TEXT_NS
159: + " or "
160: + MessageConstants.PASSWORD_DIGEST_NS);
161: }
162: }
163:
164: /**
165: * @return Returns the Nonce Encoding type.
166: */
167: public String getNonceEncodingType() {
168: return this .nonceEncodingType;
169: }
170:
171: /**
172: * Sets the nonce encoding type.
173: * As per WSS:UserNameToken profile, for valid values, refer to
174: * wsse:BinarySecurityToken schema.
175: */
176: private void setNonceEncodingType(String nonceEncodingType) {
177:
178: if (!MessageConstants.BASE64_ENCODING_NS
179: .equals(nonceEncodingType)) {
180: log.log(Level.SEVERE, "WSS0307.nonce.enctype.invalid");
181: throw new RuntimeException("Nonce encoding type invalid");
182: }
183: this .nonceEncodingType = MessageConstants.BASE64_ENCODING_NS;
184: }
185:
186: /**
187: * @return Returns the encoded nonce. Null indicates no nonce was set.
188: */
189: public String getNonceValue() throws SecurityTokenException {
190: return nonceValue;
191: }
192:
193: /**
194: * Returns the created which may be null meaning no time of creation.
195: */
196: public String getCreatedValue() {
197: return createdValue;
198: }
199:
200: public String getPasswordDigestValue() {
201: return this .passwordDigestValue;
202: }
203:
204: /**
205: * set the nonce value.If nonce value is null then it will create one.
206: * @param nonceValue
207: */
208: public void setNonce(String nonceValue) {
209: if (nonceValue == null
210: || MessageConstants.EMPTY_STRING.equals(nonceValue)) {
211: createNonce();
212: } else {
213: this .nonceValue = nonceValue;
214: }
215: }
216:
217: /**
218: * set the creation time.
219: * @param time If null or empty then this method would create one.
220: */
221: public void setCreationTime(String time)
222: throws XWSSecurityException {
223: if (time == null || MessageConstants.EMPTY_STRING.equals(time)) {
224: this .createdValue = getCreatedFromTimestamp();
225: } else {
226: this .createdValue = time;
227: }
228: }
229:
230: public void setDigestOn() throws SecurityTokenException {
231: setPasswordType(MessageConstants.PASSWORD_DIGEST_NS);
232: }
233:
234: public void isBSP(boolean flag) {
235: bsp = flag;
236: }
237:
238: public boolean isBSP() {
239: return bsp;
240: }
241:
242: public String getNamespaceURI() {
243: return MessageConstants.WSSE_NS;
244: }
245:
246: public String getLocalPart() {
247: return MessageConstants.USERNAME_TOKEN_LNAME;
248: }
249:
250: public String getAttribute(String nsUri, String localName) {
251: QName qname = new QName(nsUri, localName);
252: Map<QName, String> otherAttributes = this .getOtherAttributes();
253: return otherAttributes.get(qname);
254: }
255:
256: public String getAttribute(QName name) {
257: Map<QName, String> otherAttributes = this .getOtherAttributes();
258: return otherAttributes.get(name);
259: }
260:
261: public javax.xml.stream.XMLStreamReader readHeader()
262: throws javax.xml.stream.XMLStreamException {
263: if (!this .valuesSet)
264: setValues();
265: XMLStreamBufferResult xbr = new XMLStreamBufferResult();
266: JAXBElement<UsernameTokenType> utElem = objFac
267: .createUsernameToken(this );
268: try {
269: getMarshaller().marshal(utElem, xbr);
270:
271: } catch (JAXBException je) {
272: throw new XMLStreamException(je);
273: }
274: return xbr.getXMLStreamBuffer().readAsXMLStreamReader();
275: }
276:
277: public void writeTo(OutputStream os) {
278: if (!this .valuesSet)
279: setValues();
280: }
281:
282: public void writeTo(javax.xml.stream.XMLStreamWriter streamWriter)
283: throws javax.xml.stream.XMLStreamException {
284: if (!this .valuesSet)
285: setValues();
286: JAXBElement<UsernameTokenType> utElem = objFac
287: .createUsernameToken(this );
288: try {
289: // If writing to Zephyr, get output stream and use JAXB UTF-8 writer
290: if (streamWriter instanceof Map) {
291: OutputStream os = (OutputStream) ((Map) streamWriter)
292: .get("sjsxp-outputstream");
293: if (os != null) {
294: streamWriter.writeCharacters(""); // Force completion of open elems
295: getMarshaller().marshal(utElem, os);
296: return;
297: }
298: }
299:
300: getMarshaller().marshal(utElem, streamWriter);
301: } catch (JAXBException e) {
302: throw new XMLStreamException(e);
303: }
304: }
305:
306: private Marshaller getMarshaller() throws JAXBException {
307: return JAXBUtil.createMarshaller(soapVersion);
308: }
309:
310: /*
311: * Create a unique nonce. Default encoded with base64.
312: * A nonce is a random value that the sender creates
313: * to include in the username token that it sends.
314: * Nonce is an effective counter measure against replay attacks.
315: */
316: private void createNonce() {
317:
318: this .decodedNonce = new byte[18];
319: try {
320: SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
321: random.nextBytes(decodedNonce);
322: } catch (NoSuchAlgorithmException e) {
323: log.log(Level.SEVERE, "WSS0310.no.such.algorithm",
324: new Object[] { e.getMessage() });
325: throw new RuntimeException("No such algorithm found"
326: + e.getMessage());
327: }
328: if (MessageConstants.BASE64_ENCODING_NS == nonceEncodingType)
329: this .nonceValue = Base64.encode(decodedNonce);
330: else {
331: log.log(Level.SEVERE,
332: "WSS0389.unrecognized.nonce.encoding",
333: nonceEncodingType);
334: throw new RuntimeException("Unrecognized encoding: "
335: + nonceEncodingType);
336: }
337: }
338:
339: private String getCreatedFromTimestamp()
340: throws XWSSecurityException {
341: Timestamp ts = new Timestamp(soapVersion);
342: ts.createDateTime();
343: return ts.getCreated().getValue();
344: }
345:
346: /*
347: * Password Digest creation.
348: * As per WSS-UsernameToken spec, if either or both of <wsse:Nonce>
349: * and <wsu:Created> are present, then they must be included in the
350: * digest as follows:
351: *
352: * Password_digest = Base64( SHA_1 (nonce + created + password) )
353: *
354: */
355: private void createDigest() throws SecurityTokenException {
356:
357: String utf8String = "";
358: if (createdValue != null) {
359: utf8String = utf8String + createdValue;
360: }
361:
362: // password is also optional
363: if (password != null) {
364: utf8String = utf8String + password.getValue();
365: }
366:
367: byte[] utf8Bytes;
368: try {
369: utf8Bytes = utf8String.getBytes("utf-8");
370: } catch (UnsupportedEncodingException uee) {
371: log.log(Level.SEVERE,
372: "WSS0390.unsupported.charset.exception");
373: throw new SecurityTokenException(uee);
374: }
375:
376: byte[] bytesToHash;
377: if (decodedNonce != null) {
378: bytesToHash = new byte[utf8Bytes.length + 18];
379: for (int i = 0; i < 18; i++)
380: bytesToHash[i] = decodedNonce[i];
381: for (int i = 18; i < utf8Bytes.length + 18; i++)
382: bytesToHash[i] = utf8Bytes[i - 18];
383: } else {
384: bytesToHash = utf8Bytes;
385: }
386:
387: byte[] hash;
388: try {
389: MessageDigest sha = MessageDigest.getInstance("SHA-1");
390: hash = sha.digest(bytesToHash);
391: } catch (Exception e) {
392: log.log(Level.SEVERE,
393: "WSS0311.passwd.digest.couldnot.be.created",
394: new Object[] { e.getMessage() });
395: throw new SecurityTokenException(
396: "Password Digest could not be created. "
397: + e.getMessage());
398: }
399: this .passwordDigestValue = Base64.encode(hash);
400: }
401:
402: private void setValues() {
403: if (usernameValue != null) {
404: AttributedString ut = objFac.createAttributedString();
405: ut.setValue(usernameValue);
406: setUsername(ut);
407: }
408:
409: if (passwordValue != null
410: && !MessageConstants._EMPTY.equals(passwordValue)) {
411: AttributedString pw = objFac.createAttributedString();
412: if (MessageConstants.PASSWORD_DIGEST_NS == passwordType) {
413: try {
414: createDigest();
415: } catch (com.sun.xml.wss.impl.SecurityTokenException ex) {
416: ex.printStackTrace();
417: }
418: pw.setValue(passwordDigestValue);
419: setPassword(pw);
420: } else {
421: pw.setValue(passwordValue);
422: setPassword(pw);
423: }
424: }
425:
426: if (nonceValue != null) {
427: AttributedString non = objFac.createAttributedString();
428: non.setValue(usernameValue);
429: setNonce(non);
430: if (nonceEncodingType != null) {
431: QName qname = new QName("EncodingType");
432: non.getOtherAttributes().put(qname, nonceEncodingType);
433: }
434: }
435:
436: if (createdValue != null) {
437: AttributedString cr = objFac.createAttributedString();
438: cr.setValue(createdValue);
439: setCreated(cr);
440: }
441:
442: valuesSet = true;
443: }
444:
445: /**
446: *
447: * @param id
448: * @return
449: */
450: public boolean refersToSecHdrWithId(String id) {
451: return false;
452: }
453:
454: public void writeTo(javax.xml.stream.XMLStreamWriter streamWriter,
455: HashMap props) throws javax.xml.stream.XMLStreamException {
456: try {
457: Marshaller marshaller = getMarshaller();
458: Iterator<Map.Entry<Object, Object>> itr = props.entrySet()
459: .iterator();
460: while (itr.hasNext()) {
461: Map.Entry<Object, Object> entry = itr.next();
462: marshaller.setProperty((String) entry.getKey(), entry
463: .getValue());
464: }
465: JAXBElement<UsernameTokenType> utElem = objFac
466: .createUsernameToken(this );
467: if (streamWriter instanceof Map) {
468: OutputStream os = (OutputStream) ((Map) streamWriter)
469: .get("sjsxp-outputstream");
470: if (os != null) {
471: streamWriter.writeCharacters(""); // Force completion of open elems
472: marshaller.marshal(utElem, os);
473: return;
474: }
475: }
476: marshaller.marshal(utElem, streamWriter);
477: } catch (JAXBException jbe) {
478: throw new XMLStreamException(jbe);
479: }
480: }
481: }
|