Requesting a Token from Access Control Service in Java
[UPDATE 2/11: Updated to use new STS V0.9 instead of V0.8]
Following demonstrates requesting a token from the .NET Services Access Control Services using a Shared Secret and another using a Simple Web Token.
You'll also need these three libraries to help in the encoding process and the HTTP calls. There are some hacky pieces of code here, but that's mostly due to my ignorance in Java. Feedback to make this better quality code is welcome.
https://commons.apache.org/codec/
https://commons.apache.org/lang/
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
public class RequestACSToken {
private static final String HMAC_SHA256 = "HmacSHA256";
public static void main(String[] args) throws ClientProtocolException, NoSuchAlgorithmException, IllegalStateException, IOException, Exception
{
String stsUrl="https://[service namespace].accesscontrol.windows.net/WRAPv0.9/";
String rpUrl="[scope applies_to]";
String issuerKey="[issuer key]";
String issuerName="[issuer name]";
List<NameValuePair> claimSetSharedSecret = new ArrayList<NameValuePair>();
claimSetSharedSecret.add(new BasicNameValuePair("sample_in_claim_type","sample_in_claim_value"));
String tokenSharedSecret=GetTokenBySharedSecret(stsUrl,claimSetSharedSecret,issuerName,issuerKey,rpUrl);
System.out.println("Shared Secret: " + tokenSharedSecret);
List<NameValuePair> claimSetSimpleWebToken = new ArrayList<NameValuePair>();
claimSetSimpleWebToken.add(new BasicNameValuePair("sample_in_claim_type","sample_in_claim_value"));
String tokenSimpleWebToken=GetTokenBySimpleWebToken(stsUrl,claimSetSimpleWebToken,issuerName,issuerKey,rpUrl);
System.out.println("Simple Web Token: " + tokenSimpleWebToken);
}
public static String ExtractTokenFromResponse(String stringResponse)
{
String[] returnClaimSet=stringResponse.split("&");
for(int i=0; i<returnClaimSet.length; i++)
{
String[] claimItem=returnClaimSet[i].split("=");
if(claimItem[0].equals("wrap_token"))
{
return claimItem[1];
}
}
// this is an error case, but the body does contain the error details so useful to display
return stringResponse;
}
public static String MakeSTSRequest(List<NameValuePair> claimSet, String stsUrl) throws ClientProtocolException, IOException
{
// encode the claim set
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(claimSet, "UTF-8");
// make the request to the STS
HttpPost httpPost = new HttpPost(stsUrl);
httpPost.setEntity(entity);
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(httpPost);
// capture the response into a string
HttpEntity responseEntity = response.getEntity();
String stringResponse = entity!=null ? EntityUtils.toString(responseEntity) : "";
return stringResponse;
}
public static String GetTokenBySharedSecret(String stsUrl, List<NameValuePair> claimSet, String issuerName, String issuerKey, String rpUrl) throws ClientProtocolException, IOException
{
claimSet.add(new BasicNameValuePair("wrap_name",issuerName));
claimSet.add(new BasicNameValuePair("wrap_password",issuerKey));
claimSet.add(new BasicNameValuePair("wrap_scope",rpUrl));
String stringResponse=MakeSTSRequest(claimSet,stsUrl);
// extract the value for wrap_token and return
return ExtractTokenFromResponse(stringResponse);
}
public static String GetTokenBySimpleWebToken(String stsUrl, List<NameValuePair> claimSet, String issuerName, String issuerKey, String rpUrl) throws ClientProtocolException, IOException, Exception, NoSuchAlgorithmException, IllegalStateException
{
claimSet.add(new BasicNameValuePair("Issuer",issuerName));
claimSet.add(new BasicNameValuePair("Audience",stsUrl));
claimSet.add(new BasicNameValuePair("HMACSHA256",CreateSignature(EncodeQueryString(claimSet),issuerKey)));
List<NameValuePair> newClaimSet = new ArrayList<NameValuePair>();
String wrapToken=EncodeQueryString(claimSet);
newClaimSet.add(new BasicNameValuePair("wrap_assertion",wrapToken));
newClaimSet.add(new BasicNameValuePair("wrap_assertion_format","SWT"));
newClaimSet.add(new BasicNameValuePair("wrap_scope",rpUrl));
String stringResponse=MakeSTSRequest(newClaimSet,stsUrl);
// extract the value for wrap_token and return
return ExtractTokenFromResponse(stringResponse);
}
public static String CreateSignature(String hmacFreeClaimSet, String key) throws NoSuchAlgorithmException, InvalidKeyException, IllegalStateException, UnsupportedEncodingException
{
SecretKeySpec signingKey = new SecretKeySpec(Base64.decodeBase64(key), HMAC_SHA256);
Mac mac = Mac.getInstance(HMAC_SHA256);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(hmacFreeClaimSet.getBytes("ASCII"));
return Base64.encodeBase64String(rawHmac);
}
public static String EncodeQueryString(List<NameValuePair> claimSet) throws UnsupportedEncodingException
{
List<String> claims = new ArrayList<String>();
for(Iterator<NameValuePair> i = claimSet.iterator(); i.hasNext();){
NameValuePair item = (NameValuePair)i.next();
claims.add(item.getName() + "=" + URLEncoder.encode(item.getValue().trim(),"UTF-8"));}
return StringUtils.join(claims,"&");
}