У меня следующий рабочий процесс:
- Получить XML как строку
- Преобразовать строку в документ, подписать (некоторые элементы) документ и вернуть его обратно в строку
- Получить подписанный XML как строку, преобразовать его в документ и проверить подпись
Вот метод создания подписи:
public static String createXMLDSign(String xmlString) throws Exception{
// Create a DOM XMLSignatureFactory that will be used to
// generate the enveloped signature.
String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
// Load the KeyStore and get the signing key and certificate.
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("src/main/resources/jks/client-keystore.jks"), keyStorePass .toCharArray());
KeyStore.PrivateKeyEntry keyEntry =
(KeyStore.PrivateKeyEntry) ks.getEntry
("client-keypair", new KeyStore.PasswordProtection(keyPass .toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
// Create the KeyInfo containing the X509Data.
KeyInfoFactory kif = fac.getKeyInfoFactory();
List x509Content = new ArrayList();
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(cert);
X509Data xd = kif.newX509Data(x509Content);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
// Instantiate the document to be signed.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse
(new InputSource(new StringReader(xmlString)));
// Get element which will be signed
Element child =(Element)doc.getElementsByTagName("ns2:Eid").item(0);
child.setAttribute("Id", "1234");
child.setIdAttribute("Id", true);
String id = child.getAttributes().getNamedItem("Id").getNodeValue();
String uri = String.format("#%s", id);
List<Transform> transformList = new LinkedList<>();
transformList.add(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
Reference reference = fac.newReference(uri,
fac.newDigestMethod(DigestMethod.SHA256, null), transformList, null, null);
SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null), fac
.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null), Collections.singletonList(reference));
// Remove element from the Node
Node node = doc.getElementsByTagName("ns2:Eis").item(0);
Node eumNode = doc.getElementsByTagName("ns2:EumSignature").item(0);
node.removeChild(eumNode);
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element.
DOMSignContext dsc = new DOMSignContext
(keyEntry.getPrivateKey(), /*doc.getDocumentElement()*/node);
dsc.setDefaultNamespacePrefix("ds");
// Create the XMLSignature, but don't sign it yet.
XMLSignature signature = fac.newXMLSignature(si, ki);
// Marshal, generate, and sign the enveloped signature.
signature.sign(dsc);
/////////////Signature verification
// Find Signature element.
signVerification(fac, doc, signature);
// Convert DocToString and verify
String stringDoc = Utils.docToString(doc);
// Instantiate the document to be signed.
DocumentBuilderFactory dbf2 = DocumentBuilderFactory.newInstance();
dbf2.setNamespaceAware(true);
Document doc2 = dbf2.newDocumentBuilder().parse
(new InputSource(new StringReader(stringDoc)));
signVerification(fac, doc2, signature);
/////////////////////////
return Utils.docToString(doc);
}
ПРИМЕЧАНИЕ: Iдобавьте вручную атрибут «Id» в элемент, который я хочу подписать, потому что он не существует в исходном XML.
Вот метод, используемый для преобразования docToString:
public static String docToString(Document doc) {
try {
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
return writer.toString();
} catch (TransformerException ex) {
ex.printStackTrace();
return null;
}
}
Результат выполнения Первая проверка прошла успешно, но вторая после преобразования не удалась с ошибкой:
javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: невозможно разрешить элемент с идентификатором 1234 в org.jcp.xml.dsig.internal.dom.DOMReference.dereference (DOMReference.java:431) в org.JCP.xml.dsig.internal.dom.DOMReference.validate (DOMReference.java:393) в org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate (DOMXMLSignature.java:278) в com.innonetworks.utils.XmlSignatureHelper.signVerification (XmlSignatureHelper.java:185) по адресу com.innonetworks.utils.XmlSignatureHelper.createXMLDSign (XmlSignatureHelper.java:160) по адресу com.innonetworks.interceptor.XMLDSignInterceptor.changeOutboundMessage (XML.jpg).MessageChangeInterceptor.handleMessage (MessageChangeInterceptor.java:56) по адресу org.apache.cxf.phase.PhaseInterceptorChain.doIntercept (PhaseInterceptorChain.java:308) по адресу com.innonetworks.interceptor.MessageChangeContor.Message..cxf.phase.PhaseInterceptorChain.: 440) в org.apache.cxf.endpoint.ClientImpl.invoke (ClientImpl.java:355) в org.apache.cxf.endpoint.ClientImpl.invoke (ClientImpl.java:313) в org.apache.cxf.frontend.ClientProxy.invokeSync (ClientProxy.java:96) в org.apache.cxf.jaxws.JaxWsClientProxy.invoke (JaxWsClientProxy.java:140) в com.sun.proxy. $ Proxy55.es1RegisterEIS (неизвестный источник) в com.innonetworks.es1.client.SoapClientApp.callSoapClient.lien (17)com.innonetworks.es1.client.SoapClientApp.main (SoapClientApp.java:63), вызванный: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannotразрешить элемент с идентификатором 1234 в org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference (DOMURIDereferencer.java:134) в org.jcp.xml.dsig.internal.dom.DOMReference.dereference (DOMReference.java:425)) ... еще 18
ПРИМЕЧАНИЕ Если заменить опорный параметр "uri" на "", оба подтверждения пройдены успешно
РЕШЕНИЕ Я исправил проблему иIke поделиться, если может помочь кому-то еще.Решение, которое я нашел на странице Oracle JDK-8017265: XMLSignature не может разрешить ID
Я использовал второй обходной путь - зарегистрируйте элементы ID с помощью метода DOMValidateContext.setIdAttributeNS перед проверкой подписи
Вот код:
DOMValidateContext valContext = new DOMValidateContext
(new X509KeySelector(), nl.item(0));
final NodeList elements = docNew.getElementsByTagName("ns2:EumSignedInfo");
if (elements.getLength() > 0) {
valContext.setIdAttributeNS((Element) elements.item(0), null, "Id");
}