SAML adapter
The SAML adapter allows TrustBuilder to generate and validate SAML-tokens:
version 1.1
version 2.0
Prerequisites
A keystore with privatekey(alias/password) are required for
Signing a saml generated token
Decrypting a saml validated token
A truststore is required for validation. If a certificateAlias is given it will be validated against that certificate otherwise the whole truststore is taken into account.
Configuration
AdapterUniqueID
Unique name assigned to this adapter; the name is used to reference the adapter in the workflow. The ID has following requirements:
START with a letter or _ (underscore)
FOLLOWED by a combination of following characters: Letter, Number, '.' (dot), '-' (dash), '_' (underscore)
PrivateKeyAlias The PrivateKeyAlias identifies the key for signing and decrypting attributes. The matching certificate can either be send manually to the other end or be included in the token/template.
PrivateKeyPassword Password of the private key
CertificateAlias The CertificateAlias identifies the certificate used for validating signatures. If not specified a TrustManager will be used over the truststore defined in the config. If this truststore is not available the keystore will be taken instead. The role of this trustmanager is to attempt to take the correct certificate for the job at hand.
AddSignatureKeyInfo Whether to include a key info element containing the signer certificate in the generated token's signature.
UseSignatureKeyInfo Whether to validate the certifcate found in the token against CertificateAlias or truststore
Workflow Settings
A request for the adapter is prepared by specifying the following properties/scripts in the adapter activity:
Input Property: the variable containing the instructions the adapter have to execute
Output Property: the variable the adapter will store the response in after execution
Before Adapter Script: script that will be executed before calling the adapter
After Adapter Script: script that will be executed after the adapter fulfilled its task
Request - API
SamlGenerateRequest
The SamlGenerateRequest allows clients to generate a SAML token. This will be signed by the adapter.
samlGenerateRequest(template)
with parameters:
template: Non-null, non-empty string of a base 64 encoded saml template. You can specify the signature method inside the template as the attribute with name "SignatureMethod" inside the signature tag.
other properties are:
binding: REDIRECT, allow the use of HTTP Redirect Binding
timezone: Timezone to use for the timestamps used within the saml request
keyName: KeyName instead of key/x509 certificate
Example token (SAML V2.0):
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" Version="2.0">
<samlp:Status>
<samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</samlp:Status>
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0">
<saml:Issuer>...</saml:Issuer>
<saml:Conditions Validity="...">
<saml:AudienceRestriction>
<saml:Audience>...</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<saml:AttributeStatement>
<saml:Attribute Name="..." NameFormat="...">
<saml:AttributeValue>...</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</samlp:Response>
SamlMetadataRequest
The SamlMetadataRequest allows clients to sign and validate SAML metadata tokens.
samlMetadataRequest(template)
with parameters:
template: Non-null, non-empty string of a base 64 encoded saml template.
Example metadata (SAML V2.0):
<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor
ID="tst201307260955"
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
xmlns:attr="urn:oasis:names:tc:SAML:metadata:attribute"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
entityID="urn:be123:entities:9000" >
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Reference="#tst201307260955"
SignatureMethod="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
DigestMethod="http://www.w3.org/2001/04/xmlenc#sha256"/>
<md:SPSSODescriptor
AuthnRequestsSigned="true"
WantAssertionsSigned ="true"
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo>
<ds:KeyName>test-key</ds:KeyName>
<ds:X509Data>
<ds:X509Certificate>MIIGKTC.....hb</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:AssertionConsumerService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://www.test.be/tb/html/wsaml2acs"
index="1"
isDefault="true"/>
</md:SPSSODescriptor>
<md:Organization>
<md:OrganizationName xml:lang="nl-nl">Test</md:OrganizationName>
<md:OrganizationDisplayName xml:lang="nl-nl">Test</md:OrganizationDisplayName>
<md:OrganizationURL xml:lang="nl-nl">http://www.test.be</md:OrganizationURL>
</md:Organization>
<md:ContactPerson contactType="administrative">
<md:Company>Test</md:Company>
<md:GivenName>Test</md:GivenName>
<md:SurName>Test</md:SurName>
<md:EmailAddress>test@test.be</md:EmailAddress>
<md:TelephoneNumber>1235426505</md:TelephoneNumber>
</md:ContactPerson>
<md:ContactPerson contactType="technical">
<md:Company>Test</md:Company>
<md:GivenName>Test</md:GivenName>
<md:SurName>Test</md:SurName>
<md:EmailAddress>test@test.be</md:EmailAddress>
<md:TelephoneNumber>1235426505</md:TelephoneNumber>
</md:ContactPerson>
</md:EntityDescriptor>
SamlValidateRequest
The SamlValidateRequest allows clients to validate a SAML 1.1 or 2.0 token and retrieve the necessary info.
samlValidateRequest(token)
with parameters:
token: Non-null, non-empty string of a base 64 encoded token.
For an example token, see the generation.
Signing the request/metadata
To sign the request/metadata , Trustbuilder lets you use a template in the xml structure. This way you can place the code where you need it. The SAML Adapter finds this xml code en will replace the content with a enveloped signature.
Properties : * Reference : the reference of the signature * SignatureMethod : the signature method url according to the w3 standards.
DigestMethod : the digest method url according to the w3 standards .
By default Trustbuilder includes both PKName and X509Name. In order to exclude one or the other extra attributes can be passed.
excludeKeyName : Exclude the name from the signed element
excludeX509Name : Exclude the x509 certificate from the signed element
excludePKName : Exclude the rsa public key from the signed element
Signing example
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
Reference="#ref20130726"
excludePKName="true"
SignatureMethod="http://www.w3.org/2000/09/xmldsig#rsa-sha256"
DigestMethod="http://www.w3.org/2000/09/xmldsig#sha256"
/>
Response - API
Common Properties
The response API can be applied to the variable specified in the "output property" (see "Workflow Settings"): to verify whether the action performed by the adapter was successful, to query for the data returned by the adapter.
All responses have four properties in common:
status Status flag indicating whether the response is ok (0) or not (1).
substatus Response specific number indicating what the problem was, eg. http status code
message Response specific message in case there was a problem (can be null)
rc Return Code, a human readable code based on the substatus
Adapter Specific Properties
generated boolean flag indicating if the response was a saml response that is being generated or being validated
id ID of the SAML
version Version of SAML
issuer Issuer of the SAML response
subject Subject of the SAML respons
issueInstant Date on which the saml was issued
statusCode Saml StatusCode
size Number of available assertions
destination Destination of the SAML
consent Consent of the SAML response
getSigners() Array of X509Certificate Objects
getAssertions() Array of assertions containing :
id
version
issuer
subject
issueInstant
getSigners() Array of X509Certificate Objects
getAuthnStatements() Array of objects containing
instant date of the instant
notOnOrAfter
index
subjectAddress
subjectDns
authnContextClassRef
getAuthnContextDecl()
getAuthnContextDeclRef()
getAttributes(): Array of objects containing
name
nameFormat
friendlyName
values
Response Codes
If all is ok, the status is zero, for non-zero statusses you can find the description below.
1 = Internal Error
2 = Token is malformed
3 = Not before assertion failed
4 = Not after assertion failed
5 = Invalid signature structure
6 = Invalid signature
7 = Invalid certificate
8 = Status no success
9 = Error decrypting attribute
Additional Notes
Performance remark
When using the SAML adapter the CPU is used more intensive than other adapters for signing the templates. You should test your system to get an idea how many requests can be handled at the same time.
Mixing IBM and SUN JDK's
When using for example TFIM , the IBM JDK encodes the x509 different as the SUN JDK. IBM sets the x509 certificate on 1 line. Sun sets the x509 use lines breaks in the certificate. To solve this issue you have to add an attribute to your Sun JVM Arguments.
-Dorg.apache.xml.security.ignoreLineBreaks=true
Examples
Example Generate
function prepareVerify(workItem) {
var idpslourl = workItem.samlOutput.issuer;
var sp_issuer = workItem.samlOutput.destination;
var xml = tb.templater(SAMLLOGOUTXMLTEMPLATE, {issuer: sp_issuer, destination: idpslourl, requestid: workItem.response.requestId});
workItem.generateInput = tb.samlGenerateRequest(xml);
}
Handle response
function createLogoutResponse(workItem) {
if(workItem.generateOutput.status != 0){
workItem.output = generateError(error.SAML_GENERAL, workItem.lang, {
"Content-Type":"text/html; charset=utf-8"
});
return;
}
workItem.response.body = tb.templater(
SAMLREDIRECTPAGE,
{
form_action: workItem.samlOutput.issuer,
relaystate: "",
samlresponse:workItem.generateOutput.token
}
);
workItem.output=tb.generateResponse(workItem.response.body,workItem.response.headers);
}
Example Validate
function prepareVerify(workItem) {
workItem.samlInput = tb.samlValidateRequest(workItem.input.parameter("SAMLRequest"));
}
Handle Response
function processSamlResponse(workItem) {
try {
log("Saml response: "+workItem.samlOutput);
var username, auditIdentity;
var assertions = workItem.samlOutput.getAssertions();
if(workItem.samlOutput.status !== 0) {
throw new TBError(error.SAML_GENERAL);
}
if(assertions.length > 1) {
throw new TBError(error.SAML_GENERAL);
}
log("issuer: "+assertions[0].issuer);
var attributes = assertions[0].getAttributes();
if(!assertions[0].issuer.equals(server.samlIssuer)) {
throw new TBError(error.SAML_UNKOWN_ISSUER);
}
// Check the attributes.
var attrPrefix = "tagvalue_",
userAttr=[],
groups=[];
for(var key in attributes) {
var attrName = attributes[key].name,
attrVal = [],
values = attributes[key].values;
if(values.length > 1) {
attrVal = [];
for (var value in values) {
attrVal.push(values[value]);
}
} else {
attrVal = attrVal.push(values[0]);
}
}
return true;
} catch(e) {
if(e.status) {
workItem.auditObj.addDetail("Error Code:" + e.status);
}
if(e instanceof TBError){
workItem.output = generateError(e, workItem.lang, {
"Content-Type":"text/html; charset=utf-8"
});
} else if(e instanceof SyntaxError) {
workItem.output = generateError(error.NO_JSON, workItem.lang, {
"Content-Type":"text/html; charset=utf-8"
});
} else {
log("Message: ", e.message);
workItem.output = generateError(error.JS_ERROR, workItem.lang, {
"Content-Type":"text/html; charset=utf-8"
});
}
return false;
}
}