/**
   * Create user identity token based on an issued token
   *
   * @param ep
   * @param senderNonce
   * @param issuedIdentityToken
   * @return user identity token
   * @throws ServiceResultException if endpoint or the stack doesn't support UserName token policy
   */
  public static UserIdentityToken createIssuedIdentityToken(
      EndpointDescription ep, byte[] senderNonce, byte[] issuedIdentityToken)
      throws ServiceResultException {
    UserTokenPolicy policy = ep.findUserTokenPolicy(UserTokenType.IssuedToken);
    if (policy == null)
      throw new ServiceResultException(
          StatusCodes.Bad_IdentityTokenRejected, "IssuedToken not supported");
    String securityPolicyUri = policy.getSecurityPolicyUri();
    if (securityPolicyUri == null) securityPolicyUri = ep.getSecurityPolicyUri();
    SecurityPolicy securityPolicy = SecurityPolicy.getSecurityPolicy(securityPolicyUri);
    if (securityPolicy == null) securityPolicy = SecurityPolicy.NONE;
    IssuedIdentityToken token = new IssuedIdentityToken();
    token.setTokenData(issuedIdentityToken);

    // Encrypt the token
    SecurityAlgorithm algorithmUri = securityPolicy.getAsymmetricEncryptionAlgorithm();
    if (algorithmUri == null) algorithmUri = SecurityAlgorithm.RsaOaep;
    try {
      Cipher cipher = Cipher.getInstance(algorithmUri.getStandardName());
      Cert serverCert = new Cert(ep.getServerCertificate());
      cipher.init(Cipher.ENCRYPT_MODE, serverCert.getCertificate());
      byte[] tokenData = issuedIdentityToken;
      if (senderNonce != null)
        tokenData =
            ByteBufferUtils.concatenate(
                toArray(issuedIdentityToken.length + senderNonce.length),
                issuedIdentityToken,
                senderNonce);
      token.setTokenData(cipher.doFinal(tokenData));
      token.setEncryptionAlgorithm(algorithmUri.getUri());

    } catch (InvalidKeyException e) {
      // Server certificate does not have encrypt usage
      throw new ServiceResultException(
          StatusCodes.Bad_CertificateInvalid,
          "Server certificate in endpoint is invalid: " + e.getMessage());
    } catch (IllegalBlockSizeException e) {
      throw new ServiceResultException(
          StatusCodes.Bad_SecurityPolicyRejected, e.getClass().getName() + ":" + e.getMessage());
    } catch (BadPaddingException e) {
      throw new ServiceResultException(
          StatusCodes.Bad_CertificateInvalid,
          "Server certificate in endpoint is invalid: " + e.getMessage());
    } catch (NoSuchAlgorithmException e) {
      throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
    } catch (NoSuchPaddingException e) {
      throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
    }

    return token;
  }
  /**
   * Create user identity token based on username and password
   *
   * @param ep
   * @param username
   * @param password
   * @return user identity token
   * @throws ServiceResultException if endpoint or the stack doesn't support UserName token policy
   */
  public static UserIdentityToken createUserNameIdentityToken(
      EndpointDescription ep, byte[] senderNonce, String username, String password)
      throws ServiceResultException {
    UserTokenPolicy policy = ep.findUserTokenPolicy(UserTokenType.UserName);
    if (policy == null)
      throw new ServiceResultException(
          StatusCodes.Bad_IdentityTokenRejected, "UserName not supported");
    String securityPolicyUri = policy.getSecurityPolicyUri();
    if (securityPolicyUri == null) securityPolicyUri = ep.getSecurityPolicyUri();
    SecurityPolicy securityPolicy = SecurityPolicy.getSecurityPolicy(securityPolicyUri);
    if (securityPolicy == null) securityPolicy = SecurityPolicy.NONE;
    UserNameIdentityToken token = new UserNameIdentityToken();

    token.setUserName(username);
    token.setPolicyId(policy.getPolicyId());

    // Encrypt the password, unless no security is defined
    SecurityAlgorithm algorithm = securityPolicy.getAsymmetricEncryptionAlgorithm();
    byte[] pw = password.getBytes(BinaryEncoder.UTF8);
    if (algorithm == null) token.setPassword(pw);
    else
      try {
        Cert serverCert = new Cert(ep.getServerCertificate());
        if (senderNonce != null)
          pw =
              ByteBufferUtils.concatenate(toArray(pw.length + senderNonce.length), pw, senderNonce);
        else pw = ByteBufferUtils.concatenate(toArray(pw.length), pw);
        pw = CryptoUtil.asymmEncrypt(pw, serverCert.getCertificate().getPublicKey(), algorithm);
        token.setPassword(pw);

      } catch (InvalidKeyException e) {
        // Server certificate does not have encrypt usage
        throw new ServiceResultException(
            StatusCodes.Bad_CertificateInvalid,
            "Server certificate in endpoint is invalid: " + e.getMessage());
      } catch (IllegalBlockSizeException e) {
        throw new ServiceResultException(
            StatusCodes.Bad_SecurityPolicyRejected, e.getClass().getName() + ":" + e.getMessage());
      } catch (BadPaddingException e) {
        throw new ServiceResultException(
            StatusCodes.Bad_CertificateInvalid,
            "Server certificate in endpoint is invalid: " + e.getMessage());
      } catch (NoSuchAlgorithmException e) {
        throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
      } catch (NoSuchPaddingException e) {
        throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
      }
    token.setEncryptionAlgorithm(algorithm.getUri());

    return token;
  }