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$
27   * $Id$
28   */
29  
30  package dk.sosi.seal.model;
31  
32  import dk.sosi.seal.model.constants.*;
33  import dk.sosi.seal.model.dombuilders.AbstractDOMBuilder;
34  import dk.sosi.seal.pki.CredentialVaultSignatureProvider;
35  import dk.sosi.seal.pki.Federation;
36  import dk.sosi.seal.vault.CredentialVault;
37  import dk.sosi.seal.xml.XmlUtil;
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.w3c.dom.Document;
41  import org.w3c.dom.Element;
42  import org.w3c.dom.Node;
43  
44  import java.util.Date;
45  
46  /**
47   * Builder class used for constructing <code>IdentityToken</code> objects.<br />
48   * An <code>IdentityToken</code> is generated using the following mandatory values:
49   * <ul>
50   * <li>Audience restriction.
51   * <li>Key name.
52   * <li>Not before.
53   * <li>Not on or after.
54   * <li><code>UserIDCard</code>.
55   * </ul>
56   * <br />
57   * All operations related to constructing, wrappring, etc. of the <code>IdentityToken</code> should be done through the <code>IDWSHFactory</code>.
58   * 
59   * @author ads
60   * @since 2.1
61   */
62  public final class IdentityTokenBuilder extends AbstractDOMBuilder<IdentityToken> {
63  
64      private static final Log LOG = LogFactory.getLog(IdentityTokenBuilder.class);
65  
66      private static final String ASSURANCELEVEL = "3";
67      private static final String AUTHN_CONTEXT_CLASS_REF = "urn:oasis:names:tc:SAML:2.0:ac:classes:X509";
68      private static final String AUTHN_INSTANT_ATTRIBUTE = "AuthnInstant";
69      private static final String CONFIRMATION_METHOD = "urn:oasis:names:tc:SAML:2.0:cm:holder-of-key";
70      private static final String FORMAT_ATTRIBUTE = "Format";
71      private static final String ISSUE_INSTANT_ATTRIBUTE = "IssueInstant";
72      private static final String KEY_ID = "SigningKey";
73      private static final String NOT_BEFORE_ATTRIBUTE = "NotBefore";
74      private static final String NOT_ON_OR_AFTER_ATTRIBUTE = "NotOnOrAfter";
75      private static final String SAML_ID_ATTRIBUTE = IDValues.ID;
76      private static final String SAML_VERSION = "2.0";
77      private static final String SPECVERSION = "DK-SAML-2.0";
78      private static final String VERSION_ATTRIBUTE = "Version";
79  
80      /**
81       * Constructs an <code>IdentityToken</code> based on the contents of a URL parameter.
82       * 
83       * 
84       * @param urlParameterValue
85       *            <code>String</code> representation of an <code>IdentityToken</code> taken from a URL parameter.
86       * @param federation
87       * @return The constructed <code>IdentityToken</code>.
88       */
89      public static IdentityToken constructFromURLString(String urlParameterValue, Federation federation) {
90          return new IdentityToken(urlParameterValue, federation);
91      }
92  
93      private String audienceRestriction;
94      private boolean certAsReference;
95      private final CredentialVault credentialVault;
96      private boolean extractCprNumber;
97      private boolean extractCvrNumberIdentifier;
98      private boolean extractOrganizationName;
99      private boolean extractITSystemName;
100     private boolean extractUserAuthorizationCode;
101     private boolean extractUserEducationCode;
102     private String issuer;
103     private Date notBefore;
104     private Date notOnOrAfter;
105     private Node subjectNode;
106     private UserIDCard userIdCard;
107     private String assertionID;
108 
109     /**
110      * Default constructor for the <code>IdentityTokenBuilder</code> class.
111      * 
112      * @param credentialVault
113      *            Required <code>CredentialVault</code> used for signing the generated DOM inside the <code>IdentityToken</code>.
114      */
115     public IdentityTokenBuilder(CredentialVault credentialVault) {
116         if(credentialVault == null) {
117             throw new IllegalArgumentException("credentialVault cannot be null");
118         }
119         this.credentialVault = credentialVault;
120     }
121 
122     /**
123      * Build the final identity token.<br />
124      * Before the <code>Document</code> is generated all attributes will be validated.<br />
125      * <br />
126      * An <code>IdentityToken</code> is generated through the following steps:
127      * <ol>
128      * <li>The DOM representation of the <code>IdentityToken</code> is generated.
129      * <li>The DOM is then signed used the <code>CredentialVault</code> supplied to the constructor of the <code>IdentityTokenBuilder</code>.
130      * <li>The <code>KeyInfo</code> part is then added to the signed document
131      * <li>Finally a new <code>IdentityToken</code> object is created based on the generated and signed DOM.
132      * </ol>
133      * <br />
134      * An <code>IdentityToken</code> is generated each time the method is called. Calling this method multiple times will therefore return multiple objects.
135      * 
136      * @return DOM representation of the Identity token.
137      * @throws ModelException
138      *             Thrown if the builder finds a validation problem.
139      */
140     public final IdentityToken build() throws ModelException {
141         Document document = createDocument();
142 
143         SignatureConfiguration signatureConfiguration = new SignatureConfiguration(new String[] { assertionID }, assertionID, IDValues.Id);
144         signatureConfiguration.setSignatureSiblingNode(subjectNode);
145         signatureConfiguration.setAddCertificateAsReference(certAsReference);
146         signatureConfiguration.setKeyInfoId(KEY_ID);
147 
148         SignatureUtil.sign(new CredentialVaultSignatureProvider(credentialVault), document, signatureConfiguration);
149 
150         return new IdentityToken((Element)document.getFirstChild());
151     }
152 
153     /**
154      * <b>Optional</b>: Include only a reference to the certificate that can validate this identity token instead of the certificate itself.<br />
155      * Example:
156      * 
157      * <pre>
158      *  &lt;ds:KeyInfo Id="SigningKey"&gt;
159      *      &lt;ds:KeyName&gt;OCES1,CVR:55832218-UID:1163447368627,1077391241&lt;/ds:KeyName&gt;
160      *  &lt;/ds:KeyInfo&gt;
161      * </pre>
162      * 
163      * @return The <code>IdentityTokenBuilder</code> instance.
164      */
165     public IdentityTokenBuilder requireCertificateAsReference() {
166         this.certAsReference = true;
167         return this;
168     }
169 
170     /**
171      * <b>Optional</b>: Instructs the <code>IdentityTokenBuilder</code> to extract the cpr number from the <code>IDCard</code>.<br />
172      * Example:
173      * 
174      * <pre>
175      *     &lt;saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="dk:gov:saml:attribute:CprNumberIdentifier"&gt;
176      *       &lt;saml:AttributeValue xsi:type="xs:string"&gt;1111111118&lt;/saml:AttributeValue&gt;
177      *     &lt;/saml:Attribute&gt;
178      * </pre>
179      * 
180      * @return The <code>IdentityTokenBuilder</code> instance.
181      */
182     public IdentityTokenBuilder requireCprNumber() {
183         this.extractCprNumber = true;
184         return this;
185     }
186 
187     /**
188      * <b>Optional</b>: Instructs the <code>IdentityTokenBuilder</code> to extract the CVR number from the <code>CareProvider</code> associated with the <code>IDCard</code>.<br />
189      * Example:
190      * 
191      * <pre>
192      *     &lt;saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="dk:gov:saml:attribute:CvrNumberIdentifier"&gt;
193      *       &lt;saml:AttributeValue xsi:type="xs:string"&gt;20688092&lt;/saml:AttributeValue&gt;
194      *     &lt;/saml:Attribute&gt;
195      * </pre>
196      * 
197      * @return The <code>IdentityTokenBuilder</code> instance.
198      */
199     public IdentityTokenBuilder requireCvrNumberIdentifier() {
200         this.extractCvrNumberIdentifier = true;
201         return this;
202     }
203 
204     /**
205      * <b>Optional</b>: Instructs the <code>IdentityTokenBuilder</code> to extract the organization name from the <code>CareProvider</code> associated with the <code>IDCard</code>.<br />
206      * Example:
207      * 
208      * <pre>
209      *     &lt;saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="urn:oid:2.5.4.10" FriendlyName="organizationName"&gt;
210      *       &lt;saml:AttributeValue xsi:type="xs:string"&gt;Lægehuset på bakken&lt;/saml:AttributeValue&gt;
211      *     &lt;/saml:Attribute&gt;
212      * </pre>
213      * 
214      * @return The <code>IdentityTokenBuilder</code> instance.
215      */
216     public IdentityTokenBuilder requireOrganizationName() {
217         this.extractOrganizationName = true;
218         return this;
219     }
220 
221     /**
222      * <b>Optional</b>: Instructs the <code>IdentityTokenBuilder</code> to extract the ITSystem name from the <code>IDCard</code>.<br />
223      * Example:
224      * 
225      * <pre>
226      *     &lt;saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="dk:healthcare:saml:attribute:ITSystemName"&gt;
227      *       &lt;saml:AttributeValue xsi:type="xs:string"&gt;Harmoni/EMS&lt;/saml:AttributeValue&gt;
228      *     &lt;/saml:Attribute&gt;
229      * </pre>
230      * 
231      * @return The <code>IdentityTokenBuilder</code> instance.
232      */
233     public IdentityTokenBuilder requireITSystemName() {
234         this.extractITSystemName = true;
235         return this;
236     }
237 
238     /**
239      * <b>Optional</b>: Instructs the <code>IdentityTokenBuilder</code> to extract the UserAuthorizationCode from the <code>IDCard</code>.<br />
240      * Example:
241      * 
242      * <pre>
243      *     &lt;saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="dk:healthcare:saml:attribute:UserAuthorizationCode"&gt;
244      *       &lt;saml:AttributeValue xsi:type="xs:string"&gt;004PT&lt;/saml:AttributeValue&gt;
245      *     &lt;/saml:Attribute&gt;
246      * </pre>
247      * 
248      * @return The <code>IdentityTokenBuilder</code> instance.
249      */
250     public IdentityTokenBuilder requireUserAuthorizationCode() {
251         this.extractUserAuthorizationCode = true;
252         return this;
253     }
254 
255     /**
256      * <b>Optional</b>: Instructs the <code>IdentityTokenBuilder</code> to extract the UserEducationCode from the <code>IDCard</code>.<br />
257      * Example:
258      * 
259      * <pre>
260      *     &lt;saml:Attribute NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:basic" Name="dk:healthcare:saml:attribute:UserEducationCode"&gt;
261      *       &lt;saml:AttributeValue xsi:type="xs:string"&gt;7170&lt;/saml:AttributeValue&gt;
262      *     &lt;/saml:Attribute&gt;
263      * </pre>
264      * 
265      * @return The <code>IdentityTokenBuilder</code> instance.
266      */
267     public IdentityTokenBuilder requireUserEducationCode() {
268         this.extractUserEducationCode = true;
269         return this;
270     }
271 
272     /**
273      * <b>Mandatory</b>: Set the audience restriction part of the message.<br />
274      * Example:
275      * 
276      * <pre>
277      *  &lt;saml:Conditions ... &gt;
278      *      &lt;saml:AudienceRestriction&gt;http://fmk-online.dk&lt;/saml:AudienceRestriction&gt;
279      *  &lt;/saml:Conditions&gt;
280      * </pre>
281      * 
282      * @param audienceRestriction
283      *            The <code>audienceRestriction</code> value.
284      * @return The <code>IdentityTokenBuilder</code> instance.
285      */
286     public IdentityTokenBuilder setAudienceRestriction(String audienceRestriction) {
287         this.audienceRestriction = audienceRestriction;
288         return this;
289     }
290 
291     /**
292      * <b>Mandatory</b>: Set the issuer part of the message.<br />
293      * Example:
294      * 
295      * <pre>
296      *  &lt;saml:Assertion ... &gt;
297      *   &lt;saml:Issuer&gt;http://pan.certifikat.dk/sts/services/SecurityTokenService&lt;/saml:Issuer&gt;
298      *   ...
299      *  &lt;/saml:Assertion ... &gt;
300      * </pre>
301      * 
302      * @param issuer
303      *            The <code>issuer</code> value.
304      * @return The <code>IdentityTokenBuilder</code> instance.
305      */
306     public IdentityTokenBuilder setIssuer(String issuer) {
307         this.issuer = issuer;
308         return this;
309     }
310 
311     /**
312      * <b>Mandatory</b>: Set the date/time when the identity token is valid from.<br />
313      * Example:
314      * 
315      * <pre>
316      *  &lt;saml:Conditions NotBefore="2011-07-23T15:32:12Z" ... &gt;
317      *      ...
318      *  &lt;/saml:Conditions&gt;
319      * </pre>
320      * 
321      * @param notBefore
322      *            The beginning of the validity period
323      * @return The <code>IdentityTokenBuilder</code> instance.
324      */
325     public IdentityTokenBuilder setNotBefore(Date notBefore) {
326         this.notBefore = notBefore;
327         return this;
328     }
329 
330     /**
331      * <b>Mandatory</b>: Set the date/time when the identity token expires.<br />
332      * Example:
333      * 
334      * <pre>
335      *  &lt;saml:Conditions ... NotOnOrAfter="2011-07-23T15:32:12Z"&gt;
336      *      ...
337      *  &lt;/saml:Conditions&gt;
338      * </pre>
339      * 
340      * @param notOnOrAfter
341      *            The date/time of expiration.
342      * @return The <code>IdentityTokenBuilder</code> instance.
343      */
344     public IdentityTokenBuilder setNotOnOrAfter(Date notOnOrAfter) {
345         this.notOnOrAfter = notOnOrAfter;
346         return this;
347     }
348 
349     /**
350      * <b>Mandatory</b>: Set the UserIdCard from which attributes for the identity token are extracted.
351      * 
352      * @param userIdCard
353      *            The UserIdCard.
354      * @return The <code>IdentityTokenBuilder</code> instance.
355      */
356     public IdentityTokenBuilder setUserIdCard(UserIDCard userIdCard) {
357         this.userIdCard = userIdCard;
358         return this;
359     }
360 
361     @Override
362     protected final void addBodyContent(Document doc, Element body) {
363         body.appendChild(createIssuer());
364         subjectNode = createSubject(doc);
365         body.appendChild(subjectNode);
366         body.appendChild(createConditions());
367         body.appendChild(createAuthStatement());
368         body.appendChild(createAttributeStatementElement(doc));
369     }
370 
371     @Override
372     protected final void addHeaderContent(Document doc, Element header) {
373         // Do nothing
374     }
375 
376     @Override
377     protected void addRootAttributes(Element root) {
378         root.setAttribute(ISSUE_INSTANT_ATTRIBUTE, XmlUtil.getDateFormat(true).format(new Date()));
379         assertionID = XmlUtil.generateRandomNCName();
380         root.setAttribute(SAML_ID_ATTRIBUTE, assertionID);
381         root.setAttribute(VERSION_ATTRIBUTE, SAML_VERSION);
382 
383         addNS(root, NameSpaces.NS_SAML, NameSpaces.SAML2ASSERTION_SCHEMA);
384         addNS(root, NameSpaces.NS_DS, NameSpaces.DSIG_SCHEMA);
385         addNS(root, NameSpaces.NS_XSI, NameSpaces.XMLSCHEMAINSTANCE_SCHEMA);
386         addNS(root, NameSpaces.NS_XS, NameSpaces.XSD_SCHEMA);
387     }
388 
389     /**
390      * Overriden, since the identity token is a not a complete SOAP message and therefore does not require a SOAP body.
391      */
392     @Override
393     protected final void appendBody(Document doc, Element envelope) {
394         addBodyContent(doc, envelope);
395     }
396 
397     /**
398      * Overriden, since the identity token is a not a complete SOAP message and therefore does not require a SOAP header.
399      */
400     @Override
401     protected final void appendHeader(Document doc, Element envelope) {
402         // Do nothing
403     }
404 
405     @Override
406     protected Element appendRoot(Document doc) {
407         Element envelope = createElement(SAMLTags.assertion);
408         addRootAttributes(envelope);
409         doc.appendChild(envelope);
410         return envelope;
411     }
412 
413     @Override
414     protected void validateBeforeBuild() {
415         validate("userIdCard", userIdCard);
416         validate("notBefore", notBefore);
417         validate("audienceRestriction", audienceRestriction);
418         validate("notOnOrAfter", notOnOrAfter);
419         validate("issuer", issuer);
420 
421         if(!notBefore.before(notOnOrAfter)) {
422             throw new ModelException("notBefore is after notOnOrAfter");
423         }
424     }
425 
426     private Node createAttributeElement(Document doc, String name, String friendlyName, String value) {
427         Element attributeElm = createElement(SAMLTags.attribute);
428         attributeElm.setAttribute(SAMLAttributes.NAME_FORMAT, "urn:oasis:names:tc:SAML:2.0:attrname-format:basic");
429         attributeElm.setAttribute(SAMLAttributes.NAME, name);
430         if(friendlyName != null) {
431             attributeElm.setAttribute(SAMLAttributes.FRIENDLY_NAME, friendlyName);
432         }
433 
434         Element attributeValue = createElement(SAMLTags.attributeValue);
435         attributeValue.setAttributeNS(NameSpaces.XMLSCHEMAINSTANCE_SCHEMA, XSIAttributes.TYPE_PREFIXED, NameSpaces.NS_XS + ":string");
436         attributeValue.setTextContent(value);
437         attributeElm.appendChild(attributeValue);
438 
439         return attributeElm;
440     }
441 
442     private Element createAttributeStatementElement(Document doc) {
443         Element attributeStatementElm = createElement(SAMLTags.attributeStatement);
444         attributeStatementElm.appendChild(createAttributeElement(doc, "urn:oid:2.5.4.4", "surName", userIdCard.getUserInfo().getSurName()));
445         attributeStatementElm.appendChild(createAttributeElement(doc, "urn:oid:2.5.4.3", "CommonName", userIdCard.getUserInfo().getGivenName() + " " + userIdCard.getUserInfo().getSurName()));
446         attributeStatementElm.appendChild(createAttributeElement(doc, "urn:oid:0.9.2342.19200300.100.1.3", "email", userIdCard.getUserInfo().getEmail()));
447         attributeStatementElm.appendChild(createAttributeElement(doc, "dk:gov:saml:attribute:AssuranceLevel", null, ASSURANCELEVEL));
448         attributeStatementElm.appendChild(createAttributeElement(doc, "dk:gov:saml:attribute:SpecVer", null, SPECVERSION));
449         if(extractCvrNumberIdentifier) {
450             if(!SubjectIdentifierTypeValues.CVR_NUMBER.equals(userIdCard.getSystemInfo().getCareProvider().getType())) {
451                 throw new IllegalArgumentException("CVR no. not provided in CareProvider - was " + userIdCard.getSystemInfo().getCareProvider().getType());
452             }
453             attributeStatementElm.appendChild(createAttributeElement(doc, "dk:gov:saml:attribute:CvrNumberIdentifier", null, userIdCard.getSystemInfo().getCareProvider().getID()));
454         }
455         if(extractOrganizationName) {
456             attributeStatementElm.appendChild(createAttributeElement(doc, "urn:oid:2.5.4.10", "organizationName", userIdCard.getSystemInfo().getCareProvider().getOrgName()));
457         }
458         if(extractCprNumber) {
459             attributeStatementElm.appendChild(createAttributeElement(doc, "dk:gov:saml:attribute:CprNumberIdentifier", null, userIdCard.getUserInfo().getCPR()));
460         }
461         if(extractUserAuthorizationCode) {
462             if(userIdCard.getUserInfo().getAuthorizationCode() == null) {
463                 throw new IllegalArgumentException("UserAuthorizationCode missing in UserIdCard - cannot create IdentityToken with UserAuthorizationCode");
464             }
465             attributeStatementElm.appendChild(createAttributeElement(doc, "dk:healthcare:saml:attribute:UserAuthorizationCode", null, userIdCard.getUserInfo().getAuthorizationCode()));
466         }
467         if(extractITSystemName) {
468             attributeStatementElm.appendChild(createAttributeElement(doc, "dk:healthcare:saml:attribute:ITSystemName", null, userIdCard.getSystemInfo().getITSystemName()));
469         }
470         if(extractUserEducationCode) {
471             if(userIdCard.getUserInfo().getAuthorizationCode() == null) {
472                 throw new IllegalArgumentException("UserAuthorizationCode must also be set on UserIdCard in order to treat contents of UserRole as UserEducationCode");
473             }
474             attributeStatementElm.appendChild(createAttributeElement(doc, "dk:healthcare:saml:attribute:UserEducationCode", null, userIdCard.getUserInfo().getRole()));
475         }
476         return attributeStatementElm;
477     }
478 
479     private Node createAuthStatement() {
480         Element authnStatementElm = createElement(SAMLTags.authnStatement);
481         authnStatementElm.setAttribute(AUTHN_INSTANT_ATTRIBUTE, XmlUtil.getDateFormat(true).format(userIdCard.getCreatedDate()));
482 
483         Element authnContextElm = createElement(SAMLTags.authnContext);
484         Element authContextClassRefElm = createElement(SAMLTags.authnContextClassRef);
485         authContextClassRefElm.setTextContent(AUTHN_CONTEXT_CLASS_REF);
486         authnContextElm.appendChild(authContextClassRefElm);
487         authnStatementElm.appendChild(authnContextElm);
488         return authnStatementElm;
489     }
490 
491     private Node createConditions() {
492         Element conditionsElm = createElement(SAMLTags.conditions);
493         conditionsElm.setAttribute(NOT_BEFORE_ATTRIBUTE, XmlUtil.getDateFormat(true).format(notBefore));
494         conditionsElm.setAttribute(NOT_ON_OR_AFTER_ATTRIBUTE, XmlUtil.getDateFormat(true).format(notOnOrAfter));
495 
496         Element audienceRestrictionElm = createElement(SAMLTags.audienceRestriction);
497         conditionsElm.appendChild(audienceRestrictionElm);
498 
499         Element audienceElm = createElement(SAMLTags.audience);
500         audienceElm.setTextContent(audienceRestriction);
501         audienceRestrictionElm.appendChild(audienceElm);
502 
503         return conditionsElm;
504     }
505 
506     private Node createIssuer() {
507         Element issuerElm = createElement(SAMLTags.issuer);
508         issuerElm.setTextContent(issuer);
509         return issuerElm;
510     }
511 
512     private Node createSubject(Document doc) {
513         Element subjectElm = createElement(SAMLTags.subject);
514 
515         Element nameIdElm = createElement(SAMLTags.nameID);
516         nameIdElm.setAttribute(FORMAT_ATTRIBUTE, "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");
517         if(userIdCard.getAlternativeIdentifier() != null) {
518             nameIdElm.setTextContent(userIdCard.getAlternativeIdentifier());
519         } else {
520             nameIdElm.setTextContent(userIdCard.getUserInfo().getCPR());
521         }
522         subjectElm.appendChild(nameIdElm);
523 
524         Element subjectConfirmationElm = createElement(SAMLTags.subjectConfirmation);
525         subjectConfirmationElm.setAttribute(SAMLAttributes.METHOD, CONFIRMATION_METHOD);
526 
527         Element subjectConfirmationDataElm = createElement(SAMLTags.subjectConfirmationData);
528         Element keyInfoElm = createElement(DSTags.keyInfo);
529         Element keyNameElm = createElement(DSTags.keyName);
530         keyNameElm.setTextContent(KEY_ID);
531 
532         keyInfoElm.appendChild(keyNameElm);
533         subjectConfirmationDataElm.appendChild(keyInfoElm);
534         subjectConfirmationElm.appendChild(subjectConfirmationDataElm);
535 
536         subjectElm.appendChild(subjectConfirmationElm);
537         return subjectElm;
538     }
539 }