All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.litsec.swedisheid.opensaml.saml2.signservice.SADFactory Maven / Gradle / Ivy

/*
 * Copyright 2016-2018 Litsec AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.litsec.swedisheid.opensaml.saml2.signservice;

import java.io.IOException;
import java.security.SignatureException;
import java.util.Base64;

import org.apache.commons.lang.RandomStringUtils;
import org.opensaml.security.x509.X509Credential;

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JOSEObjectType;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.Payload;
import com.nimbusds.jose.crypto.RSASSASigner;
import com.nimbusds.jose.util.Base64URL;

import se.litsec.swedisheid.opensaml.saml2.attribute.AttributeConstants;
import se.litsec.swedisheid.opensaml.saml2.signservice.sap.SAD;
import se.litsec.swedisheid.opensaml.saml2.signservice.sap.SADVersion;

/**
 * A bean for building a {@code SAD} object and a signed JWT holding the SAD.
 * 
 * @author Martin Lindström ([email protected])
 */
public class SADFactory {

  /** The default validity time for a SAD (five minutes). */
  public static final int DEFAULT_VALIDITY_TIME = 300;

  /**
   * The default attribute name for the user ID ({@value AttributeConstants#ATTRIBUTE_NAME_PERSONAL_IDENTITY_NUMBER}).
   */
  public static final String DEFAULT_USER_ID_ATTRIBUTE_NAME = AttributeConstants.ATTRIBUTE_NAME_PERSONAL_IDENTITY_NUMBER;

  /** The default size for generated JWT identifiers (24) */
  public static final int DEFAULT_JWT_ID_SIZE = 24;

  /** The name of the default JWT signature algorithm to be used (RS256). */
  public static final String DEFAULT_JWT_SIGNING_ALGORITHM = JWSAlgorithm.RS256.getName();

  /** The entityID of the issuing IdP. */
  private String idpEntityID;

  /** The IdP signature credential. */
  private X509Credential signingCredential;

  /** The validity time for a SAD. Defaults to {@link #DEFAULT_VALIDITY_TIME}. */
  private int validityTime = DEFAULT_VALIDITY_TIME;

  /** The attribute name for the attribute holding the user ID. Defaults to {@link #DEFAULT_USER_ID_ATTRIBUTE_NAME}. */
  private String userIdAttributeName = DEFAULT_USER_ID_ATTRIBUTE_NAME;

  /** The size of generated JWT identifiers. The default is {@link #DEFAULT_JWT_ID_SIZE}. */
  private int jwtIdSize = DEFAULT_JWT_ID_SIZE;

  /** The JWT signature algorithm. The default is {@link #DEFAULT_JWT_SIGNING_ALGORITHM}. */
  private String jwtSigningAlgorithm = DEFAULT_JWT_SIGNING_ALGORITHM;

  /**
   * Constructor.
   * 
   * @param idpEntityID
   *          the entityID of the issuing IdP
   * @param signingCredential
   *          the IdP signature credential
   */
  public SADFactory(String idpEntityID, X509Credential signingCredential) {
    this.idpEntityID = idpEntityID;
    this.signingCredential = signingCredential;
  }

  /**
   * Returns a builder that allows building a SAD using a cascading builder pattern.
   * 
   * @return a SAD builder
   */
  public SADBuilder getBuilder() {
    return new SADBuilder(this);
  }

