2014-01-22 13 views
14

Ich versuche, einen WCF-Dienst eingerichtet von ADFS geschützt. Ich bin derzeit in der Lage ein Token anzufordern und mit der Bitte WIF und Thinktecture IdentityModel 4.5 mit dem folgenden Code an:Einschließlich SAML2.0-Token in WCF-Serviceanruf ohne WIF

static SecurityToken GetToken() 
{ 
    var factory = new WSTrustChannelFactory(
      new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), 
      "https://fs2.server2012.local/adfs/services/trust/13/usernamemixed") 
    { 
     TrustVersion = TrustVersion.WSTrust13 
    }; 


    if (factory.Credentials != null) 
    { 
     factory.Credentials.UserName.UserName = @"username"; 
     factory.Credentials.UserName.Password = "password"; 
    } 

    var rst = new RequestSecurityToken 
    { 
     RequestType = RequestTypes.Issue, 
     KeyType = KeyTypes.Symmetric, 
     AppliesTo = new EndpointReference(
      "https://wcfservicecertificate/wcfservice/Service.svc/wstrust"), 
    }; 

    var channel = factory.CreateChannel(); 
    RequestSecurityTokenResponse rstr; 
    return channel.Issue(rst, out rstr); 
} 

Damit kann ich den WCF-Dienst aufrufen, indem ChannelFactory.CreateChannelWithIssuedToken mit:

var factory = new ChannelFactory<IService>(binding, 
    new EndpointAddress("https://wcfservicecertificate/wcfservice/Service.svc/wstrust")); 
if (factory.Credentials != null) 
{ 
    factory.Credentials.SupportInteractive = false; 
    factory.Credentials.UseIdentityConfiguration = true; 
} 

var proxy = factory.CreateChannelWithIssuedToken(GetToken()); 
var result= proxy.GetData(2); 

Dies funktioniert wie erwartet, kann aber nur auf (mobilen) Windows-Plattformen verwendet werden. Ich möchte auch das gleiche Prinzip auf iOS und Android verwenden können. Mit this article konnte ich eine Sicherheits-Token von ADFS beantragen mit dem folgenden Code:

const string soapMessage = 
@"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" 
    xmlns:a=""http://www.w3.org/2005/08/addressing"" 
    xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd""> 
    <s:Header> 
     <a:Action s:mustUnderstand=""1"">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action> 
     <a:To s:mustUnderstand=""1"">https://fs2.server2012.local/adfs/services/trust/13/UsernameMixed</a:To> 
     <o:Security s:mustUnderstand=""1"" xmlns:o=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""> 
      <o:UsernameToken u:Id=""uuid-6a13a244-dac6-42c1-84c5-cbb345b0c4c4-1""> 
      <o:Username>username</o:Username> 
      <o:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"">password</o:Password> 
      </o:UsernameToken> 
     </o:Security> 
    </s:Header> 
    <s:Body> 
     <trust:RequestSecurityToken xmlns:trust=""http://docs.oasis-open.org/ws-sx/ws-trust/200512""> 
      <wsp:AppliesTo xmlns:wsp=""http://schemas.xmlsoap.org/ws/2004/09/policy""> 
      <a:EndpointReference> 
       <a:Address>https://wcfservicecertificate/wcfservice/Service.svc/wstrust</a:Address> 
      </a:EndpointReference> 
      </wsp:AppliesTo> 
      <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>       
      <trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType> 
      <trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType> 
     </trust:RequestSecurityToken> 
    </s:Body> 
</s:Envelope>"; 


var webClient = new WebClient(); 

webClient.Headers.Add("Content-Type", "application/soap+xml; charset=utf-8"); 

var result = webClient.UploadString(
     address: "https://fs2.server2012.local/adfs/services/trust/13/UsernameMixed", 
     method: "POST", 
     data: soapMessage); 

Dies führt zu einem SAML2.0 Token, das Ich mag würde in einer Anfrage an unseren WCF-Dienst senden, um zu authentifizieren. Es gibt verschiedene Quellen (einschließlich des oben erwähnten Artikels), die besagen, dass dies möglich sein sollte, aber ich muss noch eine Lösung finden.

Jede Hilfe wäre willkommen.

+0

