View Javadoc

1   /*
2    * The MIT License
3    *
4    * Original work sponsored and donated by National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
5    *
6    * Copyright (C) 2011 National Board of e-Health (NSI), Denmark (http://www.nsi.dk)
7    *
8    * Permission is hereby granted, free of charge, to any person obtaining a copy of
9    * this software and associated documentation files (the "Software"), to deal in
10   * the Software without restriction, including without limitation the rights to
11   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12   * of the Software, and to permit persons to whom the Software is furnished to do
13   * so, subject to the following conditions:
14   *
15   * The above copyright notice and this permission notice shall be included in all
16   * copies or substantial portions of the Software.
17   *
18   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   * SOFTWARE.
25   *
26   * $HeadURL: https://svn.softwareborsen.dk/sosi/trunk/modules/seal/src/main/java/dk/sosi/seal/SOSIFactory.java $
27   * $Id: SOSIFactory.java 9704 2012-01-10 11:55:16Z chg@lakeside.dk $
28   */
29  package dk.sosi.seal;
30  
31  import dk.sosi.seal.model.*;
32  import dk.sosi.seal.model.constants.DGWSConstants;
33  import dk.sosi.seal.modelbuilders.*;
34  import dk.sosi.seal.pki.AuditEventHandler;
35  import dk.sosi.seal.pki.CredentialVaultSignatureProvider;
36  import dk.sosi.seal.pki.Federation;
37  import dk.sosi.seal.pki.SignatureProvider;
38  import dk.sosi.seal.pki.impl.PropertiesSOSIConfiguration;
39  import dk.sosi.seal.vault.CredentialVault;
40  import dk.sosi.seal.xml.XmlUtil;
41  import dk.sosi.seal.xml.XmlUtilException;
42  import org.w3c.dom.Document;
43  
44  import java.security.cert.X509Certificate;
45  import java.util.Properties;
46  
47  /**
48   * The "factory" used to construct realizations of the SOSI abstractions in the
49   * seal component. The factory acts as the entrypoint in the component, and on
50   * it, you will find factory methods for nearly all types in the SOSI component.
51   *
52   * @author kkj
53   * @author $LastChangedBy: chg@lakeside.dk $
54   * @since 1.0
55   */
56  public class SOSIFactory {
57  
58  	//TODO change properties names to include packages instead of "sosi:"
59  	public static final String PROPERTYNAME_SOSI_VALIDATE = "sosi:validate";
60  	public static final String PROPERTYNAME_SOSI_ISSUER = "sosi:issuer";
61  	public static final String PROPERTYNAME_SOSI_ROOTSCHEMA = "sosi:rootschema";
62  	public static final String PROPERTYNAME_SOSI_FEDERATION_AUDITHANDLER = "sosi:federation.audithandler";
63  	public static final String PROPERTYNAME_SOSI_CRYPTOPROVIDER_PKCS12 = "sosi:cryptoprovider.pkcs12";
64  	public static final String PROPERTYNAME_SOSI_CRYPTOPROVIDER_RSA = "sosi:cryptoprovider.rsa";
65  	public static final String PROPERTYNAME_SOSI_CRYPTOPROVIDER_SHA1WITHRSA = "sosi:cryptoprovider.sha1withrsa";
66  	public static final String PROPERTYNAME_SOSI_CRYPTOPROVIDER_X509 = "sosi:cryptoprovider.x509";
67  	public static final String PROPERTYNAME_SOSI_CRYPTOFACADE_CERTIFICATE_REQUEST_HANDLER = "sosi:cryptofacade.certificaterequesthandler";
68  	public static final String PROPERTYVALUE_SOSI_CRYPTOPROVIDER_BOUNCYCASTLE = "BC";
69  	public static final String PROPERTYVALUE_SOSI_CRYPTOFACADE_BC_CERTIFICATE_REQUEST_HANDLER = "dk.sosi.seal.security.BCCertificateRequestHandler";
70  	public static final String SOSI_DEFAULT_AUDIT_EVENT_HANDLER = "dk.sosi.seal.pki.NoAuditEventHandler";
71  
72      public static final String PROPERTYNAME_SOSI_LDAP_CERTIFICATE_HOST_OCES1 = "sosi:federationcertificate.host.oces1";
73      public static final String PROPERTYNAME_SOSI_LDAP_CERTIFICATE_PORT_OCES1 = "sosi:federationcertificate.port.oces1";
74      //public static final String PROPERTYNAME_SOSI_LDAP_CERTIFICATE_HOST_OCES2 = "sosi:federationcertificate.host.oces2";
75      //public static final String PROPERTYNAME_SOSI_LDAP_CERTIFICATE_PORT_OCES2 = "sosi:federationcertificate.port.oces2";
76  
77  	/** Cache the DOM <code>DocumentBuilderFactory</code>. Please note that this constitutes a JEE compliance problem. Set to <code>false</code> if this is a problem */
78  	public static final String PROPERTYNAME_SOSI_USE_DOCUMENT_BUILDER_FACTORY_CACHE  = "sosi:useDBFCache";
79  	public static final String PROPERTYVALUE_SOSI_USE_DOCUMENT_BUILDER_FACTORY_CACHE  = "true";
80  
81      // enhanced validation against specialized schemas
82      public static final String PROPERTYNAME_SOSI_VALIDATE_ENHANCED = "sosi:validate.enhanced";
83      public static final String PROPERTYVALUE_SOSI_VALIDATE_ENHANCED = "true";
84  
85  	// DGWS version - currently 1.0 and 1.0.1 are supported
86      public static final String PROPERTYNAME_SOSI_DGWS_VERSION = "sosi:dgws.version";
87      public static final String SOSI_DEFAULT_DGWS_VERSION = DGWSConstants.VERSION_1_0_1;
88  
89      // Seal (Not DGWS) message version - currently [1.0_0,1.0_1,1.0_2] supported
90      public static final String PROPERTYNAME_SOSI_SEAL_MESSAGE_VERSION = "sosi:seal.msg.version";
91      public static final String PROPERTYVALUE_SOSI_SEAL_MESSAGE_VERSION = "1.0_0";
92  
93      public static final String PROPERTYNAME_SOSI_DO_NOT_REGISTER_STR_TRANSFORM = "sosi:do.not.register.STRTransform";
94  
95  	private Federation federation;
96      private SignatureProvider provider;
97  	private Properties properties;
98  
99      /**
100      * <p>
101      * Creates a <code>SOSIFactory</code> instance given a
102      * <code>SignatureProvider</code> and a <code>Federation</code>.
103      * </p>
104      *
105      * @param federation
106      *            The <code>Federation</code> to embed in this factory
107      *            instance.
108      * @param provider
109      *            The <code>SignatureProvider</code> to embed in this factory
110      *            instance.
111      * @param properties
112      *            A set of properties containing configuration values for the
113      *            SOSI library.
114      */
115     public SOSIFactory(Federation federation, SignatureProvider provider, Properties properties) throws ModelException {
116         super();
117         this.federation = federation;
118         initialize(provider, properties);
119     }
120 
121     /**
122      * <p>
123      * Creates a <code>SOSIFactory</code> instance given a
124      * <code>CredentialVault</code> and a <code>Federation</code>.
125      * </p>
126      * 
127      * @param federation
128      *            The <code>Federation</code> to embed in this factory
129      *            instance.
130      * @param credentialVault
131      *            The <code>CredentialVault</code> to embed in this factory
132      *            instance.
133      * @param properties
134      *            A set of properties containing configuration values for the
135      *            SOSI library.
136      */
137     public SOSIFactory(Federation federation, CredentialVault credentialVault, Properties properties) throws ModelException {
138         super();
139         this.federation = federation;
140         if (credentialVault == null) {
141             throw new ModelException("The SOSI factory must have a CredentialVault instance");
142         }
143         initialize(new CredentialVaultSignatureProvider(credentialVault, properties), properties);
144     }
145 
146 	/**
147 	 * <p>
148 	 * Creates a <code>SOSIFactory</code> instance given a
149 	 * <code>CredentialVault</code>.
150 	 * </p>
151 	 *
152 	 * @param credentialVault
153 	 *            The <code>CredentialVault</code> to embed in this factory
154 	 *            instance.
155 	 * @param properties
156 	 *            A set of properties containing configuration values for the
157 	 *            SOSI library.
158 	 */
159 	public SOSIFactory(CredentialVault credentialVault, Properties properties) throws ModelException {
160 	    this(null, credentialVault, properties);
161 	}
162 
163 	private void initialize(SignatureProvider provider, Properties props) throws ModelException {
164         if (provider == null) {
165             throw new ModelException("You cannot construct a SOSIFactory without SignatureProvider");
166         }
167         if (props == null) {
168             throw new ModelException("You cannot construct a SOSIFactory without properties");
169         }
170         this.provider = provider;
171         this.properties = props;
172     }
173 
174 	/**
175 	 * Returns the federation associated to this credential vault.
176 	 */
177 	public Federation getFederation() {
178 		return federation;
179 	}
180 
181 
182 	/**
183 	 * Returns the associated credential vault.
184 	 */
185 	public CredentialVault getCredentialVault() {
186         if (provider instanceof CredentialVaultSignatureProvider) {
187             return ((CredentialVaultSignatureProvider) provider).getCredentialVault();
188         } else {
189             throw new ModelException("No CredentialVault configured for this SOSIFactory");
190         }
191     }
192 
193 	/**
194 	 * Returns the properties for the SOSI library.
195 	 */
196 	public Properties getProperties() {
197 		return properties;
198 	}
199 
200 	/**
201 	 * Constructs a <code>Request</code> model instance.
202 	 * <p/>
203 	 * After the object has been created, and an <code>IDCard</code> has been
204 	 * associated with the request, the request can be <i>"serialized"</i> into XML
205 	 * (or more precisely a DOM representation) by calling the <code>serialize2DOMDocument()</code> method.
206 	 *
207 	 * @param demandNonRepudiationReceipt If <code>true</code> this request demands a digital signature on the response to this request.
208 	 * @param flowID                      an optional "session" or "workflow" ID. If the value is <code>null</code> is
209 	 *                                    the <code>flowID</code> will get the same value as <code>messageID</code>.
210 	 */
211 	public Request createNewRequest(boolean demandNonRepudiationReceipt, String flowID) {
212 		return new Request(getDGWSVersion(), demandNonRepudiationReceipt, flowID, this);
213 	}
214 
215 	/**
216 	 * Constructs a <code>SecurityTokenRequest</code> model instance. <p/>
217 	 * After the object has been created, and an <code>IDCard</code> has been
218 	 * associated with the request, the request can be <i>"serialized"</i> into
219 	 * XML (or more precisely a DOM representation) by calling the
220 	 * <code>getDOMDocument()</code> method.
221 	 *
222 	 */
223 	public SecurityTokenRequest createNewSecurityTokenRequest() {
224 		return new SecurityTokenRequest(getDGWSVersion(), this);
225 	}
226 
227 	/**
228 	 * Creates a <code>SecurityTokenResponse</code> model element for a
229 	 * positive SecurityTokenResponse. Per default a
230 	 * <code>SecurityTokenResponse</code> is created as a response to a
231 	 * request. To enable protection against replay attacks, the response embeds
232 	 * the ID of the corresponding request (see the <code>inResponseToID</code>)
233 	 * which is actually a <i>nonce</i>. The request will also get its own
234 	 * federation unique message ID (also a nonce). <p/> After the object has
235 	 * been created, and an optional <code>IDCard</code> has been associated
236 	 * with the response, the response can be <i>"serialized"</i> into XML (or
237 	 * more precisely a DOM representation) by calling the
238 	 * <code>getDOMDocument()</code> method.
239 	 *
240 	 * @param request
241 	 * 			The request that this a response to
242 	 *
243 	 * @return A new SecurityTokenResponse for a positive response
244 	 */
245 	public SecurityTokenResponse createNewSecurityTokenResponse(SecurityTokenRequest request) {
246 		return createNewSecurityTokenResponse(request.getDGWSVersion(), request.getMessageID());
247 	}
248 
249 	/**
250 	 * Creates a <code>SecurityTokenResponse</code> model element for a
251 	 * positive SecurityTokenResponse. Per default a
252 	 * <code>SecurityTokenResponse</code> is created as a response to a
253 	 * request. To enable protection against replay attacks, the response embeds
254 	 * the ID of the corresponding request (see the <code>inResponseToID</code>)
255 	 * which is actually a <i>nonce</i>. The request will also get its own
256 	 * federation unique message ID (also a nonce). <p/> After the object has
257 	 * been created, and an optional <code>IDCard</code> has been associated
258 	 * with the response, the response can be <i>"serialized"</i> into XML (or
259 	 * more precisely a DOM representation) by calling the
260 	 * <code>getDOMDocument()</code> method.
261 	 *
262 	 * @param dgwsVersion
263 	 * 			  The DGWS version to use for this message
264 	 * @param inResponseToID
265 	 * 			The messageID of the request that this error is in response to
266 	 *
267 	 * @return A new SecurityTokenResponse for a positive response
268 	 */
269 	public SecurityTokenResponse createNewSecurityTokenResponse(String dgwsVersion, String inResponseToID) {
270 		return new SecurityTokenResponse(dgwsVersion, inResponseToID, this);
271 	}
272 
273 	/**
274 	 * Creates a <code>Reply</code> model element for a negative Reply. Per
275 	 * default a <code>Reply</code> is created as a response to a request. To
276 	 * enable protection against replay attacks, the response embeds the ID of
277 	 * the corresponding request (see the <code>inResponseToID</code>) which
278 	 * is actually a <i>nonce</i>.
279 	 * <p/>
280 	 * The response can be <i>"serialized"</i> into XML (or more
281 	 * precisely a DOM representation) by calling the
282 	 * <code>getDOMDocument()</code> method.
283 	 * @param request
284 	 * 			The request that this a response to
285 	 * @param faultCode
286 	 *            The status code from FaultCodeValues
287 	 * @param faultString
288 	 *            A human readable error text
289 	 * @param faultActor
290 	 *            The "actor" who is sending the fault, for instance <code>http://www.sosi.dk/STS</code>
291 	 * @return A new SecurityTokenResponse for a negative response
292 	 */
293 	public SecurityTokenResponse createNewSecurityTokenErrorResponse(SecurityTokenRequest request, String faultCode, String faultString, String faultActor) {
294 		return createNewSecurityTokenErrorResponse(request.getDGWSVersion(), request.getMessageID(), faultCode, faultString, faultActor);
295 	}
296 
297 	/**
298 	 * Creates a <code>Reply</code> model element for a negative Reply. Per
299 	 * default a <code>Reply</code> is created as a response to a request. To
300 	 * enable protection against replay attacks, the response embeds the ID of
301 	 * the corresponding request (see the <code>inResponseToID</code>) which
302 	 * is actually a <i>nonce</i>.
303 	 * <p/>
304 	 * The response can be <i>"serialized"</i> into XML (or more
305 	 * precisely a DOM representation) by calling the
306 	 * <code>getDOMDocument()</code> method.
307 	 * @param dgwsVersion
308 	 * 			  The DGWS version to use for this message
309 	 * @param inResponseToID
310 	 * 			The messageID of the request that this error is in response to
311 	 * @param faultCode
312 	 *            The status code from FaultCodeValues
313 	 * @param faultString
314 	 *            A human readable error text
315 	 * @param faultActor
316 	 *            The "actor" who is sending the fault, for instance <code>http://www.sosi.dk/STS</code>
317 	 *
318 	 * @return A new SecurityTokenResponse for a negative response
319 	 */
320 	public SecurityTokenResponse createNewSecurityTokenErrorResponse(String dgwsVersion, String inResponseToID, String faultCode, String faultString, String faultActor) {
321 		return new SecurityTokenResponse(dgwsVersion, inResponseToID, faultCode, faultString, faultActor, this);
322 	}
323 
324 	/**
325 	 * Creates a <code>Reply</code> model element for a positive Reply. Per
326 	 * default a <code>Reply</code> is created as a response to a request. To
327 	 * enable protection against replay attacks, the response embeds the ID of
328 	 * the corresponding request (see the <code>inResponseToID</code>) which
329 	 * is actually a <i>nonce</i>. The request will also get its own federation
330 	 * unique message ID (also a nonce). <p/> After the object has been created,
331 	 * and an optional <code>IDCard</code> has been associated with the
332 	 * response, the response can be <i>"serialized"</i> into XML (or more
333 	 * precisely a DOM representation) by calling the
334 	 * <code>getDOMDocument()</code> method.
335 	 *
336 	 * @param request
337 	 * 			The request that this a response to
338 	 * @param flowStatus
339 	 *            The status code from FlowStatusValues
340 	 * @return A new Reply for a positive response
341 	 */
342 	public Reply createNewReply(Request request, String flowStatus) {
343 		return new Reply(request.getDGWSVersion(), request.getMessageID(), request.getFlowID(), flowStatus, this);
344 	}
345 
346 	/**
347 	 * Creates a <code>Reply</code> model element for a positive Reply. Per
348 	 * default a <code>Reply</code> is created as a response to a request. To
349 	 * enable protection against replay attacks, the response embeds the ID of
350 	 * the corresponding request (see the <code>inResponseToID</code>) which
351 	 * is actually a <i>nonce</i>. The request will also get its own federation
352 	 * unique message ID (also a nonce). <p/> After the object has been created,
353 	 * and an optional <code>IDCard</code> has been associated with the
354 	 * response, the response can be <i>"serialized"</i> into XML (or more
355 	 * precisely a DOM representation) by calling the
356 	 * <code>getDOMDocument()</code> method.
357 	 *
358 	 * @param dgwsVersion
359 	 *            The DGWS version of this
360 	 * @param inResponseToID
361 	 *            The ID of the request that this instance is a reponse to.
362 	 * @param flowID
363 	 *            an optional "session" or "workflow" ID. If the value is
364 	 *            <code>null</code> is the <code>flowID</code> will get the
365 	 *            same value as <code>messageID</code>. For replies the
366 	 *            <code>flowID</code> is usually taken from the corresponding
367 	 *            request.
368 	 * @param flowStatus
369 	 *            The status code from FlowStatusValues
370 	 * @return A new Reply for a positive response
371 	 */
372 	public Reply createNewReply(String dgwsVersion, String inResponseToID, String flowID, String flowStatus) {
373 		return new Reply(dgwsVersion, inResponseToID, flowID, flowStatus, this);
374 	}
375 
376 	/**
377 	 * Creates a <code>Reply</code> model element for a negative Reply. Per
378 	 * default a <code>Reply</code> is created as a response to a request. To
379 	 * enable protection against replay attacks, the response embeds the ID of
380 	 * the corresponding request (see the <code>inResponseToID</code>) which
381 	 * is actually a <i>nonce</i>. The request will also get its own federation
382 	 * unique message ID (also a nonce). <p/> After the object has been created,
383 	 * and an optional <code>IDCard</code> has been associated with the
384 	 * response, the response can be <i>"serialized"</i> into XML (or more
385 	 * precisely a DOM representation) by calling the
386 	 * <code>getDOMDocument()</code> method.
387 	 *
388 	 * @param dgwsVersion
389 	 * 			  The DGWS version to use for this message
390 	 * @param inResponseToID
391 	 *            The ID of the request that this instance is a reponse to.
392 	 * @param flowID
393 	 *            an optional "session" or "workflow" ID. If the value is
394 	 *            <code>null</code> is the <code>flowID</code> will get the
395 	 *            same value as <code>messageID</code>. For replies the
396 	 *            <code>flowID</code> is usually taken from the corresponding
397 	 *            request.
398 	 * @param faultCode
399 	 *            The status code from FaultCodeValues
400 	 * @param faultString
401 	 *            A human readable error text
402 	 * @return A new Reply for a negative response
403 	 */
404 	public Reply createNewErrorReply(String dgwsVersion, String inResponseToID, String flowID, String faultCode, String faultString) {
405 		return new Reply(dgwsVersion, inResponseToID, flowID, faultCode, faultString, this);
406 	}
407 
408 	/**
409 	 * Creates a <code>Reply</code> model element for a negative Reply. Per
410 	 * default a <code>Reply</code> is created as a response to a request. To
411 	 * enable protection against replay attacks, the response embeds the ID of
412 	 * the corresponding request (see the <code>inResponseToID</code>) which
413 	 * is actually a <i>nonce</i>. The request will also get its own federation
414 	 * unique message ID (also a nonce). <p/> After the object has been created,
415 	 * and an optional <code>IDCard</code> has been associated with the
416 	 * response, the response can be <i>"serialized"</i> into XML (or more
417 	 * precisely a DOM representation) by calling the
418 	 * <code>getDOMDocument()</code> method.
419 	 *
420 	 * @param request
421 	 * 			The request that this an error reply to
422 	 * @param faultCode
423 	 *            The status code from FaultCodeValues
424 	 * @param faultString
425 	 *            A human readable error text
426 	 * @return A new Reply for a negative response
427 	 */
428 	public Reply createNewErrorReply(Request request, String faultCode, String faultString) {
429 		return createNewErrorReply(request.getDGWSVersion(), request.getMessageID(), request.getFlowID(), faultCode, faultString);
430 	}
431 
432 	/**
433 	 * Creates a new <code>SystemIDCard</code>.
434 	 *
435 	 * @param itSystemName
436 	 *            The IT system name to embed in the IDCard.
437 	 * @param careProvider
438 	 *            The organizational unit that the user is acting on behalf of
439 	 * @param authenticationLevel
440 	 *            The requested type of signature for this ID card (DGWS level 1 through 3). See @link{dk.sosi.seal.model.AuthenticationLevel}.
441 	 * @param certificate
442 	 *            The public certificate that can validate this ID-card
443 	 * @param alternativeIdentifier
444      *            A <code>String</code> denoting an alternative identifier that
445 	 *            will be used as SAML Subject (of type medcom:other) when serializing
446 	 *            this IDCard instead. May be <code>null</null>.
447 	 * @deprecated
448 	 * 			  Use {@link #createNewSystemIDCard(String, CareProvider, AuthenticationLevel, String, String, X509Certificate, String)} instead
449 	 */
450 	@Deprecated
451     public SystemIDCard createNewSystemIDCard(String itSystemName, CareProvider careProvider, AuthenticationLevel authenticationLevel, X509Certificate certificate,
452 			String alternativeIdentifier) {
453 		return createNewSystemIDCard(itSystemName, careProvider, authenticationLevel, null, null, certificate, alternativeIdentifier);
454 	}
455 
456 	/**
457 	 * Creates a new <code>SystemIDCard</code>.
458 	 *
459 	 * @param itSystemName
460 	 *            The IT system name to embed in the IDCard.
461 	 * @param careProvider
462 	 *            The organizational unit that the user is acting on behalf of
463 	 * @param authenticationLevel
464 	 *            The requested type of signature for this ID card (DGWS level 1 through 3). See @link{dk.sosi.seal.model.AuthenticationLevel}.
465 	 * @param username
466 	 *            The username to employ when creating an idcard with authenticationLeve 2. May be <code>null</code>.
467 	 * @param password
468 	 *            The password to employ when creating an idcard with authenticationLeve 2. May be <code>null</code>.
469 	 * @param certificate
470 	 *            The public certificate that can validate this ID-card
471 	 * @param alternativeIdentifier
472      *            A <code>String</code> denoting an alternative identifier that
473 	 *            will be used as SAML Subject (of type medcom:other) when serializing
474 	 *            this IDCard instead. May be <code>null</null>.
475 	 */
476 	public SystemIDCard createNewSystemIDCard(String itSystemName, CareProvider careProvider, AuthenticationLevel authenticationLevel, String username, String password,
477 			X509Certificate certificate, String alternativeIdentifier) {
478 		SystemInfo systemInfo = new SystemInfo(careProvider, itSystemName);
479 		return new SystemIDCard(getDGWSVersion(), authenticationLevel, getIssuer(), systemInfo, SignatureUtil.getDigestOfCertificate(certificate), alternativeIdentifier, username, password);
480 	}
481 
482 	/**
483 	 * Creates a new <code>UserIDCard</code>.
484 	 *
485 	 * @param itSystemName
486 	 *            The IT system name to embed in the IDCard.
487 	 * @param cpr
488 	 *            Civil Registration number for the user
489 	 * @param givenName
490 	 *            Given name of the user (da:fornavn)
491 	 * @param surName
492 	 *            Surname of the user (da:Efternavn)
493 	 * @param email
494 	 *            The users e-mail address
495 	 * @param occupation
496 	 * 			  The occupation for the user
497 	 * @param role
498 	 *            The role in which the user is acting, for instance doctor or
499 	 *            Nurse
500 	 * @param careProvider
501 	 *            The organizational unit that the user is acting on behalf of
502 	 * @param authorizationCode
503 	 *            The authorization code for the user (SST)
504 	 * @param authenticationLevel
505 	 *            The requested type of signature for this ID card
506 	 * @param certificate
507 	 *            The public certificate that can validate this ID-card. May be <code>null</code>.
508 	 * @param alternativeIdentifier
509      *            A <code>String</code> denoting an alternative identifier that
510 	 *            will be used as SAML Subject (of type medcom:other) when serializing
511 	 *            this IDCard instead. May be <code>null</null>.
512 	 * @deprecated
513 	 *            Use {@link #createNewUserIDCard(String, UserInfo, CareProvider, AuthenticationLevel, String, String, X509Certificate, String)} instead
514 	 */
515 	@Deprecated
516     public UserIDCard createNewUserIDCard(String itSystemName, String cpr, String givenName, String surName, String email, String occupation, String role,
517 		CareProvider careProvider, String authorizationCode, AuthenticationLevel authenticationLevel, X509Certificate certificate, String alternativeIdentifier) {
518 
519 		UserInfo userInfo = new UserInfo(cpr, givenName, surName, email, occupation, role, authorizationCode);
520 		return createNewUserIDCard(itSystemName, userInfo, careProvider, authenticationLevel, null, null, certificate, alternativeIdentifier);
521 	}
522 
523 	/**
524 	 * Creates a new <code>UserIDCard</code>.
525 	 *
526 	 * @param itSystemName
527 	 *            The IT system name to embed in the IDCard.
528 	 * @param userInfo
529 	 *            A <code>UserInfo</code> instance representing the user
530 	 * @param careProvider
531 	 *            The organizational unit that the user is acting on behalf of
532 	 * @param authenticationLevel
533 	 *            The requested type of signature for this ID card
534 	 * @param username
535 	 *            The username to employ when creating an idcard with authenticationLeve 2. May be <code>null</code>.
536 	 * @param password
537 	 *            The password to employ when creating an idcard with authenticationLeve 2. May be <code>null</code>.
538 	 * @param certificate
539 	 *            The public certificate that can validate this ID-card. May be <code>null</code>.
540 	 * @param alternativeIdentifier
541      *            A <code>String</code> denoting an alternative identifier that
542 	 *            will be used as SAML Subject (of type medcom:other) when serializing
543 	 *            this IDCard instead. May be <code>null</null>.
544 	 */
545 	public UserIDCard createNewUserIDCard(String itSystemName, UserInfo userInfo, CareProvider careProvider, AuthenticationLevel authenticationLevel, String username,
546 			String password, X509Certificate certificate, String alternativeIdentifier) {
547 
548 		SystemInfo systemInfo = new SystemInfo(careProvider, itSystemName);
549 		return new UserIDCard(getDGWSVersion(), authenticationLevel, getIssuer(), systemInfo, userInfo, SignatureUtil.getDigestOfCertificate(certificate), alternativeIdentifier, username, password);
550 	}
551 
552 	/**
553 	 * Creates a new <code>IDCard</code> based on the data from an existing id
554 	 * card. The <code>IDCard</code> will get a new unique <code>id</code>
555 	 * and will have the same authentication level as the original
556 	 * <code>IDCard</code>, but will always get signed with a VOCES
557 	 * signature, making this method ideal for <code>IDCard</code> issuing
558 	 * libraries like IdP's.
559 	 *
560 	 * @param origCard
561 	 *            the id card to copy data from
562 	 * @param cpr
563 	 *            cpr to add if origCard doesnt contain it
564 	 * @return a new <code>UserIDCard</code> if the original id card was a
565 	 *         <code>UserIDCard</code> or a new <code>SystemIDCard</code> if
566 	 *         the original id card was a <code>SystemIDCard</code>.
567      *
568      * @deprecated Use one of the methods taking a specific type of IDCard instead
569 	 */
570 	@Deprecated
571     public IDCard copyToVOCESSignedIDCard(IDCard origCard, String cpr) {
572 		if (origCard instanceof UserIDCard) {
573             UserIDCard userIdCard = (UserIDCard) origCard;
574             final UserInfo userInfo;
575             if(cpr == null || "".equals(cpr))
576                 userInfo = userIdCard.getUserInfo();
577             else {
578                 userInfo = new UserInfo(userIdCard.getUserInfo(), cpr);
579             }
580             return copyToVOCESSignedIdCard(userIdCard, userInfo);
581         } else if (origCard instanceof SystemIDCard) {
582             return copyToVOCESSignedIdCard((SystemIDCard) origCard);
583         } else {
584 			throw new ModelException("Unknown IDCard type");
585 		}
586 	}
587 
588     /**
589      * Creates a new <code>IDCard</code> based on the data from an existing id
590      * card. The <code>IDCard</code> will get a new unique <code>id</code>
591      * and will have the same authentication level as the original
592      * <code>IDCard</code>, but will always get signed with a VOCES
593      * signature, making this method ideal for <code>IDCard</code> issuing
594      * libraries like IdP's.
595      *
596      * @param origCard
597      *            the id card to copy data from
598      * @return a new <code>UserIDCard</code> if the original id card was a
599      *         <code>UserIDCard</code> or a new <code>SystemIDCard</code> if
600      *         the original id card was a <code>SystemIDCard</code>.
601      */
602     public IDCard copyToVOCESSignedIDCard(IDCard origCard) {
603         if (origCard instanceof UserIDCard) {
604             UserIDCard userIdCard = (UserIDCard) origCard;
605             return copyToVOCESSignedIdCard(userIdCard, userIdCard.getUserInfo());
606         } else if (origCard instanceof SystemIDCard) {
607             return copyToVOCESSignedIdCard((SystemIDCard) origCard);
608         } else {
609             throw new ModelException("Unknown IDCard type");
610         }
611     }
612 
613     /**
614      * Creates a new <code>UserIDCard</code> based on the data from an existing id
615      * card. The <code>IDCard</code> will get a new unique <code>id</code>
616      * and will have the same authentication level as the original
617      * <code>IDCard</code>, but will always get signed with a VOCES
618      * signature, making this method ideal for <code>IDCard</code> issuing
619      * libraries like IdP's.
620      *
621      * @param origUserIdCard
622      *            the id card to copy data from
623      * @param newUserInfo
624      *            UserInfo to insert instead of the original
625      * @return a new <code>UserIDCard</code>.
626      */
627     public IDCard copyToVOCESSignedIdCard(UserIDCard origUserIdCard, UserInfo newUserInfo) {
628         // if no certHash exists on origCard, generate new on copy
629         final String certHash = getOrGenerateCertHash(origUserIdCard);
630         final IDCard result = new UserIDCard(origUserIdCard, getIssuer(), newUserInfo, certHash);
631         return getSignedIdCard(result);
632     }
633 
634     /**
635      * Creates a new <code>UserIDCard</code> based on the data from an existing id
636      * card. The <code>IDCard</code> will get a new unique <code>id</code>
637      * and will have the same authentication level as the original
638      * <code>IDCard</code>, but will always get signed with a VOCES
639      * signature, making this method ideal for <code>IDCard</code> issuing
640      * libraries like IdP's.
641      *
642      * @param origSystemIdCard
643      *            the id card to copy data from
644      * @return a new <code>UserIDCard</code>.
645      */
646     private IDCard copyToVOCESSignedIdCard(SystemIDCard origSystemIdCard) {
647         // if no certHash exists on origCard, generate new on copy
648         final String certHash = getOrGenerateCertHash(origSystemIdCard);
649         final IDCard result = new SystemIDCard(origSystemIdCard, getIssuer(), certHash);
650         return getSignedIdCard(result);
651     }
652 
653     private String getOrGenerateCertHash(IDCard origCard) {
654         if (origCard.getCertHash() == null || "".equals(origCard.getCertHash())) {
655             return origCard.generateCertHash();
656         } else {
657             return origCard.getCertHash();
658         }
659     }
660 
661     private IDCard getSignedIdCard(IDCard result) {
662         Request tmp = createNewRequest(false, "");
663         tmp.setIDCard(result);
664         Document doc = tmp.serialize2DOMDocument();
665         result.sign(doc, provider);
666         return tmp.getIDCard();
667     }
668 
669     /**
670 	 * "Deserializes" an XML document into a <code>Request</code> model
671 	 * object.
672 	 *
673 	 * @param xml
674 	 *            The XML to deserialize.
675 	 * @throws XmlUtilException
676 	 *             Thrown if the XML could not be read and schema-validated.
677 	 * @throws ModelBuildException
678 	 *             Thrown if the model builder was not able to deserialize the
679 	 *             XML.
680 	 */
681 	public Request deserializeRequest(String xml) throws XmlUtilException, ModelBuildException {
682 		RequestModelBuilder b = new RequestModelBuilder(this);
683 		return b.buildModel(XmlUtil.readXml(properties, xml, validate()));
684 	}
685 
686 	/**
687 	 * "Deserializes" an XML document into a <code>Reply</code> model object.
688 	 *
689 	 * @param xml
690 	 *            The XML to deserialize.
691 	 * @throws XmlUtilException
692 	 *             Thrown if the XML could not be read and schema-validated.
693 	 * @throws ModelBuildException
694 	 *             Thrown if the model builder was not able to deserialize the
695 	 *             XML.
696 	 */
697 	public Reply deserializeReply(String xml) throws XmlUtilException, ModelBuildException {
698 		ReplyModelBuilder b = new ReplyModelBuilder(this);
699 		return b.buildModel(XmlUtil.readXml(properties, xml, validate()));
700 	}
701 
702 	/**
703 	 * "Deserializes" an XML document into a <code>SecurityTokenRequest</code>
704 	 * model object.
705 	 *
706 	 * @param xml
707 	 *            The XML to deserialize.
708 	 * @throws XmlUtilException
709 	 *             Thrown if the XML could not be read and schema-validated.
710 	 * @throws ModelBuildException
711 	 *             Thrown if the model builder was not able to deserialize the
712 	 *             XML.
713 	 */
714 	public SecurityTokenRequest deserializeSecurityTokenRequest(String xml) throws XmlUtilException, ModelBuildException {
715 		SecurityTokenRequestModelBuilder b = new SecurityTokenRequestModelBuilder(this);
716 		return b.buildModel(XmlUtil.readXml(properties, xml, validate()));
717 	}
718 
719 	/**
720 	 * "Deserializes" an XML document into a <code>Reply</code> model object.
721 	 *
722 	 * @param xml
723 	 *            The XML to deserialize.
724 	 * @throws XmlUtilException
725 	 *             Thrown if the XML could not be read and schema-validated.
726 	 * @throws ModelBuildException
727 	 *             Thrown if the model builder was not able to deserialize the
728 	 *             XML.
729 	 */
730 	public SecurityTokenResponse deserializeSecurityTokenResponse(String xml) throws XmlUtilException, ModelBuildException {
731 		SecurityTokenResponseModelBuilder b = new SecurityTokenResponseModelBuilder(this);
732 		return b.buildModel(XmlUtil.readXml(properties, xml, validate()));
733 	}
734 
735 
736 	/**
737 	 * "Deserializes" an XML document into an <code>IDCard</code> model object.
738 	 *
739 	 * @param xml
740 	 *            The XML to deserialize.
741 	 * @throws XmlUtilException
742 	 *             Thrown if the XML could not be read and schema-validated.
743 	 * @throws ModelBuildException
744 	 *             Thrown if the model builder was not able to deserialize the
745 	 *             XML.
746 	 */
747 	public IDCard deserializeIDCard(String xml) throws XmlUtilException, ModelBuildException {
748 		IDCardModelBuilder builder = new IDCardModelBuilder();
749 		return builder.buildModel(XmlUtil.readXml(properties, xml, validate()));
750 	}
751 
752 	/**
753 	 * "Deserializes" an XML document into a <code>RequestHeader</code> model object.
754 	 *
755 	 * @param xml
756 	 *            The XML to deserialize.
757 	 * @throws XmlUtilException
758 	 *             Thrown if the XML could not be read and schema-validated.
759 	 * @throws ModelBuildException
760 	 *             Thrown if the model builder was not able to deserialize the
761 	 *             XML.
762 	 *
763 	 * @since 1.5.10
764 	 */
765 	public RequestHeader deserializeRequestHeader(String xml)  throws XmlUtilException, ModelBuildException {
766 		RequestHeaderModelBuilder builder = new RequestHeaderModelBuilder(this);
767 		return builder.buildModel(XmlUtil.readXml(properties, xml, validate()));
768 	}
769 
770 	/**
771 	 * "Deserializes" an XML document into a <code>ReplyHeader</code> model object.
772 	 *
773 	 * @param xml
774 	 *            The XML to deserialize.
775 	 * @throws XmlUtilException
776 	 *             Thrown if the XML could not be read and schema-validated.
777 	 * @throws ModelBuildException
778 	 *             Thrown if the model builder was not able to deserialize the
779 	 *             XML.
780 	 *
781 	 * @since 1.5.10
782 	 */
783 	public ReplyHeader deserializeReplyHeader(String xml)  throws XmlUtilException, ModelBuildException {
784 		ReplyHeaderModelBuilder builder = new ReplyHeaderModelBuilder(this);
785 		return builder.buildModel(XmlUtil.readXml(properties, xml, validate()));
786 	}
787 
788 	public static AuditEventHandler getAuditEventHandler(Properties properties) throws ModelException {
789         // TODO: the configuration created should probably be stored to avoid recreating it every time.
790         return new PropertiesSOSIConfiguration(properties).getAuditEventHandler();
791 	}
792 
793 	// ==================================
794 	// Private methods
795 	// ==================================
796 
797 	private String getIssuer() {
798 		return properties.getProperty(PROPERTYNAME_SOSI_ISSUER, "TheSOSILibrary");
799 	}
800 
801 	private boolean validate() {
802 		return properties.getProperty(PROPERTYNAME_SOSI_VALIDATE, "true").equalsIgnoreCase("true");
803 	}
804 
805 	private String getDGWSVersion() {
806 		return properties.getProperty(PROPERTYNAME_SOSI_DGWS_VERSION, SOSI_DEFAULT_DGWS_VERSION);
807 	}
808 }