001package com.nimbusds.oauth2.sdk.assertions.saml2; 002 003 004import java.security.interfaces.RSAPrivateKey; 005 006import com.nimbusds.oauth2.sdk.SerializeException; 007import net.jcip.annotations.ThreadSafe; 008import org.opensaml.Configuration; 009import org.opensaml.saml2.core.Assertion; 010import org.opensaml.saml2.core.impl.AssertionMarshaller; 011import org.opensaml.xml.io.MarshallerFactory; 012import org.opensaml.xml.io.MarshallingException; 013import org.opensaml.xml.security.credential.BasicCredential; 014import org.opensaml.xml.security.credential.Credential; 015import org.opensaml.xml.security.credential.UsageType; 016import org.opensaml.xml.signature.Signature; 017import org.opensaml.xml.signature.SignatureConstants; 018import org.opensaml.xml.signature.SignatureException; 019import org.opensaml.xml.signature.Signer; 020import org.opensaml.xml.util.XMLHelper; 021import org.w3c.dom.Element; 022 023 024/** 025 * Static SAML 2.0 bearer assertion factory. 026 * 027 * <p>Related specifications: 028 * 029 * <ul> 030 * <li>Assertion Framework for OAuth 2.0 Client Authentication and 031 * Authorization Grants (RFC 7521). 032 * <li>Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 033 * Client Authentication and Authorization Grants (RFC 7522). 034 * </ul> 035 */ 036@ThreadSafe 037public class SAML2AssertionFactory { 038 039 040 /** 041 * Creates a new SAML 2.0 assertion. 042 * 043 * @param details The SAML 2.0 bearer assertion details. Must not 044 * be {@code null}. 045 * @param xmlDsigAlg The XML digital signature algorithm. Must not be 046 * {@code null}. 047 * @param credential The appropriate credentials to facilitate signing 048 * of the assertion. 049 * 050 * @return The SAML 2.0 bearer assertion. 051 * 052 * @throws SerializeException If serialisation or signing failed. 053 */ 054 public static Assertion create(final SAML2AssertionDetails details, 055 final String xmlDsigAlg, 056 final Credential credential) { 057 058 Assertion a = details.toSAML2Assertion(); 059 060 // Create signature element 061 Signature signature = (Signature) Configuration.getBuilderFactory() 062 .getBuilder(Signature.DEFAULT_ELEMENT_NAME) 063 .buildObject(Signature.DEFAULT_ELEMENT_NAME); 064 065 signature.setSigningCredential(credential); 066 signature.setSignatureAlgorithm(xmlDsigAlg); 067 signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); 068 069 a.setSignature(signature); 070 071 MarshallerFactory marshallerFactory = Configuration.getMarshallerFactory(); 072 073 try { 074 // Perform actual signing 075 marshallerFactory.getMarshaller(a).marshall(a); 076 Signer.signObject(signature); 077 } catch (MarshallingException | SignatureException e) { 078 throw new SerializeException(e.getMessage(), e); 079 } 080 081 return a; 082 } 083 084 085 /** 086 * Creates a new SAML 2.0 assertion as an XML element. 087 * 088 * @param details The SAML 2.0 bearer assertion details. Must not 089 * be {@code null}. 090 * @param xmlDsigAlg The XML digital signature algorithm. Must not be 091 * {@code null}. 092 * @param credential The appropriate credentials to facilitate signing 093 * of the assertion. 094 * 095 * @return The SAML 2.0 bearer assertion as an XML element. 096 * 097 * @throws SerializeException If serialisation or signing failed. 098 */ 099 public static Element createAsElement(final SAML2AssertionDetails details, 100 final String xmlDsigAlg, 101 final Credential credential) { 102 103 Assertion a = create(details, xmlDsigAlg, credential); 104 AssertionMarshaller assertionMarshaller = new AssertionMarshaller(); 105 try { 106 return assertionMarshaller.marshall(a); 107 } catch (MarshallingException e) { 108 throw new SerializeException(e.getMessage(), e); 109 } 110 } 111 112 113 /** 114 * Creates a new SAML 2.0 assertion as an XML string. 115 * 116 * @param details The SAML 2.0 bearer assertion details. Must not 117 * be {@code null}. 118 * @param xmlDsigAlg The XML digital signature algorithm. Must not be 119 * {@code null}. 120 * @param credential The appropriate credentials to facilitate signing 121 * of the assertion. 122 * 123 * @return The SAML 2.0 bearer assertion as an XML string. Note that 124 * an XML declaration is not present in the output string. 125 * 126 * @throws SerializeException If serialisation or signing failed. 127 */ 128 public static String createAsString(final SAML2AssertionDetails details, 129 final String xmlDsigAlg, 130 final Credential credential) { 131 132 Element a = createAsElement(details, xmlDsigAlg, credential); 133 String xml = XMLHelper.nodeToString(a); 134 // Strip XML doc declaration 135 final String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; 136 return xml.substring(header.length()); 137 } 138 139 140 /** 141 * Creates a new SAML 2.0 assertion as an XML string, signed with the 142 * RSA-SHA256 XML digital signature algorithm (mandatory to implement). 143 * 144 * @param details The SAML 2.0 bearer assertion details. Must not 145 * be {@code null}. 146 * @param rsaPrivateKey The private RSA key to sign the assertion. Must 147 * not be {@code null}. 148 * 149 * @return The SAML 2.0 bearer assertion as an XML string. Note that 150 * an XML declaration is not present in the output string. 151 * 152 * @throws SerializeException If serialisation or signing failed. 153 */ 154 public static String createAsString(final SAML2AssertionDetails details, 155 final RSAPrivateKey rsaPrivateKey) { 156 157 BasicCredential credential = new BasicCredential(); 158 credential.setPrivateKey(rsaPrivateKey); 159 credential.setUsageType(UsageType.SIGNING); 160 return createAsString(details, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256, credential); 161 } 162 163 164 /** 165 * Prevents public instantiation. 166 */ 167 private SAML2AssertionFactory() {} 168}