<?php /* * COAUTHOR: Samuel Muñoz Hidalgo * EMAIL: samuel.mh@gmail.com * LAST REVISION: 13-FEB-09 * DESCRIPTION: Things the STS can do * - InfoCard issue * - Error response (if the user send us wrong credentials) * - Request Security Token Response */ class sspmod_InfoCard_STS { /* * USED IN: www/getcardform.php * INPUT: data and configuration * OUTPUT; a custom error message for the identity selector */ static public function createCard($ICdata,$ICconfig) { $infocardbuf = '<Object Id="IC01" xmlns="http://www.w3.org/2000/09/xmldsig#">'; $infocardbuf .= '<InformationCard xml:lang="en-us" xmlns="http://schemas.xmlsoap.org/ws/2005/05/identity" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex">'; //cardId $infocardbuf .= '<InformationCardReference>'; $infocardbuf .= '<CardId>'.$ICdata['CardId'].'</CardId>'; //xs:anyURI cardId (="$cardurl/$ppid"; $ppid = "$uname-" . time();) $infocardbuf .= '<CardVersion>1</CardVersion>'; //xs:unsignedInt $infocardbuf .= '</InformationCardReference>'; //cardName $infocardbuf .= '<CardName>'.$ICdata['CardName'].'</CardName>'; //image $infocardbuf .= '<CardImage MimeType="'.mime_content_type($ICdata['CardImage']).'">'; $infocardbuf .= base64_encode(file_get_contents($ICdata['CardImage'])); $infocardbuf .= '</CardImage>'; //issuer - times $infocardbuf .= '<Issuer>'.$ICconfig['InfoCard']['issuer'].'</Issuer>'; $infocardbuf .= '<TimeIssued>'.gmdate('Y-m-d').'T'.gmdate('H:i:s').'Z'.'</TimeIssued>'; $infocardbuf .= '<TimeExpires>'.$ICdata['TimeExpires'].'</TimeExpires>'; //Token Service List $infocardbuf .= '<TokenServiceList>'; $infocardbuf .= '<TokenService>'; $infocardbuf .= '<wsa:EndpointReference>'; $infocardbuf .= '<wsa:Address>'.$ICconfig['tokenserviceurl'].'</wsa:Address>'; $infocardbuf .= '<wsa:Metadata>'; $infocardbuf .= '<wsx:Metadata>'; $infocardbuf .= '<wsx:MetadataSection>'; $infocardbuf .= '<wsx:MetadataReference>'; $infocardbuf .= '<wsa:Address>'.$ICconfig['mexurl'].'</wsa:Address>'; $infocardbuf .= '</wsx:MetadataReference>'; $infocardbuf .= '</wsx:MetadataSection>'; $infocardbuf .= '</wsx:Metadata>'; $infocardbuf .= '</wsa:Metadata>'; $infocardbuf .= '</wsa:EndpointReference>'; /*Types of User Credentials * Supported: UsernamePasswordCredential, SelfIssuedCredential * Unsupported: KerberosV5Credential, X509V3Credential */ $infocardbuf .= '<UserCredential>'; $infocardbuf .= '<DisplayCredentialHint>'.$ICdata['DisplayCredentialHint'].'</DisplayCredentialHint>'; switch($ICconfig['UserCredential']){ case 'UsernamePasswordCredential': $infocardbuf .= '<UsernamePasswordCredential>'; $infocardbuf .= '<Username>'.$ICdata['UserName'].'</Username>'; $infocardbuf .= '</UsernamePasswordCredential>'; break; case 'KerberosV5Credential': $infocardbuf .= '<KerberosV5Credential/>'; break; case 'X509V3Credential': $infocardbuf .= '<X509V3Credential>'; $infocardbuf .= '<ds:X509Data>'; $infocardbuf .= '<wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/2004/xx/oasis-2004xx-wss-soap-message-security-1.1#ThumbprintSHA1" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis200401-wss-soap-message-security-1.0#Base64Binary">'; /*This element provides a key identifier for the X.509 certificate based on the SHA1 hash of the entire certificate content expressed as a “thumbprint.” Note that the extensibility point in the ds:X509Data element is used to add wsse:KeyIdentifier as a child element.*/ $infocardbuf .= $ICdata['KeyIdentifier']; //xs:base64binary; $infocardbuf .= '</wsse:KeyIdentifier>'; $infocardbuf .= '</ds:X509Data>'; $infocardbuf .= '</X509V3Credential>'; break; case 'SelfIssuedCredential': $infocardbuf .= '<SelfIssuedCredential>'; $infocardbuf .= '<PrivatePersonalIdentifier>'; $infocardbuf .= $ICdata['PPID']; //xs:base64binary; $infocardbuf .= '</PrivatePersonalIdentifier>'; $infocardbuf .= '</SelfIssuedCredential> '; break; default: break; } $infocardbuf .= '</UserCredential>'; $infocardbuf .= '</TokenService>'; $infocardbuf .= '</TokenServiceList>'; //Tokentype $infocardbuf .= '<SupportedTokenTypeList>'; $infocardbuf .= '<wst:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</wst:TokenType>'; $infocardbuf .= '</SupportedTokenTypeList>'; //Claims $infocardbuf .= '<SupportedClaimTypeList>'; $url = $ICconfig['InfoCard']['schema'].'/claims/'; foreach ($ICconfig['InfoCard']['requiredClaims'] as $claim=>$data) { $infocardbuf .= '<SupportedClaimType Uri="'.$url.$claim.'">'; $infocardbuf .= '<DisplayTag>'.$data['displayTag'].'</DisplayTag>'; $infocardbuf .= '<Description>'.$data['description'].'</Description>'; $infocardbuf .= '</SupportedClaimType>'; } foreach ($ICconfig['InfoCard']['optionalClaims'] as $claim=>$data) { $infocardbuf .= '<SupportedClaimType Uri="'.$url.$claim.'">'; $infocardbuf .= '<DisplayTag>'.$data['displayTag'].'</DisplayTag>'; $infocardbuf .= '<Description>'.$data['description'].'</Description>'; $infocardbuf .= '</SupportedClaimType>'; } $infocardbuf .= '</SupportedClaimTypeList>'; //Privacy URL $infocardbuf .= '<PrivacyNotice>'.$ICconfig['InfoCard']['privacyURL'].'</PrivacyNotice>'; $infocardbuf .= '</InformationCard>'; $infocardbuf .= '</Object>'; $canonicalbuf = sspmod_InfoCard_Utils::canonicalize($infocardbuf); //construct a SignedInfo block $signedinfo = '<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">'; $signedinfo .= '<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>'; $signedinfo .= '<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>'; $signedinfo .= '<Reference URI="#IC01">'; $signedinfo .= '<Transforms>'; $signedinfo .= '<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>'; $signedinfo .= '</Transforms>'; $signedinfo .= '<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>'; $signedinfo .= '<DigestValue>'.base64_encode(sha1($canonicalbuf, TRUE)).'</DigestValue>'; $signedinfo .= '</Reference>'; $signedinfo .= '</SignedInfo>'; $canonicalbuf = sspmod_InfoCard_Utils::canonicalize($signedinfo); $signature = ''; $privkey = openssl_pkey_get_private(file_get_contents($ICconfig['sts_key'])); openssl_sign($canonicalbuf, $signature, $privkey); openssl_free_key($privkey); $infocard_signature = base64_encode($signature); //Envelope $buf = '<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">'; $buf .= $signedinfo; $buf .= '<SignatureValue>'.$infocard_signature.'</SignatureValue>'; $buf .= '<KeyInfo>'; $buf .= '<X509Data>'; // signing certificate(s) foreach ($ICconfig['certificates'] as $idx=>$cert) $buf .= '<X509Certificate>'.sspmod_InfoCard_Utils::takeCert($cert).'</X509Certificate>'; $buf .= '</X509Data>'; $buf .= '</KeyInfo>'; $buf .= $infocardbuf; $buf .= '</Signature>'; return $buf; } /* * USED IN: www/tokenservice.php * INPUT: error message, uuid of the RST * OUTPUT; a custom error message for the identity selector */ static public function errorMessage($msg,$relatesto){ $buf = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">'; $buf .= '<s:Header>'; $buf .= '<a:Action s:mustUnderstand="1">http://www.w3.org/2005/08/addressing/soap/fault</a:Action>'; $buf .= '<a:RelatesTo>'.$relatesto.'</a:RelatesTo>'; $buf .= '</s:Header>'; $buf .= '<s:Body>'; $buf .= '<s:Fault>'; $buf .= '<s:Code>'; $buf .= '<s:Value xmlns:a="http://www.w3.org/2003/05/soap-envelope">'; $buf .= 'a:Sender'; $buf .= '</s:Value>'; $buf .= '<s:Subcode>'; $buf .= '<s:Value xmlns:a="http://schemas.xmlsoap.org/ws/2005/05/identity">'; $buf .= 'a:MissingAppliesTo'; $buf .= '</s:Value>'; $buf .= '</s:Subcode>'; $buf .= '</s:Code>'; $buf .= '<s:Reason>'; $buf .= '<s:Text xml:lang="en">'; $buf .= $msg; $buf .= '</s:Text>'; $buf .= '</s:Reason>'; $buf .= '</s:Fault>'; $buf .= '</s:Body>'; $buf .= '</s:Envelope>'; return $buf; } /* * USED IN: www/tokenservice.php * INPUT: claims value, configuration, uuid of the RST * OUTPUT; a security token for the identity selector */ static public function createToken($claimValues,$config,$relatesto){ $assertionid = uniqid('uuid-'); $created = gmdate('Y-m-d').'T'.gmdate('H:i:s').'Z'; $expires = gmdate('Y-m-d', time()+3600).'T'.gmdate('H:i:s', time()+3600).'Z'; //SOAP ENVELOPE $env = '<?xml version="1.0"?>'; $env .= '<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ic="http://schemas.xmlsoap.org/ws/2005/05/identity" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:xenc="http://www.w3.org/2001/04/xmlenc" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">'; $env .= '<S:Header>'; $env .= '<wsa:Action wsu:Id="_1">'; $env .= 'http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue'; $env .= '</wsa:Action>'; $env .= '<wsa:RelatesTo wsu:Id="_2">'; $env .= $relatesto; $env .= '</wsa:RelatesTo>'; $env .= '<wsa:To wsu:id="_3">'; $env .= 'http://www.w3.org/2005/08/addressing/anonymous'; $env .= '</wsa:To>'; $env .= '<wsse:Security S:mustUnderstand="1">'; $env .= '<wsu:Timestamp wsu:Id="_6">'; $env .= '<wsu:Created>'.$created.'</wsu:Created>'; $env .= '<wsu:Expires>'.$expires.'</wsu:Expires>'; $env .= '</wsu:Timestamp>'; $env .= '</wsse:Security>'; $env .= '</S:Header>'; $env .= '<S:Body wsu:Id="_10">'; //RequestSecurityTokenResponse $env .= sspmod_InfoCard_STS::RequestSecurityTokenResponse($claimValues,$config,$assertionid,$created,$expires); $env .= '</S:Body>'; $env .= '</S:Envelope>'; return $env; } /* * USED IN: createToken * INPUT: claims value, configuration, uuid, times * OUTPUT; returns the <wst:RequestSecurityTokenResponse>' of the RSTR */ static private function RequestSecurityTokenResponse ($claimValues,$config,$assertionid,$created,$expires){ $tr = '<wst:RequestSecurityTokenResponse>'; $tr .= '<wst:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</wst:TokenType>'; $tr .= '<wst:LifeTime>'; $tr .= '<wsu:Created>'.$created.'</wsu:Created>'; $tr .= '<wsu:Expires>'.$expires.'</wsu:Expires>'; $tr .= '</wst:LifeTime>'; //Encrypted token: SAML assertion $tr .= '<wst:RequestedSecurityToken>'; $tr .= sspmod_InfoCard_STS::saml_assertion($claimValues,$config,$assertionid,$created,$expires); $tr .= '</wst:RequestedSecurityToken>'; //RequestedAattachedReference $tr .= '<wst:RequestedAttachedReference>'; $tr .= '<wsse:SecurityTokenReference>'; $tr .= '<wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">'; $tr .= $assertionid; $tr .= '</wsse:KeyIdentifier>'; $tr .= '</wsse:SecurityTokenReference>'; $tr .= '</wst:RequestedAttachedReference>'; //RequestedUnattachedReference $tr .= '<wst:RequestedUnattachedReference>'; $tr .= '<wsse:SecurityTokenReference>'; $tr .= '<wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">'; $tr .= $assertionid; $tr .= '</wsse:KeyIdentifier>'; $tr .= '</wsse:SecurityTokenReference>'; $tr .= '</wst:RequestedUnattachedReference>'; //RequestedDisplayToken $tr .= '<ic:RequestedDisplayToken>'; $tr .= '<ic:DisplayToken xml:lang="en-us">'; foreach ($claimValues as $claim=>$data) { $tr .= '<ic:DisplayClaim Uri="'.$config['InfoCard']['schema'].'/claims/'.$claim.'">'; $tr .= '<ic:DisplayTag>'.$data['displayTag'].'</ic:DisplayTag>'; $tr .= '<ic:DisplayValue>'.$data['value'].'</ic:DisplayValue>'; $tr .= "</ic:DisplayClaim>"; } $tr .= '</ic:DisplayToken>'; $tr .= '</ic:RequestedDisplayToken>'; $tr .= '</wst:RequestSecurityTokenResponse>'; return $tr; } /* * USED IN: RequestSecurityTokenResponse * INPUT: claims value, configuration, uuid, times * OUTPUT; STS Signed SAML assertion */ static private function saml_assertion($claimValues,$config,$assertionid,$created,$expires){ $saml = '<saml:Assertion MajorVersion="1" MinorVersion="0" AssertionID="'.$assertionid.'" Issuer="'.$config['issuer'].'" IssueInstant="'.$created.'" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">'; $saml .= '<saml:Conditions NotBefore="'.$created.'" NotOnOrAfter="'.$expires.'" />'; $saml .= '<saml:AttributeStatement>'; $saml .= '<saml:Subject>'; $saml .= '<saml:SubjectConfirmation>'; $saml .= '<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:holder-of-key</saml:ConfirmationMethod>'; // proof key $saml .= '<dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">'; $saml .= '<dsig:X509Data>'; $saml .= '<dsig:X509Certificate>'.sspmod_InfoCard_Utils::takeCert($config['sts_crt']).'</dsig:X509Certificate>'; $saml .= '</dsig:X509Data>'; $saml .= '</dsig:KeyInfo>'; $saml .= '</saml:SubjectConfirmation>'; $saml .= '</saml:Subject>'; foreach ($claimValues as $claim=>$data) { $saml .= '<saml:Attribute AttributeName="'.$claim.'" AttributeNamespace="'.$config['InfoCard']['schema'].'/claims">'; $saml .= '<saml:AttributeValue>'.$data['value'].'</saml:AttributeValue>'; $saml .= '</saml:Attribute>'; } $saml .= '</saml:AttributeStatement>'; //Pure SAML Assertion digest $canonicalbuf = sspmod_InfoCard_Utils::canonicalize($saml.'</saml:Assertion>'); $myhash = sha1($canonicalbuf,TRUE); $samldigest = base64_encode($myhash); //Digest block $signedinfo = '<dsig:SignedInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" >'; $signedinfo .= '<dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />'; $signedinfo .= '<dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />'; $signedinfo .= '<dsig:Reference URI="#'.$assertionid.'">'; $signedinfo .= '<dsig:Transforms>'; $signedinfo .= '<dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />'; $signedinfo .= '<dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />'; $signedinfo .= '</dsig:Transforms>'; $signedinfo .= '<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />'; $signedinfo .= '<dsig:DigestValue>'.$samldigest.'</dsig:DigestValue>'; $signedinfo .= '</dsig:Reference>'; $signedinfo .= '</dsig:SignedInfo>'; //Signature of the digest $canonicalbuf = sspmod_InfoCard_Utils::canonicalize($signedinfo); $privkey = openssl_pkey_get_private(file_get_contents($config['sts_key'])); $signature = ''; openssl_sign($canonicalbuf, $signature, $privkey); openssl_free_key($privkey); $samlsignature = base64_encode($signature); //Signature block $saml .= '<dsig:Signature xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">'; $saml .= $signedinfo; $saml .= '<dsig:SignatureValue>'.$samlsignature.'</dsig:SignatureValue>'; $saml .= '<dsig:KeyInfo>'; $saml .= '<dsig:X509Data>'; $saml .= '<dsig:X509Certificate>'.sspmod_InfoCard_Utils::takeCert($config['sts_crt']).'</dsig:X509Certificate>'; $saml .= '</dsig:X509Data>'; $saml .= '</dsig:KeyInfo>'; $saml .= '</dsig:Signature>'; $saml .= '</saml:Assertion>'; return $saml; } }