I Ich kenne die Antwort nicht, aber ich werde mich bald selbst mit diesen Problemen auseinandersetzen müssen. Aus meinen Nachforschungen geht hervor, dass eine präzisere Aussage über das Problem lautet: "Wo in der Nachricht erwartet der WCF-Dienst das Token?". Ich konnte Google noch nicht dazu bringen, mir eine klare Antwort zu dieser Frage zu geben. Viel Glück! –

+0

Nur eine Idee, aber wenn Sie WebClient verwenden, um das SAML-Token zu erhalten, würde ich annehmen, dass Sie WebClient oder einen anderen http-Client verwenden werden, um die Anfrage an den WCF-Endpunkt zu stellen. Wenn dies der Fall ist, können Sie Ihre Arbeits-http-Anfrage (Top-C# -Code) mit einem Tool wie Fiddler überprüfen und dann mit dem WebClient duplizieren. – GemCer

+0

Ich würde vorschlagen, dass Sie OAuth 2 und JWT-Token anstelle von WS-Trust und SAML verwenden. –

Antwort

0

Dies kann leicht ohne Verwendung von WIF erfolgen. Lasse WIF und das .Net-Framework vollständig meiden und mache es in Java zu Illustrationszwecken. Rufen Sie zuerst den Sicherheitstoken-Service unter Verwendung des Vorlagenansatzes auf, wie Sie es getan haben. Sie müssen dann die SAML aus der Antwort extrahieren, Base64 encodieren und in den Autorisierungsheader der nachfolgenden Anfrage an Ihren geschützten WCF-Dienst stopfen. Möglicherweise müssen Sie auch mit einem ProofKey das gleiche tun, wenn Sie für die Nichtabstreitbarkeit kodieren. Außerdem zeige ich nur kurz die Authentifizierung mit Benutzername/Passwort, da die Zertifikatsauthentifizierung viel mehr Arbeit erfordert - Sie müssen einen Hash-Teil (SHA1) der Nachricht verschlüsseln und dann den Hash mit dem privaten Schlüssel des Zertifikats verschlüsseln und dann als XML hinzufügen Element auf die ursprüngliche Nachricht etc ...

hier ist die Java-Helfer-Code:

import java.io.*; 
import java.io.BufferedReader; 
import java.io.DataOutputStream; 
import java.io.InputStreamReader; 
import java.net.HttpURLConnection; 
import java.net.URL; 
import java.time.Instant; 
import java.util.Map; 
import java.util.Properties; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 
import java.util.Base64; 

import javax.crypto.Mac; 
import javax.crypto.spec.SecretKeySpec; 
import javax.net.ssl.HttpsURLConnection; 

public class SecurityService { 

private String _username; 
private String _password; 
private String _stsUrl; 
private String _samlAssertion; 
private String _samlEncoded; 
private String _binarySecret; 
private String _workingDirectory; 
private String _platformUrl; 
private String _soapBody; 
private Integer _responseCode; 
private Integer _plaformResponseCode; 
private String _response; 
private String _platformResponse; 
private String _xproofSignature; 
private Map<String, String> _headerDictionary; 

public void setUsername(String username) { 
    this._username = username; 
} 

public void setPassword(String password) { 
    this._password = password; 
} 

public void setStsUrl(String stsUrl) { 
    this._stsUrl = stsUrl; 
} 

public String getStsUrl() { 
    return _stsUrl; 
} 

public void setplatformUrl(String platformUrl) { 
    this._platformUrl = platformUrl; 
} 

public String getSamlAssertion() { 
    return _samlAssertion; 
} 

public String getSamlEncoded() { 
    return _samlEncoded; 
} 

public String getSoapBody() { 
    return _soapBody; 
} 

public Integer getResponseCode() { 
    return _responseCode; 
} 

public Integer getPlatformResponseCode() { 
    return _plaformResponseCode; 
} 

public String getResponse() { 
    return _response; 
} 

public String getPlatformResponse() { 
    return _platformResponse; 
} 

public String getXProofSignature() { 
    return _xproofSignature; 
} 

public String getBinarySecret() { 
    return _binarySecret; 
} 

public String gePlatFormUrl() { 
    return _platformUrl; 
} 

public void setHeaderDictionary(Map<String, String> headerDictionary){ 
    this._headerDictionary = headerDictionary; 
} 

public Map<String, String> getHeaderDictionary(){ 
    return _headerDictionary; 
} 

public SecurityService() throws Exception { 
} 

public SecurityService(Boolean useConfig) throws Exception { 

    if (useConfig) { 
     this._workingDirectory = System.getProperty("user.dir") + "\\app.config"; 
     this.getProperties(); 
    } 
}  

public void sendAuthenticatedGet() throws Exception { 

    URL obj = new URL(_platformUrl); 
    HttpURLConnection con = (HttpURLConnection) obj.openConnection(); 

    // optional default is GET 
    con.setRequestMethod("GET"); 

    // Add request header   
    con.setRequestProperty("Authorization", "Saml " + _samlEncoded); 
    con.setRequestProperty("X-ProofSignature", _xproofSignature); 

    _plaformResponseCode = con.getResponseCode();  

    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); 
    String inputLine; 
    StringBuffer response = new StringBuffer(); 

    while ((inputLine = in.readLine()) != null) { 
     response.append(inputLine); 
    } 
    in.close(); 

    _platformResponse = response.toString(); 

} 

public void sendAuthenticatedPost(String body) throws Exception { 

    URL obj = new URL(_platformUrl); 
    HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); 

    //add request header 
    con.setRequestMethod("POST"); 
    con.setRequestProperty("Content-Type", "application/json"); 

    // Add request header 
    con.setRequestProperty("Authorization", "Saml " + _samlEncoded); 
    con.setRequestProperty("X-ProofSignature", _xproofSignature); 

    // Add Azure Subscription Key using generic Add Headers method 
    if (_headerDictionary != null) { 
     for (String key : _headerDictionary.keySet()) { 
      con.setRequestProperty(key, _headerDictionary.get(key)); 
     } 
    } 

    _soapBody = body; 

    // Send post request 
    con.setDoOutput(true); 
    DataOutputStream wr = new DataOutputStream(con.getOutputStream()); 
    //wr.writeBytes(urlParameters); 
    wr.writeBytes(_soapBody); 
    wr.flush(); 
    wr.close(); 
    _responseCode = con.getResponseCode(); 

    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); 
    String inputLine; 
    StringBuffer response = new StringBuffer(); 

    while ((inputLine = in.readLine()) != null) { 
     response.append(inputLine); 
    } 
    in.close(); 

    _response = response.toString(); 

} 

