Wcf 在WinRT中创建服务总线SAS令牌和使用中继

Wcf 在WinRT中创建服务总线SAS令牌和使用中继,wcf,azureservicebus,azure-servicebusrelay,winrt-httpclient,Wcf,Azureservicebus,Azure Servicebusrelay,Winrt Httpclient,我有一个服务总线中继(WCF SOAP),我想在我的Windows应用商店应用中使用。我已经编写了代码来创建令牌以及下面的客户端 问题是,我得到了一个AuthorizationFailedFault,它返回了一个faultstring“InvalidSignature:The token has a invalid signature”。我无法理解它 我的创建令牌方法: private static string CreateSasToken() { TimeSpan sinceEpoc

我有一个服务总线中继(WCF SOAP),我想在我的Windows应用商店应用中使用。我已经编写了代码来创建令牌以及下面的客户端

问题是,我得到了一个AuthorizationFailedFault,它返回了一个faultstring“InvalidSignature:The token has a invalid signature”。我无法理解它

我的创建令牌方法:

private static string CreateSasToken()
{
    TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970,1, 1);
    var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + 3600);
    string stringToSign = webUtility.UrlEncode(ServiceUri.AbsoluteUri) + "\n" + expiry;

    string hashKey = Encoding.UTF8.GetBytes(Secret).ToString();

    MacAlgorithmProvider macAlgorithmProvider = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
    BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;

    var messageBuffer = CryptographicBuffer.ConvertStringToBinary(stringToSign,encoding);
    IBuffer keyBuffer = CryptographicBuffer.ConvertStringToBinary(hashKey,encoding);

    CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
    IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);

    string signature = CryptographicBuffer.EncodeToBase64String(signedMessage);

    var sasToken = String.Format(CultureInfo.InvariantCulture,
        "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
        WebUtility.UrlEncode(ServiceUri.AbsoluteUri),
        WebUtility.UrlEncode(signature), expiry, Issuer);

    return sasToken;
}
我的客户端类:

    public partial class ServiceClient
    {
        public async Task<string> GetDataUsingDataContract(string item, string sasToken)
        {

            HttpClient client = new HttpClient();

            client.DefaultRequestHeaders.Add("ServiceBusAuthorization",sasToken);
            client.DefaultRequestHeaders.Add("SOAPAction",".../GetDataUsingDataContract");
            client.DefaultRequestHeaders.Add("Host", "xxxxxxxxxxx.servicebus.windows.net");

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,ServiceUri);

            var content =new StringContent(@"<s:Envelope
                xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
                <s:Header></s:Header><s:Body>"+ item +@"</s:Body>
                </s:Envelope>",System.Text.Encoding.UTF8,"application/xml");
            request.Content = content;

            HttpResponseMessage wcfResponse = client.SendAsync(request).Result;
            HttpContent stream = wcfResponse.Content;

            var response = stream.ReadAsStringAsync();
            var returnPacket = response.Result;

            return returnPacket;
        }
    }
公共部分类ServiceClient
{
公共异步任务GetDataUsingDataContract(字符串项,字符串标记)
{
HttpClient=新的HttpClient();
client.DefaultRequestHeaders.Add(“ServiceBusAuthorization”,sasToken);
client.DefaultRequestHeaders.Add(“SOAPAction”,“../GetDataUsingDataContract”);
client.DefaultRequestHeaders.Add(“主机”,“xxxxxxxxxx.servicebus.windows.net”);
HttpRequestMessage请求=新的HttpRequestMessage(HttpMethod.Post,ServiceUri);
var content=新的StringContent(@“
“+项目+@”
,System.Text.Encoding.UTF8,“application/xml”);
request.Content=内容;
HttpResponseMessage wcfResponse=client.SendAsync(请求).Result;
HttpContent流=wcfResponse.Content;
var response=stream.ReadAsStringAsync();
var returnPacket=response.Result;
返回包;
}
}

通过在控制台应用程序中复制由Micorosft.ServiceBus创建的未过期令牌,我已成功使用Http(通过Fiddler)使用中继。

我找到了一个解决方案,该解决方案涉及两种方法都是错误的

CreateSasToken方法:

一个小的更改涉及将hashKey变量设置为byte[]而不是string。这一行:
string hashKey=Encoding.UTF8.GetBytes(Secret.ToString()
改为:
var hashKey=Encoding.UTF8.GetBytes(Secret)

这个改变意味着我需要使用不同的方法来设置keyBuffer。 这一行:
IBuffer-keyBuffer=cryptographicsbuffer.ConvertStringToBinary(hashKey,编码)
更改为:
IBuffer keyBuffer=cryptographicsbuffer.CreateFromByteArray(hashKey)

因此,新的CreateSasToken方法是:

    private static string GetSasToken()
    {
        TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
        var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + 3600);
        string stringToSign = WebUtility.UrlEncode(ServiceUri.AbsoluteUri) + "\n" + expiry;

        var hashKey = Encoding.UTF8.GetBytes(Secret);

        MacAlgorithmProvider macAlgorithmProvider =
            MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
        const BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
        var messageBuffer = CryptographicBuffer.ConvertStringToBinary(stringToSign,
            encoding);

        IBuffer keyBuffer = CryptographicBuffer.CreateFromByteArray(hashKey);
        CryptographicKey hmacKey = macAlgorithmProvider.CreateKey(keyBuffer);
        IBuffer signedMessage = CryptographicEngine.Sign(hmacKey, messageBuffer);

        string signature = CryptographicBuffer.EncodeToBase64String(signedMessage);

        var sasToken = String.Format(CultureInfo.InvariantCulture,
            "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
            WebUtility.UrlEncode(ServiceUri.AbsoluteUri),
            WebUtility.UrlEncode(signature),
            expiry, Issuer);

        return sasToken;
    }
