这是我在网上的第一个问题。希望它有意义。
我在网上看到了几个与此问题相关的博客,我尝试了一些没有成功的想法。这是我的情况:
我有一个Web应用程序调用WCF Web服务,然后调用Java Web服务。它们都在不同的服务器上。 WCF Web服务与java Web服务之间的调用未通过https,因为证书足以识别调用者(因此消息安全性)。
Java Web服务需要收到已签名的邮件,并按以下方式工作:
在处理每个请求之前,处理程序拦截所有传入的消息并执行以下验证规则:
1.邮件是否包含安全标头
2.邮件是否包含正确的安全标头ID
3.邮件是否已正确签名
4.邮件是否包含KeyInfo x.509证书
5.证书是否从受信任的CA - 基于配置发出
6.证书是否有效(未过期,已撤销)
7.证书是否包含正确的策略OID
确认所有这些步骤后,可以处理该消息,如果任何步骤失败,则将返回soap消息异常。
SOAP安全标头应根据xxx ... w3.org/TR/SOAP-dsig/数字签名规范进行验证。
最完整的描述可以在这里找到xxx ... ibm.com/developerworks/webservices/library/ws-security.html这篇IBM文章列出了每个WS-Security标头的详细信息,另外一个示例签名的SOAP消息有已经提供。
签署SOAP消息时,还必须将x.509证书添加到消息KeyInfo中,这是证书验证所必需的。
SOAP Request应该是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header>
<ds:Signature xmlns:ds="xxx...w3.org/2000/09/xmldsig#" Id="Signature001">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="xxx...w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="xxx...w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="xxx...w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="xxx...w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>soe1PnaGXVGrsauC61JSHD+uqGw=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#KeyInfo001">
<ds:DigestMethod Algorithm="xxx...w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>Y9SRPQ9TcDu+GazO3LFwodEdhaA=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>jBX/8XkY2aCte7qgXEp1sbNWmQcK/90iVL58sAvwYAEcBABGzOk2agxR0HvWrNa6ixkocAQ205lggwOxnxZJvoVozVYAAjcLtayPBOUYrnSEBFrwKWP/vxgvUDRIdXeIuw5GLY87NrTQMm1Ehf/HvMX9hTBJn4Nm8RdDiUmPcIo=</ds:SignatureValue>
<ds:KeyInfo Id="KeyInfo001">
<ds:X509Data>
<ds:X509Certificate>MIIEbZCCA1WgAwIBAgIES1XpMjANBgkqhkiG9w0BAQUFADBYMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTAeFw0xMDA0MjIxMDQ4MDBaFw0xMzA0MjIxMTE4MDBaMGoxFTATBgoJkiaJk/IsZAEZFgVsb2NhbDEXMBUGCgmSJomT8ixkARkWB2VudHJ1c3QxEjAQBgNVBAMTCWVudHJ1c3RTTTESMBAGA1UEAxMJZW50cnVzdENBMRAwDgYDVQQDEwdSYnMgUmJzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMf88L2JjLPG1hNmTA/KBiC53WVwS2WU9Jh3lC1Rob6RMzOojomZ/dNrvSRB6nzWeXJpZXwik4XFrsAq24By2SZpLTO4p8Vcq71mTAfDu33cnO49Au2pwNvcMn5qIKBk1Xx+oVb4fzK9ncTRu7bW46HsIYth+qkGhbI2JEHwr/zwIDAQABo4IBrzCCAaswCwYDVR0PBAQDAgeAMCsGA1UdEAQkMCKADzIwMTAwNDIyMTA0ODAwWoEPMjAxMjA1MjgxNTE4MDBaMCMGA1UdIAQcMBowCwYJYIZIAYb6awoEMAsGCSqGSIb2fQdLAzAbBgNVHQkEFDASMBAGCSqGSIb2fQdEHTEDAgEBMIHGBgNVHR8Egb4wgbswb6BtoGukaTBnMRUwEwYKCZImiZPyLGQBGRYFbG9jYWwxFzAVBgoJkiaJk/IsZAEZFgdlbnRydXN0MRIwEAYDVQQDEwllbnRydXN0U00xEjAQBgNVBAMTCWVudHJ1c3RDQTENMAsGA1UEAxMEQ1JMMTBIoEagRIZCZmlsZTovLy8vTVNJREhVLTQ0NUE0RkVFL0NSTC9lbnRydXN0Y2FfZW50cnVzdHNtX2xvY2FsX2NybGZpbGUuY3JsMB8GA1UdIwQYMBaAFBvSL6cPz8L5shubV58yf0pczKzuMB0GA1UdDgQWBBT1/j6OSS8FTjwqluvew16sv7h+VzAJBgNVHRMEAjAAMBkGCSqGSIb2fQdBAAQMMAobBFY4LjADAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQBXxRIA4HUvGSw4L+4uaR51pY4ISjUQWo2Fh7FYBMt29NsKCTdur1OWVVdndt1yjXP4yWXxoAhHtvZL+XNALUFlR2HAWiXuL1nRcxHkB98N5gPqQzW/lJk9cLtL4hVp28EiEpgmKT3I3NP2Pdb2G5MMOdvQ/GFb2y6OwblR8ViPQ8B2aHWzXMrH+0qadPAuBhXyAohwb+mMuYT/ms6xpGi1NMYuYMf6XONz9GkZgnGnMwa+9CCQws1HNz8WYHtmFIxLsVuEWc/0a1vg4IYX1Ds/ttyhJGTVXOSJSkBz8kRyj1pNBDdc1KeG8M++O8m8VgRTJvYaPc7NMiclISukGpea</ds:X509Certificate> </ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</S:Header>
<S:Body Id="ABC">
<ns2:createUser xmlns:ns2="http://webservice.rbs.emea.ps.entrust.com/" xmlns:ns3="http://webservice.rbs.emea.ps.entrust.com/types/CertificateException" xmlns:ns4="http://webservice.rbs.emea.ps.entrust.com/types/UserException">
<userID>0061020051</userID>
</ns2:createUser>
</S:Body>
</S:Envelope>
我有一个服务器证书(来自受信任的CA的p7b格式),我安装在我的WCF Web服务工作站(dev)使用mmc Certificate Snap-in(目前cert在受信任的发布者中)。我不认为我需要在Java服务器上使用另一个证书,因为响应应该是明确的(既没有签名也没有加密)。我对这个证书仍然有点困惑 - 一般都是证书 - 因为它似乎只持有一个公钥。
这是我的测试项目的app.config:
<client>
<endpoint address="http://entrust-user-certification-uat.fm.rbsgrp.net/rbs/WebAS"
behaviorConfiguration="endpointCredentialsBehavior" binding="wsHttpBinding"
bindingConfiguration="WebAsServicePortTypeBinding" contract="IWebAsServicePortType"
name="WebAsServicePortType">
<!--<identity>
<dns value="entrust-user-certification-uat.fm.rbsgrp.net" />
</identity>-->
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<binding name="WebAsServicePortTypeBinding" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Message">
<message clientCredentialType="Certificate" negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="endpointCredentialsBehavior">
<clientCredentials>
<clientCertificate findValue="entrust-user-certification-uat.fm.rbsgrp.net"
storeLocation="LocalMachine" storeName="TrustedPublisher"
x509FindType="FindBySubjectName"></clientCertificate>
<serviceCertificate>
<!--
Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate
is in the user's Trusted People store, then it will be trusted without performing a
validation of the certificate's issuer chain. This setting is used here for convenience so that the
sample can be run without having to have certificates issued by a certificate authority (CA).
This setting is less secure than the default, ChainTrust. The security implications of this
setting should be carefully considered before using PeerOrChainTrust in production code.
-->
<authentication certificateValidationMode="None" revocationMode="NoCheck" trustedStoreLocation="LocalMachine"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
当我进行简单测试时:
WebAS委托服务=新WebAS();
ActivationCodes certCodes = entrustService.createUser(“testNomad”);
我有错误:
失败:System.Web.Services.Protocols.SoapException:
javax.xml.soap.SOAPException:在soap消息中找不到Signature元素
我如何强制每封邮件的签名过程?我以为我可以很容易地通过WCF配置来实现。任何帮助将不胜感激!
答案 0 :(得分:14)
行。经过几次尝试和错误后,使用SignedXml和IClientMessageInspector / BeforeSendRequest模式的解决方案。非常感谢Yaron Naveh提出相关建议。
// Sign an XML request and return it
public static string SignRequest(string request, string SubjectName, string Signature, string keyInfoRefId)
{
if (string.IsNullOrEmpty(request))
throw new ArgumentNullException("request");
if (string.IsNullOrEmpty(SubjectName))
throw new ArgumentNullException("SubjectName");
// Load the certificate from the certificate store.
X509Certificate2 cert = GetCertificateBySubject(SubjectName);
// Create a new XML document.
XmlDocument doc = new XmlDocument();
// Format the document to ignore white spaces.
doc.PreserveWhitespace = false;
// Load the passed XML
doc.LoadXml(request);
// Add the declaration as per Entrust sample provided -don't think it's necessary though
if (!(doc.FirstChild is XmlDeclaration))
{
XmlDeclaration declaration = doc.CreateXmlDeclaration("1.0", "UTF-8", string.Empty);
doc.InsertBefore(declaration, doc.FirstChild);
}
// Remove the Action (MustUnderstand).
// TODO: Need to find a more elegant way to do so
XmlNode headerNode = null;
XmlNodeList nodeList = doc.GetElementsByTagName("Action");
if (nodeList.Count > 0)
{
headerNode = nodeList[0].ParentNode;
headerNode.RemoveChild(nodeList[0]);
}
// Set the body id - not in used but could be useful at a later stage of this project
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
XmlElement body = doc.DocumentElement.SelectSingleNode(@"//s:Body", ns) as XmlElement;
if (body == null)
throw new ApplicationException("No body tag found");
body.RemoveAllAttributes(); // no need to have namespace
body.SetAttribute("Id", "ABC"); // Body Id could be passed as a param
// Create a custom SignedXml object so that we could sign the keyinfo
CustomSignedXml signedXml = new CustomSignedXml(doc);
// Add the key to the SignedXml document.
signedXml.SigningKey = cert.PrivateKey;
// Create a new KeyInfo object.
KeyInfo keyInfo = new KeyInfo();
keyInfo.Id = keyInfoRefId;
// Load the certificate into a KeyInfoX509Data object
// and add it to the KeyInfo object.
KeyInfoX509Data keyInfoData = new KeyInfoX509Data();
keyInfoData.AddCertificate(cert);
keyInfo.AddClause(keyInfoData);
// Add the KeyInfo object to the SignedXml object.
signedXml.KeyInfo = keyInfo;
// Create a reference to be signed.
Reference reference = new Reference();
reference.Uri = "";
// Add an enveloped transformation to the reference.
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
// Add the reference to the SignedXml object.
signedXml.AddReference(reference);
Reference reference2 = new Reference();
reference2.Uri = "#" + keyInfoRefId;
signedXml.AddReference(reference2);
// Add the Signature Id
signedXml.Signature.Id = Signature;
// Compute the signature.
signedXml.ComputeSignature();
// Get the XML representation of the signature and save
// it to an XmlElement object.
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Append the Signature element to the XML document.
if (headerNode != null)
{
headerNode.AppendChild(doc.ImportNode(xmlDigitalSignature, true));
}
return doc.InnerXml;
}
public static X509Certificate2 GetCertificateBySubject(string CertificateSubject)
{
// Check the args.
if (string.IsNullOrEmpty(CertificateSubject))
throw new ArgumentNullException("CertificateSubject");
// Load the certificate from the certificate store.
X509Certificate2 cert = null;
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
try
{
// Open the store.
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
// Find the certificate with the specified subject.
cert = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateSubject, false)[0];
// Throw an exception of the certificate was not found.
if (cert == null)
{
throw new CryptographicException("The certificate could not be found.");
}
}
finally
{
// Close the store even if an exception was thrown.
store.Close();
}
return cert;
}
和CustomSignedXml类:
public class CustomSignedXml : SignedXml
{
public CustomSignedXml(XmlDocument doc) : base(doc)
{
return;
}
public override XmlElement GetIdElement(XmlDocument doc, string id)
{
// see if this is the key info being referenced, otherwise fall back to default behavior
if (String.Compare(id, this.KeyInfo.Id, StringComparison.OrdinalIgnoreCase) == 0)
return this.KeyInfo.GetXml();
else
return base.GetIdElement(doc, id);
}
}
答案 1 :(得分:0)
您可以捕获WCF服务发送的消息吗?顺便说一句。是WSDL中描述的Java服务使用的消息安全性 - 这将使事情变得更加容易。
根据您的描述,我认为您的配置错误,因为使用证书客户端凭据时,您需要两个证书 - 带有公钥和私钥的客户端证书以及带有公钥的服务器证书。
也可能在您的要求中描述:
邮件是否包含KeyInfo x.509证书
证书是否来自受信任的CA - 基于配置
为什么需要发回已安装在该服务器上的服务证书?为什么服务检查其证书是否来自可信CA?我想这些要求表明您必须为您的客户创建新证书。
但这些只是假设,因为实际需求通常用共享语言描述 - WSDL + WS-Security断言。
控制签名和加密可以在几个级别上进行。首先,每个ServiceContract
和MessageContract
都有属性ProtectionLevel,默认为EncryptAndSign
。您可以将其更改为Sign
。