Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在WCF服务调用中包含SAML2.0令牌而不使用WIF_C#_Wcf_.net 4.5_Saml 2.0_Adfs - Fatal编程技术网

C# 在WCF服务调用中包含SAML2.0令牌而不使用WIF

C# 在WCF服务调用中包含SAML2.0令牌而不使用WIF,c#,wcf,.net-4.5,saml-2.0,adfs,C#,Wcf,.net 4.5,Saml 2.0,Adfs,我正在尝试设置受ADFS保护的WCF服务。我目前可以使用WIF和Thinktecture IdentityModel 4.5请求令牌并随请求一起发送,代码如下: static SecurityToken GetToken() { var factory = new WSTrustChannelFactory( new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),

我正在尝试设置受
ADFS
保护的
WCF
服务。我目前可以使用
WIF
Thinktecture IdentityModel 4.5
请求令牌并随请求一起发送,代码如下:

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);
}
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);
有了它,我可以使用
ChannelFactory.CreateChannelWithIssuedToken调用WCF服务:

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);
var工厂=新的ChannelFactory(绑定,
新端点地址(“https://wcfservicecertificate/wcfservice/Service.svc/wstrust"));
如果(factory.Credentials!=null)
{
factory.Credentials.SupportInteractive=false;
factory.Credentials.UseIdentityConfiguration=true;
}
var proxy=factory.CreateChannelWithIssuedToken(GetToken());
var result=proxy.GetData(2);
这与预期一样有效,但只能在(移动)windows平台上使用。我也希望能够在iOS和Android上使用相同的原则。使用以下代码,我能够从ADFS请求安全令牌:

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);
}
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);
const字符串soapMessage=
@"
http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue
https://fs2.server2012.local/adfs/services/trust/13/UsernameMixed
用户名
密码
https://wcfservicecertificate/wcfservice/Service.svc/wstrust
http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey                        
http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue
urn:oasis:names:tc:SAML:2.0:assertion
";
var webClient=新的webClient();
Add(“内容类型”,“应用程序/soap+xml;字符集=utf-8”);
var result=webClient.UploadString(
地址:“https://fs2.server2012.local/adfs/services/trust/13/UsernameMixed",
方法:“张贴”,
数据:soapMessage);
这将产生一个SAML2.0令牌,我希望将该令牌发送到我们的WCF服务中,以便进行身份验证。有各种来源(包括前面提到的文章)表明这应该是可能的,但我还没有找到解决方案


任何帮助都将不胜感激。

您可以使用一种混合解决方案,该解决方案将SAML与OAuth或其他授权技术结合使用。这对网络钓鱼技术更安全。对于仅SAML方法,您可以参考以下链接:。据说您需要在webconfig上启用
saveBootstrapTokens
属性


此链接也很有用:

无需使用WIF即可轻松完成此操作。为了便于说明,让我们完全避免WIF和.Net框架,而使用Java来实现它。首先,像您所做的那样,使用模板方法调用安全令牌服务。然后,您需要从响应中提取SAML,对其进行Base64编码,并将其填充到后续请求到受保护WCF服务的自动化头中。如果您正在为不可否认性编码,那么您可能还需要对ProofKey执行相同的操作。此外,为了简洁起见,我只展示了使用用户名/密码的身份验证,因为证书身份验证需要更多的工作-您必须散列(SHA1)消息的一部分,然后使用证书的私钥加密散列,然后将其作为xml元素添加到原始消息中,等等

以下是java帮助程序代码:

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");
}

我不知道这个问题的答案,但我很快就要自己解决这些问题,所以我把它作为一个学习练习。根据我的研究,似乎对这个问题更准确的说法是“WCF服务希望令牌在消息中的什么位置?”。我还没能让谷歌在这个问题上给我一个直接的答案。祝你好运这只是一个想法,但如果您使用WebClient获取SAML令牌,我会假设您将使用WebClient或其他http客户端向WCF端点发出请求。如果这是真的,您可以使用像Fiddler这样的工具检查您的工作HTTP请求(顶C代码),然后使用WebCube复制它。我建议您考虑使用OAuth2和JWT令牌而不是WS-Trand和SAML。