// HTTP POST request 
public void sendPostToSts() throws Exception { 

    URL obj = new URL(_stsUrl); 
    HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); 

    //add request header 
    con.setRequestMethod("POST"); 
    con.setRequestProperty("Content-Type", "application/soap+xml"); 

    String body = getTemplateCertificate(); 

    _soapBody = (((body.replace("[Created]", Instant.now().toString())).replace("[Expires]", Instant.now() 
      .plusSeconds(300).toString())).replace("[username]", _username)).replace("[password]", _password).replace("[stsUrl]",    _stsUrl); 

    // Send post request 
    con.setDoOutput(true); 
    DataOutputStream wr = new DataOutputStream(con.getOutputStream()); 
    //wr.writeBytes(urlParameters); 
    wr.writeBytes(_soapBody); 
    wr.flush(); 
    wr.close(); 
    _responseCode = con.getResponseCode(); 

    BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); 
    String inputLine; 
    StringBuffer response = new StringBuffer(); 

    while ((inputLine = in.readLine()) != null) { 
     response.append(inputLine); 
    } 
    in.close(); 

    _response = response.toString(); 
    // Get Binary Secret 
    // <trust:BinarySecret></trust:BinarySecret> 

    final Pattern patternBinarySecret = Pattern.compile("<trust:BinarySecret>(.+?)</trust:BinarySecret>"); 
    final Matcher matcherBinarySecret = patternBinarySecret.matcher(response.toString()); 
    matcherBinarySecret.find(); 

    _binarySecret = matcherBinarySecret.group(1); 

    // Get the SAML Assertion 
    final Pattern patternEncryptedAssertion = Pattern.compile("<trust:RequestedSecurityToken>(.+?)</trust:RequestedSecurityToken>"); 
    final Matcher matcherEncryptedAssertion = patternEncryptedAssertion.matcher(response.toString()); 
    matcherEncryptedAssertion.find(); 
    _samlAssertion = matcherEncryptedAssertion.group(1);   


    byte[] proofKeyBytes = _binarySecret.getBytes("UTF-8"); 
    String encoded = Base64.getEncoder().encodeToString(proofKeyBytes); 
    byte[] decoded = Base64.getDecoder().decode(encoded); 

    // SAML Stuff - Works beautifully 
    byte[] samlBytes = _samlAssertion.getBytes("UTF-8"); 
    _samlEncoded = Base64.getEncoder().encodeToString(samlBytes);  

    _xproofSignature = this.encode(_samlAssertion, _binarySecret); 
} 

