我正在开发一个 JAX-WS Web 服务,它必须根据 XML 数字签名规范验证传入的 SOAP 消息。我必须操纵传入的消息以匹配在签名之前对引用所做的一些转换,并且未在 XML 文档中报告。我使用 JDOM 来做到这一点。我注意到一个奇怪的行为,我可以验证第一条传入的消息,但随后的消息(签名和引用)验证失败。如果我重新启动应用程序服务器(Websphere),我可以验证第一条消息。这个问题是否与后续 Web 服务调用之间共享的脏数据有关?
这是 SOAP 处理程序的实现:
@Override
public boolean handleMessage(SOAPMessageContext messageContext) {
// get the message from the context
SOAPMessage message = messageContext.getMessage();
// is an outgoing message?
Boolean isOutgoing = (Boolean) messageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if(!isOutgoing){
// incoming message...
// Retrieve the SOAP part of the incoming message
SOAPPart soapPart = message.getSOAPPart();
InputStream inStream = null;
try {
Document doc = null;
// Retrieve the SOAP Envelope of the incoming message
Source source = soapPart.getContent();
inStream = ((StreamSource)source).getInputStream();
// Instantiate a Document containing the SOAP Envelope
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(inStream);
// Use JDOM to retrieve the CommandMessage element and add the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" namespace declaration
org.jdom2.input.DOMBuilder jdomBuilder = new org.jdom2.input.DOMBuilder();
org.jdom2.Document jdomDocument = jdomBuilder.build(doc);
org.jdom2.Element jdomBodyElement = jdomDocument.getRootElement().getChild("Body", org.jdom2.Namespace.getNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/"));
org.jdom2.Element jdomCommandMessageElement = jdomBodyElement.getChild("CommandMessage", org.jdom2.Namespace.getNamespace("", "http://www.cryptomathic.com/ckms"));
jdomCommandMessageElement.addNamespaceDeclaration(org.jdom2.Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"));
// Instantiate W3C Document to be validated
org.jdom2.Document jdomSignedDocument = new org.jdom2.Document(jdomCommandMessageElement.detach());
org.jdom2.output.DOMOutputter outputter = new org.jdom2.output.DOMOutputter();
Document signedDocument = outputter.output(jdomSignedDocument);
// Find Signature element
NodeList nl = signedDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
// Create a DOMValidateContext and specify a KeyValue KeySelector and document context
DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0));
// Create a DOM XMLSignatureFactory that will be used to unmarshal the
// document containing the XMLSignature
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
// unmarshal the XMLSignature
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
boolean coreValidity = signature.validate(valContext);
// Check core validation status
if (coreValidity == false) {
System.out.println("Signature failed core validation");
boolean sv = signature.getSignatureValue().validate(valContext);
System.out.println("signature validation status: " + sv);
// check the validation status of each Reference
Iterator i = signature.getSignedInfo().getReferences().iterator();
for (int j=0; i.hasNext(); j++) {
Reference ref = (Reference) i.next();
boolean refValid = ref.validate(valContext);
System.out.println("ref["+j+"] validity status: " + refValid);
}
} else {
System.out.println("Signature passed core validation");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
inStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return true;
}
这是第一个请求的控制台输出:
签名通过核心验证
但是对于后续请求:
签名未通过核心验证
签名验证状态:false
ref[0] 有效性状态:false
问候,乔瓦尼