Подписать конверт с сертификатом X509 в Python с тем же результатом, что и в Java - PullRequest
0 голосов
/ 23 января 2020

Я получил этот код 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&#13; iGp33xxxxxxt4gOrD4ymV3txxxxxx7xei9ag4Gt8VXY96hHyxxxxxxZ/IMWQ9Fo9PxxxxxxkuOV&#13; xxxxxxWe6Yp3xxxxxxFP/kMxxxxxxUFNx9fXiRHy6ENxxxxxxpZZxq1FKghxxxxxxF2vyH3+QDZw&#13; VTCEfkxxxxxxT1KBs+cxKLExxxxxxnikHTxeKbzfxxxxxx+0Kwj0kD6bxxxxxxEMwmfixxxxxxgM&#13; 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? Способ отправки обоих одинаков.

...