服务客户端类

这里有几件事需要注意

  • 为了使请求工作,必须将SAS令牌作为AuthenticationValueHeader对象的参数添加到标头中。因此,我向助手类(ServiceBusHelper)添加了以下方法,该类将Key、KeyName和SasToken作为属性,将CreateSasToken作为方法

    public static AuthenticationHeaderValue CreateBasicHeader()
    {
        return new AuthenticationHeaderValue("Basic", SasToken);
    }
    
  • HttpRequestMessage内容属性必须以特殊方式创建。接受传入的item参数,这是一个序列化的WCF数据契约类型,我需要做一些事情来制作SOAP信封。这里不是详细介绍它们,而是整个类(仅一个方法)。我将对代码进行注释,以立即处理响应

    public partial class SalesNotifyServiceClient
    {
        public async Task<string> GetDataUsingDataContract(string item)
        {
            string returnPacket = "";
            string element = "";
            try
            {
                HttpClient client = new HttpClient();
    
                client.DefaultRequestHeaders.Add("ServiceBusAuthorization",
                    ServiceBusHelper.CreateBasicHeader().Parameter);
                client.DefaultRequestHeaders.Add("SOAPAction",
                    ".../GetDataUsingDataContract");
                client.DefaultRequestHeaders.Add("Host",
                    "xxxxxxxxxx.servicebus.windows.net");
    
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,
                    ServiceBusHelper.ServiceUri);
    
                //Creating the request.Content
                var encodedItem = item.Replace("<", "&lt;").Replace(">", "&gt;");
    
                var strRequest =
                    @"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"">
                    <s:Header></s:Header><s:Body><GetDataUsingDataContract xmlns=
                    ""http://www.xxxxxxxxxx.com/servicemodel/relay""><item>" +
                    encodedItem + 
                    @"</item></GetDataUsingDataContract></s:Body></s:Envelope>";
    
                var content = new StringContent(strRequest,
                    System.Text.Encoding.UTF8, "application/xml");
    
                request.Content = content;
    
                HttpResponseMessage wcfResponse = client.SendAsync(request).Result;
                HttpContent stream = wcfResponse.Content;
    
                var response = await stream.ReadAsStringAsync();
    
                //Handling the response
                XDocument doc;
                using (StringReader s = new StringReader(response))
                {
                    doc = XDocument.Load(s);
                }
    
                if (doc.Root != null)
                {
                    element = doc.Root.Value;
                }
    
                returnPacket = element;
            }
            catch (Exception e)
            {
                var message = e.Message;
            }
    
            return returnPacket;
        }
    }
    
    公共部分类SalesNotifyServiceClient
    {
    公共异步任务GetDataUsingDataContract(字符串项)
    {
    字符串returnPacket=“”;
    字符串元素=”;
    尝试
    {
    HttpClient=新的HttpClient();
    client.DefaultRequestHeaders.Add(“ServiceBusAuthorization”,
    ServiceBusHelper.CreateBasicHeader().Parameter);
    client.DefaultRequestHeaders.Add(“SOAPAction”,
    “../GetDataUsingDataContract”);
    client.DefaultRequestHeaders.Add(“主机”,
    “xxxxxxxxx.servicebus.windows.net”);
    HttpRequestMessage请求=新的HttpRequestMessage(HttpMethod.Post,
    ServiceBusHelper.ServiceUri);
    //创建request.Content
    var encodedItem=item.Replace(“,”);
    var Strequest=
    @"
    " +
    encodedItem+
    @"";
    var内容=新的StringContent(strRequest,
    System.Text.Encoding.UTF8,“应用程序/xml”);
    request.Content=内容;
    HttpResponseMessage wcfResponse=client.SendAsync(请求).Result;
    HttpContent流=wcfResponse.Content;
    var response=wait stream.ReadAsStringAsync();
    //处理回应
    XDocument文件;
    使用(StringReader s=新StringReader(响应))
    {
    doc=XDocument.Load(s);
    }
    如果(doc.Root!=null)
    {
    元素=doc.Root.Value;
    }
    returnPacket=元素;
    }
    捕获(例外e)
    {
    var消息=e.消息;
    }
    返回包;
    }
    }
    
  • 为了得到DataContract对象,我必须对响应字符串做一些事情。正如您在上面处理响应的
    //注释中所看到的,我使用StringReader将返回的SOAP信封作为字符串加载到XDocument中,根值是我的序列化DataContract对象。然后,我反序列化了从方法had my response对象返回的returnPacket变量