source: ccr/trunk/nhin-vista/projects/NHINC/Current/Product/Production/Gateway/NhincSAMLCallbackLib/src/gov/hhs/fha/nhinc/callback/SamlCallbackHandler.java@ 507

Last change on this file since 507 was 507, checked in by George Lilly, 15 years ago

NHIN gateway and adaptor for use on linux with VistA EHR and RPMS

File size: 35.5 KB
Line 
1package gov.hhs.fha.nhinc.callback;
2
3import com.sun.org.apache.xml.internal.security.keys.KeyInfo;
4import com.sun.xml.wss.XWSSecurityException;
5import java.io.*;
6import java.security.KeyStoreException;
7import java.security.NoSuchAlgorithmException;
8import java.security.UnrecoverableKeyException;
9import java.security.cert.CertificateException;
10import java.util.*;
11import java.security.KeyStore;
12import java.security.PrivateKey;
13import java.security.PublicKey;
14import java.security.cert.X509Certificate;
15import java.security.cert.Certificate;
16import javax.security.auth.callback.Callback;
17import javax.security.auth.callback.CallbackHandler;
18import javax.security.auth.callback.UnsupportedCallbackException;
19import com.sun.xml.wss.impl.callback.*;
20import com.sun.xml.wss.saml.*;
21import javax.xml.parsers.DocumentBuilderFactory;
22import javax.xml.parsers.ParserConfigurationException;
23import org.w3c.dom.*;
24import java.text.ParseException;
25import java.text.SimpleDateFormat;
26//import javax.mail.internet.AddressException;
27//import javax.mail.internet.InternetAddress;
28import javax.security.auth.x500.X500Principal;
29import org.apache.commons.logging.Log;
30import org.apache.commons.logging.LogFactory;
31
32/**
33 * This class implements the CallbackHandler which is invoked upon sending a
34 * message requiring the SAML Assertion Token. It accesses the information
35 * stored in NMProperties in order to build up the required token elements.
36 */
37public class SamlCallbackHandler implements CallbackHandler {
38
39 private static Log log = LogFactory.getLog(SamlCallbackHandler.class);
40 private static final String AUTHN_DECISION = "Permit";
41 private static final String EVIDENCE_FORM_TYPE = "application/pdf";
42 public static final String HOK_CONFIRM = "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key";
43 public static final String SV_CONFIRM = "urn:oasis:names:tc:SAML:2.0:cm:authorization-over-ssl";
44 private static final String X509_ID = "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName";
45 private static final String WIN_ID = "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName";
46 private static final String UNSPECIFIED = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified";
47 private static final String X509_AUTHN_CNTX_CLS = "urn:oasis:names:tc:SAML:2.0:ac:classes:X509";
48 private static final String NHIN_NS = "http://www.hhs.gov/healthit/nhin";
49 private static final int DEFAULT_NAME = 0;
50 private static final int PRIMARY_NAME = 1;
51 private static final String STORE_TYPE = "JKS";
52 // Custom Property Names must match as defined in the WSDL
53 private static final String ACTION_PROP = "action";
54 private static final String RESOURCE_PROP = "resource";
55 private static final String PURPOSE_CODE_PROP = "purposeForUseRoleCode";
56 private static final String PURPOSE_SYST_PROP = "purposeForUseCodeSystem";
57 private static final String PURPOSE_SYST_NAME_PROP = "purposeForUseCodeSystemName";
58 private static final String PURPOSE_DISPLAY_PROP = "purposeForUseDisplayName";
59 private static final String USER_FIRST_PROP = "userFirstName";
60 private static final String USER_MIDDLE_PROP = "userMiddleName";
61 private static final String USER_LAST_PROP = "userLastName";
62 private static final String USER_NAME_PROP = "userName";
63 private static final String USER_ORG_PROP = "userOrganization";
64 private static final String USER_CODE_PROP = "userRoleCode";
65 private static final String USER_SYST_PROP = "userRoleCodeSystem";
66 private static final String USER_SYST_NAME_PROP = "userRoleCodeSystemName";
67 private static final String USER_DISPLAY_PROP = "userRoleCodeDisplayName";
68 private static final String EXPIRE_PROP = "expirationDate";
69 private static final String SIGN_PROP = "signDate";
70 private static final String CONTENT_REF_PROP = "contentReference";
71 private static final String CONTENT_PROP = "content";
72 private HashMap<Object, Object> tokenVals = new HashMap<Object, Object>();
73 private KeyStore keyStore;
74 private KeyStore trustStore;
75 private static Element svAssertion;
76 private static Element hokAssertion20;
77
78 /**
79 * Constructs the callback handler and initializes the keystore and
80 * truststore references to the security certificates
81 */
82 public SamlCallbackHandler() {
83 log.debug("SamlCallbackHandler Constructor -- Begin");
84 try {
85 initKeyStore();
86 initTrustStore();
87 } catch (IOException e) {
88 log.error("SamlCallbackHandler Exception: " + e.getMessage());
89 e.printStackTrace();
90 throw new RuntimeException(e);
91 }
92 log.debug("SamlCallbackHandler Constructor -- Begin");
93 }
94
95 /**
96 * This is the invoked implementation to handle the SAML Token creation upon
97 * notification of an outgoing message needing SAML. Based on the type of
98 * confirmation method detected on the Callbace it creates either a
99 * "Sender Vouches: or a "Holder-ok_Key" variant of the SAML Assertion.
100 * @param callbacks The SAML Callback
101 * @throws java.io.IOException
102 * @throws javax.security.auth.callback.UnsupportedCallbackException
103 */
104 public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
105 log.debug(" ********************************** Handle SAML Callback Begin**************************");
106 for (int i = 0; i < callbacks.length; i++) {
107 if (callbacks[i] instanceof SAMLCallback) {
108 SAMLCallback samlCallback = (SAMLCallback) callbacks[i];
109 log.debug("=============== Print Runtime properties =============");
110 tokenVals.putAll(samlCallback.getRuntimeProperties());
111 log.debug(tokenVals);
112 log.debug("=============== Completed Print properties =============");
113 if (samlCallback.getConfirmationMethod().equals(SAMLCallback.HOK_ASSERTION_TYPE)) {
114 samlCallback.setAssertionElement(createHOKSAMLAssertion20());
115 hokAssertion20 = samlCallback.getAssertionElement();
116 } else if (samlCallback.getConfirmationMethod().equals(SAMLCallback.SV_ASSERTION_TYPE)) {
117 samlCallback.setAssertionElement(createSVSAMLAssertion20());
118 svAssertion = samlCallback.getAssertionElement();
119 } else {
120 log.error("Unknown SAML Assertion Type: " + samlCallback.getConfirmationMethod());
121 throw new UnsupportedCallbackException(null, "SAML Assertion Type is not matched:" + samlCallback.getConfirmationMethod());
122 }
123 } else {
124 log.error("Unknown Callback encountered: " + callbacks[i]);
125 throw new UnsupportedCallbackException(null, "Unsupported Callback Type Encountered");
126 }
127 }
128 log.debug("********************************** Handle SAML Callback End**************************");
129 }
130
131 /**
132 * Creates the "Sender Vouches" variant of the SAML Assertion token.
133 * @return The Assertion element
134 */
135 private Element createSVSAMLAssertion20() {
136 log.debug("SamlCallbackHandler.createSVSAMLAssertion20() -- Begin");
137 Assertion assertion = null;
138 try {
139 SAMLAssertionFactory factory = SAMLAssertionFactory.newInstance(SAMLAssertionFactory.SAML2_0);
140
141 // create the assertion id
142 String aID = String.valueOf(UUID.randomUUID());
143
144 // name id of the issuer - For now just use default
145 NameID issueId = create509NameID(factory, DEFAULT_NAME);
146
147 // issue instant
148 GregorianCalendar issueInstant = calendarFactory();
149
150 // name id of the subject - user name
151 String uname = "defUser";
152 if (tokenVals.containsKey(USER_NAME_PROP)) {
153 uname = tokenVals.get(USER_NAME_PROP).toString();
154 }
155 NameID nmId = factory.createNameID(uname, null, WIN_ID);
156 Subject subj = factory.createSubject(nmId, null);
157
158 // authentication statement
159 List statements = createAuthnStatements(factory, issueInstant);
160
161 assertion = factory.createAssertion(aID, issueId, issueInstant,
162 null, null, subj, statements);
163
164 assertion.setVersion("2.0");
165
166 log.debug("createSVSAMLAssertion20 end ()");
167 return assertion.toElement(null);
168 } catch (Exception e) {
169 e.printStackTrace();
170 throw new RuntimeException(e);
171 }
172 }
173
174 /**
175 * Creates the "Holder-of-Key" variant of the SAML Assertion token.
176 * @return The Assertion element
177 */
178 private Element createHOKSAMLAssertion20() {
179 log.debug("SamlCallbackHandler.createHOKSAMLAssertion20() -- Begin");
180 Assertion assertion = null;
181 try {
182 SAMLAssertionFactory factory = SAMLAssertionFactory.newInstance(SAMLAssertionFactory.SAML2_0);
183
184 // create the assertion id
185 String aID = String.valueOf(UUID.randomUUID());
186
187 // name id of the issuer - For now just use default
188 NameID issueId = create509NameID(factory, DEFAULT_NAME);
189
190 // issue instant
191 GregorianCalendar issueInstant = calendarFactory();
192
193 // subject information
194 NameID subjId = create509NameID(factory, PRIMARY_NAME);
195
196 // default private key cert request
197 SignatureKeyCallback.DefaultPrivKeyCertRequest request = new SignatureKeyCallback.DefaultPrivKeyCertRequest();
198 getDefaultPrivKeyCert(request);
199 if (request.getX509Certificate() == null) {
200 throw new RuntimeException("Not able to resolve the Default Certificate");
201 }
202 PublicKey pubKey = request.getX509Certificate().getPublicKey();
203 PrivateKey privKey = request.getPrivateKey();
204
205 // subject confirmation
206 DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
207 Document doc = docFactory.newDocumentBuilder().newDocument();
208 KeyInfo keyInfo = new KeyInfo(doc);
209 keyInfo.addKeyValue(pubKey);
210 SubjectConfirmationData scd = factory.createSubjectConfirmationData(null, null, null, null, null, keyInfo.getElement());
211 SubjectConfirmation scf = factory.createSubjectConfirmation(null, scd, HOK_CONFIRM);
212 Subject subj = factory.createSubject(subjId, scf);
213
214 // authentication statement
215 List statements = createAuthnStatements(factory, issueInstant);
216
217 assertion = factory.createAssertion(aID, issueId, issueInstant, null, null, subj, statements);
218 assertion.setVersion("2.0");
219 log.debug("SamlCallbackHandler.createHOKSAMLAssertion20() -- End");
220 return assertion.sign(pubKey, privKey);
221 } catch (ParserConfigurationException ex) {
222 log.error("Unable to create HOK Assertion: " + ex.getMessage());
223 ex.printStackTrace();
224 throw new RuntimeException(ex);
225 } catch (IOException ex) {
226 log.error("Unable to create HOK Assertion: " + ex.getMessage());
227 ex.printStackTrace();
228 throw new RuntimeException(ex);
229 } catch (SAMLException ex) {
230 log.error("Unable to create HOK Assertion: " + ex.getMessage());
231 ex.printStackTrace();
232 throw new RuntimeException(ex);
233 } catch (XWSSecurityException ex) {
234 log.error("Unable to create HOK Assertion: " + ex.getMessage());
235 ex.printStackTrace();
236 throw new RuntimeException(ex);
237 }
238 }
239
240 /**
241 * Both the Issuer and the Subject elements have a NameID element which is
242 * formed through this method. Currently default data is used to specify
243 * the required Issuer information. However, the Subject information is
244 * defined based on the stored value of the userid. If this is a legal X509
245 * structute the NameId is constructed in that format, if not it is
246 * constructed as an "Unspecified" format.
247 * @param factory The factory object used to assist in the construction of
248 * the SAML Assertion token
249 * @param assId Identifies this as default usage case or one with declared
250 * value.
251 * @return The constructed NameID element
252 * @throws com.sun.xml.wss.saml.SAMLException
253 */
254 private NameID create509NameID(SAMLAssertionFactory factory, int assId) throws SAMLException {
255 log.debug("SamlCallbackHandler.createNameID() -- Begin " + assId);
256 NameID nmId = null;
257 String defCN = "SAML User";
258 String defOU = "SU";
259 String defO = "SAML User";
260 String defL = "Los Angeles";
261 String defST = "CA";
262 String defC = "US";
263
264 String identifier;
265 if (assId != PRIMARY_NAME || !tokenVals.containsKey(USER_NAME_PROP)) {
266 identifier = "CN=" + defCN + "," + "OU=" + defOU + "," +
267 "O=" + defO + "," + "L=" + defL + "," +
268 "ST=" + defST + "," + "C=" + defC;
269 nmId = factory.createNameID(identifier, null, X509_ID);
270 log.debug("Create default X509 name: " + identifier);
271 } else {
272 String x509Name = "UID=" + tokenVals.get(USER_NAME_PROP);
273 try {
274 X500Principal prin = new X500Principal(x509Name);
275 nmId = factory.createNameID(x509Name, null, X509_ID);
276 log.debug("Create X509 name: " + x509Name);
277 } catch (IllegalArgumentException iae) {
278 /* Could also test if email form if we wanted to support that */
279 log.warn("Set format as Unspecified. Invalid X509 format: " +
280 tokenVals.get(USER_NAME_PROP) + " " + iae.getMessage());
281 nmId = factory.createNameID(tokenVals.get(USER_NAME_PROP).toString(), null, UNSPECIFIED);
282 }
283 }
284
285 log.debug("SamlCallbackHandler.createNameID() -- End");
286 return nmId;
287 }
288
289 /*public boolean isValidEmailAddress(String address) {
290 log.debug("SamlCallbackHandler.isValidEmailAddress() " + address + " -- Begin");
291 boolean retBool = false;
292 if (address != null && address.length() > 0) {
293 try {
294 InternetAddress emailAddr = new InternetAddress(address, true);
295 String[] tokens = address.split("@");
296 if (tokens.length == 2 && tokens[0].trim().length() > 0 && tokens[1].trim().length() > 0) {
297 retBool = true;
298 } else {
299 log.debug("Address does not follow the form 'local-part@domain'");
300 }
301 } catch (AddressException ex) {
302 // address does not comply with RFC822
303 log.debug("Address is not of the RFC822 format");
304 }
305 }
306 log.debug("SamlCallbackHandler.isValidEmailAddress() " + retBool + " -- End");
307 return retBool;
308 }*/
309 /**
310 * Creates the authentication statement, the attribute statements, and the
311 * authorization decision statements for placement in the SAML Assertion.
312 * @param factory The factory object used to assist in the construction of
313 * the SAML Assertion token
314 * @param issueInstant The calendar representing the time of Assertion issuance
315 * @return A listing of all statements
316 * @throws com.sun.xml.wss.saml.SAMLException
317 */
318 private List createAuthnStatements(SAMLAssertionFactory factory, GregorianCalendar issueInstant) throws SAMLException {
319 log.debug("SamlCallbackHandler.createAuthnStatements() -- Begin");
320 List statements = new ArrayList();
321
322 // Create Subject Locality
323 SubjectLocality subjLoc = null;
324 /* This is currently an optional item
325 try {
326 subjLoc = factory.createSubjectLocality(InetAddress.getLocalHost().getHostAddress(), InetAddress.getLocalHost().getCanonicalHostName());
327 } catch (UnknownHostException ex) {
328 log.debug("Optional element SubjectLocality can not be determined: " + ex.getMessage());
329 }*/
330 AuthnContext authnContext = factory.createAuthnContext(X509_AUTHN_CNTX_CLS, null);
331
332 // Create Authentication statement
333 AuthnStatement authState = (com.sun.xml.wss.saml.assertion.saml20.jaxb20.AuthnStatement) factory.createAuthnStatement(issueInstant, subjLoc, authnContext, "123456", null);
334
335 if (authState != null) {
336 statements.add(authState);
337 }
338
339 statements.addAll(addAssertStatements(factory));
340
341 // Create resource for Authentication Statement
342 String resource = null;
343 if (tokenVals.containsKey(RESOURCE_PROP)) {
344 log.debug("Resource: " + tokenVals.get(RESOURCE_PROP));
345 resource = tokenVals.get(RESOURCE_PROP).toString();
346 }
347
348 // Options are Permit (Deny and Indeterminate are not used at this time)
349 String decision = AUTHN_DECISION;
350
351 List actions = new ArrayList();
352 if (tokenVals.containsKey(ACTION_PROP)) {
353 String actionAttr = tokenVals.get(ACTION_PROP).toString();
354 try {
355 final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
356 final Element elemURAttr = document.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "Action");
357 elemURAttr.setAttribute("Namespace", NHIN_NS);
358 elemURAttr.setTextContent(actionAttr);
359 actions.add(elemURAttr);
360 } catch (ParserConfigurationException ex) {
361 actions.add(actionAttr);
362 }
363 }
364
365 // Evidence Assertion generation
366 Evidence evidence = createEvidence(factory, issueInstant);
367
368 AuthnDecisionStatement authDecState = factory.createAuthnDecisionStatement(resource, decision, actions, evidence);
369 if (authDecState != null) {
370 statements.add(authDecState);
371 }
372
373 log.debug("SamlCallbackHandler.createAuthnStatements() -- End");
374 return statements;
375 }
376
377 /**
378 * Creates the Attribute statements for UserName, UserOrganization,
379 * UserRole, and PurposeForUse
380 * @param factory The factory object used to assist in the construction of
381 * the SAML Assertion token
382 * @return The listing of all Attribute statements
383 * @throws com.sun.xml.wss.saml.SAMLException
384 */
385 private List addAssertStatements(SAMLAssertionFactory factory) throws SAMLException {
386
387 log.debug("SamlCallbackHandler.addAssertStatements() -- Begin");
388 List statements = new ArrayList();
389 List attributes = new ArrayList();
390
391 // Set the User Name Attribute
392 List attributeValues1 = new ArrayList();
393 StringBuffer nameConstruct = new StringBuffer();
394 if (tokenVals.containsKey(USER_FIRST_PROP)) {
395 nameConstruct.append(tokenVals.get(USER_FIRST_PROP) + " ");
396 }
397 if (tokenVals.containsKey(USER_MIDDLE_PROP)) {
398 nameConstruct.append(tokenVals.get(USER_MIDDLE_PROP) + " ");
399 }
400 if (tokenVals.containsKey(USER_LAST_PROP)) {
401 nameConstruct.append(tokenVals.get(USER_LAST_PROP) + " ");
402 }
403 if (nameConstruct.length() > 0) {
404 if (nameConstruct.charAt(nameConstruct.length()-1) == ' ') {
405 nameConstruct.setLength(nameConstruct.length() - 1);
406 }
407 log.debug("UserName: " + nameConstruct.toString());
408 attributeValues1.add(nameConstruct.toString());
409 attributes.add(factory.createAttribute("UserName", NHIN_NS, attributeValues1));
410 } else {
411 log.warn("No information provided to fill in user name attribute");
412 }
413
414 // Set the User Organization Attribute
415 List attributeValues2 = new ArrayList();
416 if (tokenVals.containsKey(USER_ORG_PROP)) {
417 log.debug("UserOrg: " + tokenVals.get(USER_ORG_PROP));
418 attributeValues2.add(tokenVals.get(USER_ORG_PROP));
419 attributes.add(factory.createAttribute("UserOrganization", NHIN_NS, attributeValues2));
420 } else {
421 log.warn("No information provided to fill in user organization attribute");
422 }
423
424 try {
425 // Set the User Role Attribute
426 List attributeValues3 = new ArrayList();
427 final Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
428 final Element elemURAttr = document.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "AttibuteValue");
429 final Element userRole = document.createElementNS(NHIN_NS, "nhin:Role");
430 elemURAttr.appendChild(userRole);
431 if (tokenVals.containsKey(USER_CODE_PROP)) {
432 log.debug("User Role Code: " + tokenVals.get(USER_CODE_PROP));
433 userRole.setAttribute("code", tokenVals.get(USER_CODE_PROP).toString());
434 } else {
435 log.warn("No information provided to fill in user role code attribute");
436 }
437 if (tokenVals.containsKey(USER_SYST_PROP)) {
438 log.debug("User Role Code System: " + tokenVals.get(USER_SYST_PROP));
439 userRole.setAttribute("codeSystem", tokenVals.get(USER_SYST_PROP).toString());
440 } else {
441 log.warn("No information provided to fill in user role code system attribute");
442 }
443 if (tokenVals.containsKey(USER_SYST_NAME_PROP)) {
444 log.debug("User Role Code System Name: " + tokenVals.get(USER_SYST_NAME_PROP));
445 userRole.setAttribute("codeSystemName", tokenVals.get(USER_SYST_NAME_PROP).toString());
446 } else {
447 log.warn("No information provided to fill in user role code system name attribute");
448 }
449 if (tokenVals.containsKey(USER_DISPLAY_PROP)) {
450 log.debug("User Role Display: " + tokenVals.get(USER_DISPLAY_PROP));
451 userRole.setAttribute("displayName", tokenVals.get(USER_DISPLAY_PROP).toString());
452 } else {
453 log.warn("No information provided to fill in user role display attribute");
454 }
455 attributeValues3.add(elemURAttr);
456 attributes.add(factory.createAttribute("UserRole", NHIN_NS, attributeValues3));
457
458 // Add the Purpose For Use Attribute
459 List attributeValues4 = new ArrayList();
460 final Element elemPFUAttr = document.createElementNS("urn:oasis:names:tc:SAML:2.0:assertion", "AttibuteValue");
461 final Element purpose = document.createElementNS(NHIN_NS, "nhin:PurposeForUse");
462 elemPFUAttr.appendChild(purpose);
463 if (tokenVals.containsKey(PURPOSE_CODE_PROP)) {
464 log.debug("Purpose Code: " + tokenVals.get(PURPOSE_CODE_PROP));
465 purpose.setAttribute("code", tokenVals.get(PURPOSE_CODE_PROP).toString());
466 }
467 if (tokenVals.containsKey(PURPOSE_SYST_PROP)) {
468 log.debug("Purpose Code System: " + tokenVals.get(PURPOSE_SYST_PROP));
469 purpose.setAttribute("codeSystem", tokenVals.get(PURPOSE_SYST_PROP).toString());
470 }
471 if (tokenVals.containsKey(PURPOSE_SYST_NAME_PROP)) {
472 log.debug("Purpose Code System Name: " + tokenVals.get(PURPOSE_SYST_NAME_PROP));
473 purpose.setAttribute("codeSystemName", tokenVals.get(PURPOSE_SYST_NAME_PROP).toString());
474 }
475 if (tokenVals.containsKey(PURPOSE_DISPLAY_PROP)) {
476 log.debug("Purpose Display: " + tokenVals.get(PURPOSE_DISPLAY_PROP));
477 purpose.setAttribute("displayName", tokenVals.get(PURPOSE_DISPLAY_PROP).toString());
478 }
479 attributeValues4.add(elemPFUAttr);
480 attributes.add(factory.createAttribute("PurposeForUse", NHIN_NS, attributeValues4));
481
482 if (!attributes.isEmpty()) {
483 statements.add(factory.createAttributeStatement(attributes));
484 }
485 } catch (ParserConfigurationException ex) {
486 log.debug("Unable to create an XML Document to set Attributes" + ex.getMessage());
487 }
488 log.debug("SamlCallbackHandler.addAssertStatements() -- End");
489 return statements;
490
491 }
492
493 /**
494 * Creates the Evidence element that encompasses the Assertion defining the
495 * authorization form needed in cases where evidence of authorization to
496 * access the medical records must be provided along with the message request
497 * @param factory The factory object used to assist in the construction of
498 * the SAML Assertion token
499 * @param issueInstant The calendar representing the time of Assertion issuance
500 * @return The Evidence element
501 * @throws com.sun.xml.wss.saml.SAMLException
502 */
503 private Evidence createEvidence(SAMLAssertionFactory factory, GregorianCalendar issueInstant) throws SAMLException {
504 log.debug("SamlCallbackHandler.createEvidence() -- Begin");
505
506 List evAsserts = new ArrayList();
507 try {
508 String evAssertionID = String.valueOf(UUID.randomUUID());
509 NameID evIssuerId = create509NameID(factory, DEFAULT_NAME);
510
511 GregorianCalendar beginValidTime = calendarFactory();
512 if (tokenVals.containsKey(SIGN_PROP)) {
513 beginValidTime = createCal(tokenVals.get(SIGN_PROP).toString());
514 }
515 GregorianCalendar endValidTime = calendarFactory();
516 if (tokenVals.containsKey(EXPIRE_PROP)) {
517 endValidTime = createCal(tokenVals.get(EXPIRE_PROP).toString());
518 }
519
520 if (beginValidTime.after(endValidTime)) {
521 // set beginning time to now
522 beginValidTime = calendarFactory();
523 log.warn("The beginning time for the valid evidence should be before the ending time. " +
524 "Setting the beginning time to the current system time.");
525 }
526
527 Conditions conditions = factory.createConditions(beginValidTime, endValidTime, null, null, null, null);
528
529 List statements = createEvidenceStatements(factory);
530 evAsserts.add(factory.createAssertion(evAssertionID, evIssuerId, issueInstant, conditions, null, null, statements));
531 } catch (SAMLException ex) {
532 log.debug("Unable to create Evidence Assertion: " + ex.getMessage());
533 }
534 Evidence evidence = factory.createEvidence(null, evAsserts);
535 log.debug("SamlCallbackHandler.createEvidence() -- End");
536 return evidence;
537 }
538
539 /**
540 * Creates a calendar object representing the time given.
541 * @param time following the simple date form MM/dd/yyyy HH:mm:ss
542 * @return The calendar object representing the given time
543 */
544 private GregorianCalendar createCal(String time) {
545 GregorianCalendar cal = calendarFactory();
546 try {
547 SimpleDateFormat dateForm = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
548 cal.setTime(dateForm.parse(time));
549 log.info("SamlCallbackHandler.createCal() Date: " + (cal.get(Calendar.MONTH) + 1) + "/" + cal.get(Calendar.DAY_OF_MONTH) + "/" + cal.get(Calendar.YEAR) + " " + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND));
550 } catch (ParseException ex) {
551 log.error(SamlCallbackHandler.class.getName() + "Date form is expected to be MM/dd/yyyy HH:mm:ss set default date");
552 }
553 return cal;
554 }
555
556 /**
557 * Creates the Attribute Statements needed for the Evidence element. These
558 * include the Attributes for the ContentType, ContentReference, and the
559 * base64binary Content as well.
560 * @param factory The factory object used to assist in the construction of
561 * the SAML Assertion token
562 * @return The listing of the attribute statements for the Evidence element
563 * @throws com.sun.xml.wss.saml.SAMLException
564 */
565 private List createEvidenceStatements(SAMLAssertionFactory factory) throws SAMLException {
566 log.debug("SamlCallbackHandler.createEvidenceStatements() -- Begin");
567 List statements = new ArrayList();
568 List attributes = new ArrayList();
569
570 // Set the Reference to the SSA-827 form
571 List attributeValues1 = new ArrayList();
572 if (tokenVals.containsKey(CONTENT_REF_PROP)) {
573 attributeValues1.add(tokenVals.get(CONTENT_REF_PROP));
574 }
575 attributes.add(factory.createAttribute("ContentReference", NHIN_NS, attributeValues1));
576
577 // Set the format of the SSA-827 form
578 List attributeValues2 = new ArrayList();
579 attributeValues2.add(EVIDENCE_FORM_TYPE);
580 attributes.add(factory.createAttribute("ContentType", NHIN_NS, attributeValues2));
581
582 // Set the content of the SSA-827 form
583 List attributeValues3 = new ArrayList();
584 if (tokenVals.containsKey(CONTENT_PROP)) {
585 byte[] contentForm = Base64Coder.decode(tokenVals.get(CONTENT_PROP).toString());
586 attributeValues3.add(contentForm);
587 }
588 attributes.add(factory.createAttribute("Content", NHIN_NS, attributeValues3));
589
590 if (!attributes.isEmpty()) {
591 statements.add(factory.createAttributeStatement(attributes));
592 }
593 log.debug("SamlCallbackHandler.createEvidenceStatements() -- End");
594 return statements;
595 }
596
597 /**
598 * Initializes the keystore access using the system properties defined in
599 * the domain.xml javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword
600 * @throws java.io.IOException
601 */
602 private void initKeyStore() throws IOException {
603 log.debug("SamlCallbackHandler.initKeyStore() -- Begin");
604 InputStream is = null;
605 String storeLoc = System.getProperty("javax.net.ssl.keyStore");
606 if (storeLoc != null) {
607 String password = System.getProperty("javax.net.ssl.keyStorePassword");
608 if (password != null) {
609 try {
610 keyStore = KeyStore.getInstance(STORE_TYPE);
611 is = new FileInputStream(storeLoc);
612 keyStore.load(is, password.toCharArray());
613 } catch (NoSuchAlgorithmException ex) {
614 log.error("Error initializing KeyStore: " + ex);
615 throw new IOException(ex.getMessage());
616 } catch (CertificateException ex) {
617 log.error("Error initializing KeyStore: " + ex);
618 throw new IOException(ex.getMessage());
619 } catch (KeyStoreException ex) {
620 log.error("Error initializing KeyStore: " + ex);
621 throw new IOException(ex.getMessage());
622 }
623 log.debug("SamlCallbackHandler.initKeyStore() -- End");
624 } else {
625 log.error("javax.net.ssl.keyStorePassword is not defined in domain.xml");
626 }
627 } else {
628 log.error("javax.net.ssl.keyStore is not defined in domain.xml");
629 }
630 }
631
632 /**
633 * Initializes the truststore access using the system properties defined in
634 * the domain.xml javax.net.ssl.trustStore and
635 * javax.net.ssl.trustStorePassword
636 * @throws java.io.IOException
637 */
638 private void initTrustStore() throws IOException {
639 log.debug("SamlCallbackHandler.initTrustStore() -- Begin");
640 InputStream is = null;
641 String storeLoc = System.getProperty("javax.net.ssl.trustStore");
642 if (storeLoc != null) {
643 String password = System.getProperty("javax.net.ssl.trustStorePassword");
644 if (password != null) {
645 try {
646 trustStore = KeyStore.getInstance(STORE_TYPE);
647 is = new FileInputStream(storeLoc);
648 trustStore.load(is, password.toCharArray());
649 } catch (NoSuchAlgorithmException ex) {
650 log.error("Error initializing TrustStore: " + ex);
651 throw new IOException(ex.getMessage());
652 } catch (CertificateException ex) {
653 log.error("Error initializing TrustStore: " + ex);
654 throw new IOException(ex.getMessage());
655 } catch (KeyStoreException ex) {
656 log.error("Error initializing TrustStore: " + ex);
657 throw new IOException(ex.getMessage());
658 }
659 } else {
660 log.error("javax.net.ssl.trustStorePassword is not defined in domain.xml");
661 }
662 } else {
663 log.error("javax.net.ssl.trustStore is not defined in domain.xml");
664 }
665 log.debug("SamlCallbackHandler.initTrustStore() -- End");
666 }
667
668 /**
669 * Finds the X509 certificate in the keystore with the client alias as
670 * defined in the domain.xml system property CLIENT_KEY_ALIAS and
671 * establishes the private key on the SignatureKeyCallback request using
672 * this certificate.
673 * @param request The SignatureKeyCallback request object
674 * @throws java.io.IOException
675 */
676 private void getDefaultPrivKeyCert(
677 SignatureKeyCallback.DefaultPrivKeyCertRequest request)
678 throws IOException {
679 log.debug("SamlCallbackHandler.getDefaultPrivKeyCert() -- Begin");
680 String uniqueAlias = null;
681 String client_key_alias = System.getProperty("CLIENT_KEY_ALIAS");
682 if (client_key_alias != null) {
683 String password = System.getProperty("javax.net.ssl.keyStorePassword");
684 if (password != null) {
685 try {
686 Enumeration aliases = keyStore.aliases();
687 while (aliases.hasMoreElements()) {
688 String currentAlias = (String) aliases.nextElement();
689 if (currentAlias.equals(client_key_alias)) {
690 if (keyStore.isKeyEntry(currentAlias)) {
691 Certificate thisCertificate = keyStore.getCertificate(currentAlias);
692 if (thisCertificate != null) {
693 if (thisCertificate instanceof X509Certificate) {
694 if (uniqueAlias == null) {
695 uniqueAlias = currentAlias;
696 break;
697 }
698 }
699 }
700 }
701 }
702 }
703 if (uniqueAlias != null) {
704 request.setX509Certificate(
705 (X509Certificate) keyStore.getCertificate(uniqueAlias));
706 request.setPrivateKey(
707 (PrivateKey) keyStore.getKey(uniqueAlias, password.toCharArray()));
708 } else {
709 log.error("Client key alais can not be determined");
710 }
711 } catch (UnrecoverableKeyException ex) {
712 log.error("Error initializing Private Key: " + ex);
713 throw new IOException(ex.getMessage());
714 } catch (NoSuchAlgorithmException ex) {
715 log.error("Error initializing Private Key: " + ex);
716 throw new IOException(ex.getMessage());
717 } catch (KeyStoreException ex) {
718 log.error("Error initializing Private Key: " + ex);
719 throw new IOException(ex.getMessage());
720 }
721 } else {
722 log.error("javax.net.ssl.keyStorePassword is not defined in domain.xml");
723 }
724 } else {
725 log.error("CLIENT_KEY_ALIAS is not defined in domain.xml");
726 }
727 log.debug("SamlCallbackHandler.getDefaultPrivKeyCert() -- End");
728 }
729
730 /**
731 * Creates a calendar instance set to the current system time in GMT
732 * @return The calendar instance
733 */
734 private GregorianCalendar calendarFactory() {
735 GregorianCalendar calendar = new GregorianCalendar();
736 calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
737 return calendar;
738 }
739}
Note: See TracBrowser for help on using the repository browser.