private static String readFile(String file) throws IOException { 
    BufferedReader reader = new BufferedReader(new FileReader(file)); 
    String line = null; 
    StringBuilder stringBuilder = new StringBuilder(); 
    String ls = System.getProperty("line.separator"); 

    try { 
     while((line = reader.readLine()) != null) { 
      stringBuilder.append(line); 
      stringBuilder.append(ls); 
     } 

     return stringBuilder.toString(); 
    } finally { 
     reader.close(); 
    } 
} 

// Embedded WS-Trust template for username/password RST 
private static String getTemplate() { 
    return "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\" xmlns:u=    \"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><s:Header><a:Action s:mustUnderstand=    \"1\">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action><a:MessageID>urn:uuid:cfea5555-248c-46c3-9b4d-    54936b7f815c</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To    s:mustUnderstand=\"1\">[stsUrl]</a:To><o:Security s:mustUnderstand=\"1\" xmlns:o=\"http://docs.oasis-    open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"><u:Timestamp u:Id=\"_0\"><u:Created>[Created]    </u:Created><u:Expires>[Expires]</u:Expires></u:Timestamp><o:UsernameToken u:Id=\"uuid-e273c018-1da7-466e-8671-86f6bfe7ce3c-    17\"><o:Username>[username]</o:Username><o:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-    token-profile-1.0#PasswordText\">[password]    </o:Password></o:UsernameToken></o:Security></s:Header><s:Body><trust:RequestSecurityToken xmlns:trust=\"http://docs.oasis-    open.org/ws-sx/ws-trust/200512\"><wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy    \"><wsa:EndpointReference xmlns:wsa=\"http://www.w3.org/2005/08/addressing    \"><wsa:Address>https://mbplatform/</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><trust:RequestType>http://docs.oasis-    open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType><trust:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-    profile-1.1#SAMLV2.0</trust:TokenType></trust:RequestSecurityToken></s:Body></s:Envelope>"; 
}  

private String encode(String key, String data) throws Exception { 
    Mac sha256_HMAC = Mac.getInstance("HmacSHA256"); 
    SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"); 
    sha256_HMAC.init(secret_key); 
    return Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(data.getBytes("UTF-8"))); 
} 

private void getProperties() throws Exception { 
    Properties prop = new Properties(); 
    String fileName = _workingDirectory; 
    InputStream is = new FileInputStream(fileName); 
    prop.load(is); 
    _username = prop.getProperty("app.username"); 
    _password = prop.getProperty("app.password"); 
    _platformUrl = prop.getProperty("app.platformUrl"); 
    _stsUrl = prop.getProperty("app.stsUrl"); 
} 

}

und hier ist zB Nutzung:

SecurityService mbss = new SecurityService(true); 

    mbss.sendPostToSts(); 

    System.out.println("CONTACTING AZURE SECURITY TOKEN SERVICE"); 
    System.out.println("\nSending 'POST' request to URL : " + mbss.getStsUrl()); 
    System.out.println("\nPost parameters : \n" + mbss.getSoapBody()); 
    System.out.println("\nResponse Code : " + mbss.getResponseCode()); 
    System.out.println("\nHERE IS THE SAML RESPONSE\n"); 
    System.out.println(mbss.getResponse()); 
    System.out.println("\nHERE IS THE BINARY SECRET\n"); 
    System.out.println(mbss.getBinarySecret()); 
    System.out.println("\nHERE IS THE SAML ASSERTION\n"); 
    System.out.println(mbss.getSamlAssertion()); 
    System.out.println("\nHERE IS THE ENCODED SAML ASSERTION\n"); 
    System.out.println(mbss.getSamlEncoded()); 
    System.out.println("\nHERE IS THE X-PROOF SIGNATURE\n"); 
    System.out.println(mbss.getXProofSignature()); 
    System.out.println("\nNOW CONTACTING WCF SERVICES WITH SECURITY HEADER\n"); 

    mbss.sendAuthenticatedGet(); 


    System.out.println("\nSending 'GET' request to URL : " + mbss.gePlatFormUrl()); 
    System.out.println("Response Code : " + mbss.getPlatformResponseCode()); 
    System.out.println("\nHERE ARE THE RESULTS FOLKS...ENJOY\n"); 
    System.out.println(mbss.getPlatformResponse());