Я получил этот код Java, который я выполняю из кода Python, поскольку я не могу подписать сообщение soap так же, как Java делает в Python.
SOAP API говорит, что подпись недействительна, когда я запускаю свои реализации в Python. Я пробовал zeep
, signxml
и suds
.
Это мой Java код:
package pl.dlabs;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.WSEncryptionPart;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.components.crypto.CredentialException;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
public class XMLSigner {
private static String ALIAS = "alias";
private static String PASSWORD = "password";
private static String KEY_FILE = "cert.p12";
private static final String KEY_FILE_TYPE = "PKCS12";
private static String OUTPUT_FILE_PATH = "./signed.xml";
private static Crypto crypto;
private static final Properties cryptoProperties = new Properties();
private static final WSSecSignature signature = new WSSecSignature();
private static final WSSecTimestamp timestamp = new WSSecTimestamp();
private static final WSSecHeader header = new WSSecHeader();
private static Document signedDocument;
public static void main(String[] args) {
// ARGS: input_file_path, save_file_path, key_file_path
OUTPUT_FILE_PATH = args[1]; // save file path
KEY_FILE = args[2]; // key file path
PASSWORD = args[3];
ALIAS = args[4];
String message = readFileFromArgs(args);
init();
signMessage(message);
saveToFile(getStringFromDoc());
}
private static String readFileFromArgs(String[] args) {
String message = null;
try {
message = new String(Files.readAllBytes(Paths.get(args[0])));
} catch (IOException e) {
System.err.println("Cannot open file with SOAP message.");
System.exit(1);
} catch (IndexOutOfBoundsException e) {
System.err.println("Pass path to the file with SOAP message.");
System.exit(2);
}
return message;
}
private static void init() {
setCryptoProperties();
try {
crypto = new Merlin(cryptoProperties);
} catch (CredentialException | IOException e) {
System.err.println("Error during initializing Crypto instance.");
System.exit(3);
}
}
private static void setCryptoProperties(){
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.alias", ALIAS);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", PASSWORD);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", KEY_FILE_TYPE);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.file", KEY_FILE);
}
private static Document xmlToDoc(String xml) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try {
DocumentBuilder documentBuilder = factory.newDocumentBuilder();
InputSource source = new InputSource();
source.setCharacterStream(new StringReader(xml));
return documentBuilder.parse(source);
} catch (ParserConfigurationException | SAXException | IOException e) {
System.err.println("Error during converting file content to xml document.");
System.exit(4);
}
return null;
}
private static void signMessage(String message) {
Document document = xmlToDoc(message);
header.setMustUnderstand(true);
signature.setSignatureAlgorithm(WSConstants.C14N_EXCL_OMIT_COMMENTS);
signature.setSignatureAlgorithm(WSConstants.RSA);
signature.setUserInfo(ALIAS, PASSWORD);
signature.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
List<WSEncryptionPart> parts = new ArrayList<>();
parts.add(new WSEncryptionPart(WSConstants.ELEM_BODY, WSConstants.URI_SOAP11_ENV, ""));
parts.add(new WSEncryptionPart("Action", "http://www.w3.org/2005/08/addressing", ""));
parts.add(new WSEncryptionPart("ReplyTo", "http://www.w3.org/2005/08/addressing", ""));
parts.add(new WSEncryptionPart("MessageID", "http://www.w3.org/2005/08/addressing", ""));
parts.add(new WSEncryptionPart("To", "http://www.w3.org/2005/08/addressing", ""));
parts.add(new WSEncryptionPart("Timestamp", WSConstants.WSU_NS, ""));
signature.setParts(parts);
try {
header.insertSecurityHeader(document);
timestamp.build(document, header);
signature.build(document, crypto, header);
} catch (WSSecurityException e) {
System.err.println("Error during signing document.");
System.exit(5);
}
signedDocument = document;
}
private static String getStringFromDoc() {
DOMSource domSource = new DOMSource(signedDocument);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
try {
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
} catch (TransformerException e) {
System.err.println("Error during converting signed document to string.");
System.exit(6);
}
writer.flush();
return writer.toString();
}
private static void saveToFile(String content) {
try {
Files.write(Paths.get(OUTPUT_FILE_PATH), content.getBytes());
} catch (IOException e) {
System.err.println("Error during saving signed document to file.");
System.exit(7);
}
}
}
Я преобразовал свой файл .p12 в закрытый ключ и Сертифицируйте с помощью этих команд:
openssl pkcs12 -in out.p12 -nodes -out private.key -nocerts
openssl pkcs12 -in filename.p12 -clcerts -nokeys -out filename.crt
из Как преобразовать файл .p12 в файл .key и Как преобразовать файл .p12 в файл .crt?
Затем я пытаюсь подписать сообщение, используя XMLSigner
, например:
with open(private_key, 'rb') as f:
key = f.read()
with open(private_cert, 'rb') as f:
cert = f.read()
tree = ET.parse(xml_input_path)
root = tree.getroot()
signed_root = XMLSigner(signature_algorithm="rsa-sha1", digest_algorithm="sha1", c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#").sign(root, key=key, cert=cert)
# print(ET.tostring(signed_root))
msg = ET.tostring(signed_root)
Это сообщение с подписью java (я удалил часть данных и скрыл значения DigestValues или Cert. )
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns="http://API/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soapenv:mustUnderstand="1">
<wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="X509-055B4D732368E0302C15797787984432">Same value of cert</wsse:BinarySecurityToken>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-055B4D732368E0302C157977879848610">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsa ns soapenv"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#id-055B4D732368E0302C15797787984685">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="ns"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>This value is only present here</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-055B4D732368E0302C15797787984706">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="ns soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>This value is only present here</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-055B4D732368E0302C15797787984707">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="ns soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>This value is only present here</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-055B4D732368E0302C15797787984708">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="ns soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>This value is only present here</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-055B4D732368E0302C15797787984709">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="ns soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>This value is only present here</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#TS-055B4D732368E0302C15797787984421">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse wsa ns soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>This value is only present here</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>0tJgipounzdeH0XajK0ZT1ahDxxxxxxxxxxx42ErNffhTq2tsmu7U8hPKyxxxxxxrUM+BgaAKU4l iGp33xxxxxxt4gOrD4ymV3txxxxxx7xei9ag4Gt8VXY96hHyxxxxxxZ/IMWQ9Fo9PxxxxxxkuOV xxxxxxWe6Yp3xxxxxxFP/kMxxxxxxUFNx9fXiRHy6ENxxxxxxpZZxq1FKghxxxxxxF2vyH3+QDZw VTCEfkxxxxxxT1KBs+cxKLExxxxxxnikHTxeKbzfxxxxxx+0Kwj0kD6bxxxxxxEMwmfixxxxxxgM EDlxxxxxxrmWTUxxxxxxTRL2SsxxxxxxSyFlig==</ds:SignatureValue>
<ds:KeyInfo Id="KI-055B4D732368E0302C15797787984653">
<wsse:SecurityTokenReference wsu:Id="STR-055B4D732368E0302C15797787984664">
<wsse:Reference URI="#X509-055B4D732368E0302C15797787984432" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
<wsu:Timestamp wsu:Id="TS-055B4D732368E0302C15797787984421">
<wsu:Created>2020-01-23T11:26:38.436Z</wsu:Created>
<wsu:Expires>2020-01-23T11:31:38.436Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soapenv:Header>
</soapenv:Envelope>
А вот python подписанное сообщение:
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://www.w3.org/2005/08/addressing"
xmlns:ns2="http://API/"
xmlns:ns3="http://www.w3.org/2000/09/xmldsig#">
<ns0:Header>
</ns0:Header>
<ns3:Signature>
<ns3:SignedInfo>
<ns3:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<ns3:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ns3:Reference URI=""> <!-- This URI is indeed empty, I didn't erase it -->
<ns3:Transforms>
<ns3:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<ns3:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</ns3:Transforms>
<ns3:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ns3:DigestValue>This value is only present here</ns3:DigestValue>
</ns3:Reference>
</ns3:SignedInfo>
<ns3:SignatureValue>dDDW+QT/exxxxxxnCbCr7r2SxxxxxxzlUwm2AxxxxxxTIZVipyHgxxxxxxLHaPOod5DlOxxxxxxf+spwD9hCc/xxxxxx+WM8Ldx6+YgfnxxxxxxHp+kdeZxxxxxxqW/xq9xxxxxxbdidw2wIxxxxxxDVsF1FCxxxxxxw2Q6kq2MBZxxxxxxaUV8bSkzigM2xxxxxxcQezESXWyA/xxxxxx7KOGKFJODRIxxxxxxmmh/pM9gtTqxxxxxxAyQpQ5haWxxxxxxG/x2LyEaxxxxxxzELpvY/IxxxxxxzSaBxCMxxxxxxYtKMMgdxsaxxxxxxpCSzwj4pxxxxxxBw==</ns3:SignatureValue>
<ns3:KeyInfo>
<ns3:X509Data>
<ns3:X509Certificate>Same value of cert</ns3:X509Certificate>
</ns3:X509Data>
</ns3:KeyInfo>
</ns3:Signature>
</ns0:Envelope>
Как можно добиться того же результата, используя python? Способ отправки обоих одинаков.