  /**
   * Given a SAD, the method builds a JWT and signs it using the configured key.
   * 

* Note: Only RSA keys are supported. *

* * @param sad * the SAD to include in the JWT * @return a signed JWT (encoded) * @throws IOException * for JSON processing errors * @throws SignatureException * for JWT signature errors * @see SADBuilder#buildJwt() */ public String createJwt(SAD sad) throws IOException, SignatureException { String encodedSad = Base64.getUrlEncoder().encodeToString(sad.toJsonBytes()); // Create JWT and sign ... // try { JWSHeader header = new JWSHeader.Builder(new JWSAlgorithm(this.jwtSigningAlgorithm)).type(JOSEObjectType.JWT).build(); JWSObject signedJwt = new JWSObject(header, new Payload(new Base64URL(encodedSad))); signedJwt.sign(new RSASSASigner(this.signingCredential.getPrivateKey())); return signedJwt.serialize(); } catch (JOSEException e) { throw new SignatureException("Failed to sign JWT", e); } } /** * Assigns the validity time for a SAD object (in seconds). The default is {@link #DEFAULT_VALIDITY_TIME}. * * @param seconds * validity time in seconds */ public void setValidityTime(int seconds) { if (seconds < 1) { throw new IllegalArgumentException("seconds must be a positive integer"); } this.validityTime = seconds; } /** * Assigns the attribute name for the attribute holding the user ID. Defaults to * {@link #DEFAULT_USER_ID_ATTRIBUTE_NAME}. * * @param userIdAttributeName * attribute name (URI) */ public void setUserIdAttributeName(String userIdAttributeName) { if (userIdAttributeName == null || userIdAttributeName.isEmpty()) { throw new IllegalArgumentException("userIdAttributeName must not be null or empty"); } this.userIdAttributeName = userIdAttributeName; } /** * Assigns the size of generated JWT identifiers. The default is {@link #DEFAULT_JWT_ID_SIZE}. * * @param jwtIdSize * the size */ public void setJwtIdSize(int jwtIdSize) { if (jwtIdSize < 12) { throw new IllegalArgumentException("The jwtIdSize must be at least 12 characters"); } this.jwtIdSize = jwtIdSize; } /** * Assigns the JWT signature algorithm. The default is {@link #DEFAULT_JWT_SIGNING_ALGORITHM}. * * @param jwtSigningAlgorithm * JWT algorithm name */ public void setJwtSigningAlgorithm(String jwtSigningAlgorithm) { this.jwtSigningAlgorithm = jwtSigningAlgorithm; } /** * A builder for creating a SAD and a signed SAD JWT. * * @author Martin Lindström ([email protected]) */ public static class SADBuilder { /** Reference to the actual factory. */ private final SADFactory sadFactory; /** The SAD object being built. */ private SAD sad; /** * Constructor. * * @param sadFactory * the SAD factory */ SADBuilder(SADFactory sadFactory) { this.sadFactory = sadFactory; this.sad = new SAD(); this.sad.setIssuer(sadFactory.idpEntityID); this.sad.setSeElnSadext(new SAD.Extension()); this.sad.getSeElnSadext().setAttributeName(sadFactory.userIdAttributeName); } /** * Builds a SAD. * * @return the SAD */ public SAD buildSAD() { if (this.sad.getJwtId() == null) { this.sad.setJwtId(RandomStringUtils.random(this.sadFactory.jwtIdSize, true, true)); } this.sad.setIssuedAt((int) (System.currentTimeMillis() / 1000)); this.sad.setExpiry(this.sad.getIssuedAt() + this.sadFactory.validityTime); if (this.sad.getSeElnSadext().getVersion() == null) { this.sad.getSeElnSadext().setVersion(SADVersion.VERSION_10.toString()); } return this.sad; } /** * Builds a SAD, creates a JWT, signs it and returns its serialization. * * @return serialized JWT * @throws IOException * for JSON processing errors * @throws SignatureException * for signature errors */ public String buildJwt() throws IOException, SignatureException { return this.sadFactory.createJwt(this.buildSAD()); } /** * Assigns the attribute value of the signer's unique identifier attribute. * * @param subject * the user ID * @return the SAD builder */ public SADBuilder subject(String subject) { this.sad.setSubject(subject); return this; } /** * Assigns the entityID of the Signature Service which is the recipient of this SAD. * * @param audience * the entityID of the recipient * @return the SAD builder */ public SADBuilder audience(String audience) { this.sad.setAudience(audience); return this; } /** * Assigns the unique identifier of this JWT. * * @param jwtId * JWT ID * @return the SAD builder */ public SADBuilder jwtId(String jwtId) { this.sad.setJwtId(jwtId); return this; } /** * Assigns the version of the SAD claim. * * @param version * the version * @return the SAD builder */ public SADBuilder version(SADVersion version) { this.sad.getSeElnSadext().setVersion(version.toString()); return this; } /** * Assigns the ID of the {@code SADRequest} message that requested this SAD. * * @param irt * ID of corresponding {@code SADRequest} * @return the SAD builder */ public SADBuilder inResponseTo(String irt) { this.sad.getSeElnSadext().setInResponseTo(irt); return this; } /** * Assigns the URI identifier of the level of assurance (LoA) used to authenticate the signer. * * @param loa * LoA URI * @return the SAD builder */ public SADBuilder loa(String loa) { this.sad.getSeElnSadext().setLoa(loa); return this; } /** * Assigns the ID of the Sign Request associated with this SAD. * * @param requestID * SignRequest ID * @return the SAD builder */ public SADBuilder requestID(String requestID) { this.sad.getSeElnSadext().setRequestID(requestID); return this; } /** * Assigns the number of documents to be signed in the associated sign request. * * @param docs * the number of documents to be signed * @return the SAD builder */ public SADBuilder numberOfDocuments(int docs) { this.sad.getSeElnSadext().setNumberOfDocuments(docs); return this; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy