在Apache JMeter

时间:2016-01-15 14:50:24

标签: soap jmeter signature

我需要在Apache JMeter的SOAP请求中使用X509证书添加签名。 我和我已经有了.p12。 请帮助我如何在Apache JMeter中实现它。 我知道如何在SOAPUI中执行此操作,但在JMeter中找不到任何方法。

2 个答案:

答案 0 :(得分:3)

不知道你还需要这个答案,但我遇到了同样的问题,经过几天的工作后得到修复,我想其他人也可能会发现这个问题很有用......

我根据Dmitri T的答案给出答案; the blog post on blazemeterthis bugreport +一些自定义黑客攻击。

我们假设您需要签署此请求:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ser="http://custom.namespace.com/service-v1.0-rc2">
   <soap:Header>
      <wsa:MessageID xmlns:wsa="http://www.w3.org/2004/12/addressing">a2749a0f-555-9135-367ed901d244</wsa:MessageID>
   </soap:Header>
   <soap:Body>
      <ser:request>
         <ser:person>
            <ser:id>11552</ser:id>
            <ser:number>81067776992</ser:number>
         </ser:person>
      </ser:request>
   </soap:Body>
</soap:Envelope>

您已经关注了blazemeter指南,直到带有自定义Java代码的部分来签署请求。

不使用该代码,而是执行以下操作:

将以下代码复制粘贴到JMeter(3.0)中:

import com.example.wss.SOAPSecurity;
import org.apache.jmeter.services.FileServer;

// get SOAP message from parent sampler body
String soapData = sampler.getArguments().getArgument(0).getValue();

String baseDir = FileServer.getFileServer().getBaseDir();

String pathToKeystore = baseDir + File.separator + "keystore_files" + File.separator + "your.jks";
String keystorePassword = "yourPassword";
int timeToLive = 5000;
String signingAlias = "yourAlias";
String encryptAlias = "yourEncryptingAlias";
String secureSoap = "";

try {
    secureSoap = SOAPSecurity.secureSoapMessageFromString(soapData, pathToKeystore, keystorePassword, null, null, timeToLive, signingAlias, yourEncryptingAlias);
}
catch (Exception ex){
    log.warn("Error in script", ex);
    throw ex;
}

// replace parent sampler body with secured SOAP message
sampler.getArguments().getArgument(0).setValue(secureSoap);
vars.put("SoapDataRaw", secureSoap);

使用groovy作为翻译。

这是适用于我的用例的java类文件:

package com.example.wss;

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.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.message.WSSecEncrypt;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.soap.*;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;

import javax.net.ssl.*;
import javax.xml.transform.dom.DOMSource;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class SOAPSecurity {

    private KeyStore keystore;
    private KeyManagerFactory keyManagerFactory;
    private String keystorePassword;

    private TrustManagerFactory trustManagerFactory;
    private KeyStore truststore;
    private String truststorePassword;

    private Crypto crypto;

    public SOAPSecurity(String pathToKeystore, String keystorePassword, String pathToTruststore, String truststorePassword) throws IOException, NoSuchAlgorithmException, KeyStoreException, CertificateException, UnrecoverableKeyException, KeyManagementException, WSSecurityException {

        keystore = KeyStore.getInstance("JKS");
        InputStream fileReader = new FileInputStream(new File(pathToKeystore));
        keystore.load(fileReader, keystorePassword.toCharArray());
        this.keystorePassword = keystorePassword;

        keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keystore, keystorePassword.toCharArray());

        Properties properties = new Properties();
        properties.setProperty("org.apache.ws.security.crypto.provider", "org.apache.ws.security.components.crypto.Merlin");
        crypto = CryptoFactory.getInstance(properties);
        ((Merlin) crypto).setKeyStore(keystore);

        truststore = KeyStore.getInstance("JKS");
        fileReader = new FileInputStream(new File(pathToTruststore));
        truststore.load(fileReader, truststorePassword.toCharArray());
        this.truststorePassword = truststorePassword;

        trustManagerFactory = TrustManagerFactory.getInstance("PKIX");
        trustManagerFactory.init(truststore);
    }

    //EDITOR: added constructor without truststore
    public SOAPSecurity(String pathToKeystore, String keystorePassword) throws IOException, NoSuchAlgorithmException, KeyStoreException, CertificateException, UnrecoverableKeyException, KeyManagementException, WSSecurityException {

        keystore = KeyStore.getInstance("JKS");
        InputStream fileReader = new FileInputStream(new File(pathToKeystore));
        keystore.load(fileReader, keystorePassword.toCharArray());
        this.keystorePassword = keystorePassword;

        keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        keyManagerFactory.init(keystore, keystorePassword.toCharArray());

        Properties properties = new Properties();
        properties.setProperty("org.apache.ws.security.crypto.provider", "org.apache.ws.security.components.crypto.Merlin");
        crypto = CryptoFactory.getInstance(properties);
        ((Merlin) crypto).setKeyStore(keystore);
    }

    public static String secureSoapMessageFromFile(String messagePath, String pathToKeystore, String keystorePassword,
                                    String pathToTruststore, String trustStorePassword, int timeToLive,
                                    String signingAlias, String encryptAlias) throws
            SAXException, ParserConfigurationException, SOAPException, IOException, WSSecurityException,
            TransformerException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException,
            KeyStoreException, KeyManagementException {

        SOAPSecurity soapSecurity = new SOAPSecurity(pathToKeystore, keystorePassword, pathToTruststore, trustStorePassword);
        SOAPMessage soapMessage = SOAPSecurity.createSOAPRequestFromFile(messagePath);
        return soapSecurity.applyWSSecurity(soapMessage, timeToLive, signingAlias, encryptAlias);
    }

    public static String secureSoapMessageFromString(String messageString, String pathToKeystore, String keystorePassword,
                                           String pathToTruststore, String trustStorePassword, int timeToLive,
                                           String signingAlias, String encryptAlias) throws
            SAXException, ParserConfigurationException, SOAPException, IOException, WSSecurityException,
            TransformerException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException,
            KeyStoreException, KeyManagementException {

        SOAPSecurity soapSecurity = new SOAPSecurity(pathToKeystore, keystorePassword);
        SOAPMessage soapMessage = SOAPSecurity.createSOAPRequestFromString(messageString);
        return soapSecurity.applyWSSecurity(soapMessage, timeToLive, signingAlias, encryptAlias);
    }

    //EDITOR: ...and static signing methods as well
    public static String secureSoapMessageFromString(String messageString, String pathToKeystore, String keystorePassword,
                                           int timeToLive,
                                           String signingAlias, String encryptAlias) throws
            SAXException, ParserConfigurationException, SOAPException, IOException, WSSecurityException,
            TransformerException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException,
            KeyStoreException, KeyManagementException {

        SOAPSecurity soapSecurity = new SOAPSecurity(pathToKeystore, keystorePassword);
        SOAPMessage soapMessage = SOAPSecurity.createSOAPRequestFromString(messageString);
        return soapSecurity.applyWSSecurity(soapMessage, timeToLive, signingAlias, encryptAlias);
    }

    public static String secureSoapMessageFromFile(String messagePath, String pathToKeystore, String keystorePassword,
                                    int timeToLive,
                                    String signingAlias, String encryptAlias) throws
            SAXException, ParserConfigurationException, SOAPException, IOException, WSSecurityException,
            TransformerException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException,
            KeyStoreException, KeyManagementException {

        SOAPSecurity soapSecurity = new SOAPSecurity(pathToKeystore, keystorePassword);
        SOAPMessage soapMessage = SOAPSecurity.createSOAPRequestFromFile(messagePath);
        return soapSecurity.applyWSSecurity(soapMessage, timeToLive, signingAlias, encryptAlias);
    }

    public interface SOAPDocWriter {
        Document writeDocument(String s, DocumentBuilder documentBuilder) throws IOException, SAXException;
    }

    public static SOAPMessage createSOAPRequestFromFile(String messagePath) throws SOAPException, IOException, ParserConfigurationException, SAXException {

        SOAPDocWriter pathWriter = (s, d) -> {
            File messageFile = new File(s);
            return d.parse(new InputSource(new FileInputStream(messageFile)));
        };

        return createSOAPRequestLambda(messagePath, pathWriter);
    }

    public static SOAPMessage createSOAPRequestFromString(String messageString) throws SOAPException, IOException, ParserConfigurationException, SAXException {

        SOAPDocWriter stringWriter = (s, d) -> d.parse(new InputSource(new StringReader(s)));
        return createSOAPRequestLambda(messageString, stringWriter);
    }

    private static SOAPMessage createSOAPRequestLambda(String s, SOAPDocWriter w) throws SOAPException, IOException, ParserConfigurationException, SAXException
    {
        //MessageFactory messageFactory = MessageFactory.newInstance();
        //we use the SOAP 1.2 specification
        MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
        SOAPMessage soapMessage = messageFactory.createMessage();
        SOAPPart soapPart = soapMessage.getSOAPPart();

        SOAPEnvelope soapEnvelope = soapPart.getEnvelope();

        SOAPBody soapBody = soapEnvelope.getBody();

        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
        Document document = w.writeDocument(s, documentBuilder);

        soapBody.addDocument(document);

        soapMessage.saveChanges();

        return soapMessage;
    }

    public static Document toDocument(SOAPMessage soapMsg) throws TransformerConfigurationException, TransformerException, SOAPException, IOException {
        Source src = soapMsg.getSOAPPart().getContent();
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMResult result = new DOMResult();
        transformer.transform(src, result);
        return (Document) result.getNode();
    }

    /**
     * Secures a soap message according to the given security actions
     *
     * @param soapMessage the soap message to be secured
     * @param timestampTimeToLive optional: the time to live for the timestamp
     * @param signatureKeyAlias optional: the alias for the signature key in the keystore
     * @param encryptionKeyAlias optional: the alias for the encryption key in the keystore
     * @throws WSSecurityException
     * @throws IOException
     * @throws SOAPException
     * @throws TransformerException
     */
    public String applyWSSecurity(SOAPMessage soapMessage,
                                  int timestampTimeToLive,
                                  String signatureKeyAlias,
                                  String encryptionKeyAlias) throws WSSecurityException, IOException, SOAPException, TransformerException {

        Document soapMessageDocument = toDocument(soapMessage);

        // add security header
        WSSecHeader securityHeader = new WSSecHeader();
        securityHeader.setMustUnderstand(false);

        //we keep the security header element because we need it afterwards

        Element secHead = securityHeader.insertSecurityHeader(soapMessageDocument);

        /*
        for a reason not yet clear to me this method of signing creates a soap request inside a soap request
        I don't know why but I know how to work around it:
        */

        //append the security header to the soap:Header parent
        soapMessageDocument.getElementsByTagName("soap:Header").item(0).appendChild(secHead);

        //move the soap:Envelope inner soap message to the root of the document and omit the env:Envelope tree
        soapMessageDocument.replaceChild(soapMessageDocument.getElementsByTagName("soap:Envelope").item(0), 
                soapMessageDocument.getElementsByTagName("env:Envelope").item(0));

        //for debugging purposes -> this output shows up in the console output of JMeter.bat
        //System.out.println("insert:"+soapMessageDocument.getFirstChild().getNodeName()+",soap:"+soapMessageDocument.getElementsByTagName("soap:Envelope").item(0).getNodeName());

        WSSecTimestamp timestamp = null;

        // timestamp document
        timestamp = new WSSecTimestamp();
        timestamp.setTimeToLive(timestampTimeToLive);
        timestamp.build(soapMessageDocument, securityHeader); 

        // sign document

        /*
        EDITOR: originals are commented out and replaced by own values
        values should be adapted from SOAPUI and searched for at:
        https://ws.apache.org/wss4j/apidocs/org/apache/wss4j/dom/WSConstants.html
        */

        WSSecSignature signatureBuilder = new WSSecSignature();
        signatureBuilder.setUserInfo(signatureKeyAlias, keystorePassword);
        signatureBuilder.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
        //signatureBuilder.setSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
        signatureBuilder.setSignatureAlgorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1");
        signatureBuilder.setSigCanonicalization(WSConstants.C14N_EXCL_OMIT_COMMENTS);
        /*
        also setDigestAlgo can be set
        https://ws.apache.org/wss4j/apidocs/org/apache/wss4j/dom/message/WSSecSignature.html#setDigestAlgo-java.lang.String-
        but I used the default so I didn't bother       
        */

        //also custom
        signatureBuilder.setUseSingleCertificate(true);

        List<WSEncryptionPart> signatureParts = new ArrayList<WSEncryptionPart>();

        //WSEncryptionPart timestampPart = new WSEncryptionPart(timestamp.getId());
        WSEncryptionPart timestampPart = new WSEncryptionPart("Timestamp","http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd","Content");
        signatureParts.add(timestampPart);

        //WSEncryptionPart bodyPart = new WSEncryptionPart(WSConstants.ELEM_BODY, WSConstants.URI_SOAP11_ENV, "Element");
        WSEncryptionPart bodyPart = new WSEncryptionPart("Body","http://www.w3.org/2003/05/soap-envelope", "Content");
        signatureParts.add(bodyPart);
        signatureBuilder.setParts(signatureParts);

        signatureBuilder.build(soapMessageDocument, crypto, securityHeader);

         // encrypt document
/*
            we didn't encrypt so no need for this code

            (encryption code untested (by me))

         WSSecEncrypt encrypt = new WSSecEncrypt();
         encrypt.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
         encrypt.setSymmetricEncAlgorithm(WSConstants.AES_128_GCM);
         encrypt.setKeyEncAlgo(WSConstants.KEYTRANSPORT_RSAOEP);
         encrypt.setUserInfo(encryptionKeyAlias, keystorePassword);

         List<WSEncryptionPart> encryptionParts = new ArrayList<WSEncryptionPart>();
         WSEncryptionPart encryptionSignaturePart = new WSEncryptionPart("Signature", WSConstants.SIG_NS, "Element");
         WSEncryptionPart encryptionBodyPart = new WSEncryptionPart("Body", WSConstants.URI_SOAP11_ENV, "Content");
         encryptionParts.add(encryptionBodyPart);
         encryptionParts.add(encryptionSignaturePart);
         encrypt.setParts(encryptionParts);
         encrypt.build(soapMessageDocument, crypto, securityHeader);
*/

        DOMSource domSource = new DOMSource(soapMessageDocument);
        soapMessage.getSOAPPart().setContent(domSource);

        soapMessage.saveChanges();
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        soapMessage.writeTo(out);
        String strMsg = new String(out.toByteArray());
        return strMsg;
    }
}

将代码放在com \ example \ wss \ SOAPSecurity.java文件

将wss4j-1.6.18.jar放在同一个目录中。 http://archive.apache.org/dist/ws/wss4j/1.6.18/

我用这个.sh脚本编译/构建/部署它(你可以使用cygwin):

/cygdrive/c/Program\ Files\ \(x86\)/Java/jdk1.8.0_73/bin/javac.exe -cp wss4j-1.6.18.jar com/example/wss/SOAPSecurity.java
cp SOAPSecurity.jar SOAPSecurity.jar.bak$1
zip -r test.zip com/
mv test.zip SOAPSecurity.jar
cp SOAPSecurity.jar /cygdrive/c/Program\ Files\ \(x86\)/apache-jmeter-3.0/lib/

使用.bat文件运行JMeter 3.0

- &GT; SOAPSecurity.jar应该在apache-jmeter-3.0 / lib /文件夹

- &GT;我在JMeter的system.properties文件中也有这些配置:

-Djavax.net.ssl.keyStore=C:/path/to/client.jks
-Djavax.net.ssl.keyStorePassword=verySecret

- &GT;并在JMeter

中的soap请求中添加了一个keystore配置元素

所以,这是关于它的,请随意给它一个旋转,让我知道它是否适合你,如果不是我可以提供一些帮助,如果你想调试自己一个很好的方法做因此,通过在自定义类中调整/放置System.out.println,它可以提供有关正在进行的操作的非常有用的信息,并且可以节省大量时间,

继续玩得开心!!!

S上。

答案 1 :(得分:2)

您需要执行一些脚本以便通过JSR223 PreProcessor加密消息。我们的想法是获取当前的采样器主体,对其进行加密并即时更换。

  1. 将JSR223 PreProcessor添加为请求的子级
  2. 使用以下代码作为参考:

    import com.sun.org.apache.xml.internal.security.Init;
    import com.sun.org.apache.xml.internal.security.c14n.Canonicalizer;
    import com.sun.org.apache.xml.internal.security.signature.XMLSignature;
    import com.sun.org.apache.xml.internal.security.transforms.Transforms;
    import com.sun.org.apache.xml.internal.security.utils.Constants;
    import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
    import org.apache.jmeter.protocol.http.sampler.SoapSampler;
    import org.apache.commons.io.FileUtils;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import java.io.ByteArrayInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.security.KeyStore;
    import java.security.PrivateKey;
    import java.security.cert.X509Certificate;
    import java.text.SimpleDateFormat;
    import java.util.Date;        
    
    //write sampler body into "signature.xml" file
    
    String body = sampler.getXmlData();
    FileUtils.writeStringToFile(new File("signature.xml"),body);
    
    //X509 properties
    
    String keystoreType = "JKS";
    String keystoreFile = "wso2carbon.jks";
    String keystorePass = "wso2carbon";
    String privateKeyAlias = "wso2carbon";
    String privateKeyPass = "wso2carbon";
    String certificateAlias = "wso2carbon";
    
    
    Element element = null;
    String BaseURI = signatureFile.toURI().toURL().toString();
    //SOAP envelope to be signed
    
    
    //get the private key used to sign, from the keystore
    KeyStore ks = KeyStore.getInstance(keystoreType);
    FileInputStream fis = new FileInputStream(keystoreFile);
    ks.load(fis, keystorePass.toCharArray());
    PrivateKey privateKey =
    
    (PrivateKey) ks.getKey(privateKeyAlias, privateKeyPass.toCharArray());
    //create basic structure of signature
    javax.xml.parsers.DocumentBuilderFactory dbf =
         javax.xml.parsers.DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
    String request = sampler.getXmlData();
    ByteArrayInputStream in = new ByteArrayInputStream(request.getBytes());
    Document doc = dBuilder.parse(in);
    in.close();
    Init.init();
    XMLSignature sig =
         new XMLSignature(doc, BaseURI, XMLSignature.ALGO_ID_SIGNATURE_RSA);
    
    element = doc.getDocumentElement();
    element.normalize();
    element.getElementsByTagName("soapenv:Header").item(0).appendChild(sig.getElement());
    
    {
     Transforms transforms = new Transforms(doc);
     transforms.addTransform(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
     //Sign the content of SOAP Envelope
     sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
    }
    
    //Signing procedure
    {
     X509Certificate cert =
             (X509Certificate) ks.getCertificate(certificateAlias);
     sig.addKeyInfo(cert);
     sig.addKeyInfo(cert.getPublicKey());
     sig.sign(privateKey);
    }
    
    //write signature to file
    FileOutputStream f = new FileOutputStream(signatureFile);
    XMLUtils.outputDOMc14nWithComments(doc, f);
    f.close();
    
    
    //set sampler's XML data from file
    String request = FileUtils.readFileToString(signatureFile);
    sampler.setXmlData(request);
    

    您需要根据配置和服务定义替换密钥库和加密相关位

  3. 有关综合解释,请参阅Take the Pain out of Load Testing Secure Web Services