Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tilln committed Feb 6, 2017
0 parents commit 7f982aa
Show file tree
Hide file tree
Showing 19 changed files with 814 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
11 changes: 11 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
language: java
jdk:
- oraclejdk7
- oraclejdk8
deploy:
provider: releases
api_key:
secure: XRKnP/Pqj7nVUXSzEQqgYmaK63S0La4AM7FneUeApzGaOK5ggK+tk0K5xAqn0zdy9wMZf9gwAZm9qFLsjk6FTnAGGK1IX5KJeljirEQAEbT2uSgMZAOz78F3ODflnbWPK1YeH8ntrfVAA6Opyxqt9To940geFt70bt1GS/8qey7Jn+j8DUKAR9JR4tzPPBFmfqcoI5hN28Q5otI7ztgxrVpsaBtF67Nn8EivdKQgfyXAv9NT0efgjfYo9NYbizsCZ+tXqBkxZ8aIKS0HvvAA5sNhEXLcsRfEh9oFSKnx1Mi3qpR3J64HBOt+SgBaYcp93GxBx8D33zU7rzBs5YWQ+NLSXjWgJVdsqaygcVTZttj7lwDGNvcyl3dsWn9V9M7lhs5sTd1FpqA2R2aHtgD622YQO3j+Oc2dHmu4cj4I/nbXutCRoiuZjsGPsCUv3KxHUKhY0DSU8KOKQ3VjksqUnYRPmpAKmrcFVdMUskKH80oy2RfUZyQFjl4aDAf8SBukWu91zMk0u0ojpJtrlaOdAiqCDUrlpQSIyt0ByvqBgBQhTM+ZIs7qT8IEHaYz93C/EBYox6tmNtPAa67wszz2G0XRqYWfIafASu+OQxjlfdFwP+E4fFy4XGWfF3oIDFkiX1utKgCSYnosiuAYeRYfSO8FloomHCCwZYiTXTRLW7w=
file: target/jmeter-wss-preprocessor-1.0-SNAPSHOT.jar
on:
repo: tilln/jmeter-wss-preprocessor
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# jmeter-wss-preprocessor [![travis][travis-image]][travis-url]

[travis-image]: https://travis-ci.org/tilln/jmeter-wss-preprocessor.svg?branch=master
[travis-url]: https://travis-ci.org/tilln/jmeter-wss-preprocessor

Overview
------------

Apache JMeter plugin for signing and encrypting SOAP messages (WS-Security).

Installation
------------

1. Copy the jmeter-wss-preprocessor jar file into JMeter's lib/ext directory.
2. Copy the following dependencies into JMeter's lib directory:
* [org.apache.wss4j / wss4j-ws-security-dom](http://central.maven.org/maven2/org/apache/wss4j/wss4j-ws-security-common/2.1.8/wss4j-ws-security-common-2.1.8.jar)
* [org.apache.wss4j / wss4j-ws-security-common](http://central.maven.org/maven2/org/apache/wss4j/wss4j-ws-security-common/2.1.8/wss4j-ws-security-common-2.1.8.jar)
* [org.apache.santuario / xmlsec](http://central.maven.org/maven2/org/apache/santuario/xmlsec/2.0.8/xmlsec-2.0.8.jar)
3. When starting JMeter there will be the following two Preprocessors:
SOAP Message Signer
SOAP Message Encrypter

Usage
------------

The plugin provides two [Preprocessors](http://jmeter.apache.org/usermanual/component_reference.html#preprocessors)
that can be configured for signing and encrypting the payloads of an HTTP or JMS request.
Users familiar with SoapUI will find similarities to the [outgoing WS-Security configuration](https://www.soapui.org/soapui-projects/ws-security.html#3-Outgoing-WS-Security-configurations).

### SOAP Message Signer

![SOAP Message Signer](https://raw.githubusercontent.com/tilln/jmeter-wss-preprocessor/master/docs/signature.png)

### SOAP Message Encrypter

![SOAP Message Encrypter](https://raw.githubusercontent.com/tilln/jmeter-wss-preprocessor/master/docs/encryption.png)


Dependencies
------------

Maven retrieves the following dependencies:

* org.apache.wss4j / wss4j-ws-security-dom
* org.apache.wss4j / wss4j-ws-security-common
* org.apache.santuario / xmlsec
Binary file added docs/encryption.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/signature.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
88 changes: 88 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>co.nz.breakpoint.jmeter.modifiers</groupId>
<artifactId>jmeter-wss-preprocessor</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>jmeter-wss-preprocessor</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<scm>
<url>https://github.com/tilln/jmeter-wss-preprocessor</url>
<connection>scm:git:git://github.com/tilln/jmeter-wss-preprocessor</connection>
<developerConnection>scm:git:[email protected]:tilln/jmeter-wss-preprocessor.git</developerConnection>
<tag>HEAD</tag>
</scm>

<dependencies>
<dependency>
<groupId>org.apache.wss4j</groupId>
<artifactId>wss4j-ws-security-dom</artifactId>
<version>2.1.8</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_components</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_core</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_http</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.apache.jmeter</groupId>
<artifactId>ApacheJMeter_jms</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<preparationGoals>clean install</preparationGoals>
<tagNameFormat>@{project.artifactId}-@{project.version}</tagNameFormat>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<finalName>${project.name}-${project.version}</finalName>
</configuration>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package nz.co.breakpoint.jmeter.modifiers;

import org.apache.jmeter.testelement.AbstractTestElement;
import org.apache.jmeter.processor.PreProcessor;
import org.apache.jmeter.testbeans.TestBean;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.jms.sampler.JMSSampler;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import java.io.ByteArrayInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.wss4j.common.crypto.Crypto;
import org.apache.wss4j.common.crypto.CryptoFactory;
import org.apache.wss4j.common.ext.WSSecurityException;
import org.apache.wss4j.common.util.XMLUtils;
import org.apache.wss4j.dom.message.WSSecHeader;
import org.apache.wss4j.dom.message.WSSecBase;
import org.w3c.dom.Document;

import static org.apache.wss4j.common.crypto.Merlin.PREFIX;
import static org.apache.wss4j.common.crypto.Merlin.KEYSTORE_FILE;
import static org.apache.wss4j.common.crypto.Merlin.KEYSTORE_PASSWORD;
import static org.apache.wss4j.common.crypto.Merlin.KEYSTORE_TYPE;

public abstract class AbstractWSSecurityPreProcessor extends AbstractTestElement implements PreProcessor, TestBean {

private static final Logger log = LoggingManager.getLoggerForClass();

private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
static { factory.setNamespaceAware(true); }

private final DocumentBuilder docBuilder; // Handles the XML document

protected WSSecBase secBuilder; // Subclasses are to instantiate an appropriate instance

private final Properties cryptoProps; // Holds configured attributes for crypto instance

private String certAlias, certPassword; // Certificate alias and password (if private cert)

static final Map<String, Integer> keyIdentifiers = new HashMap<String, Integer>(); // Subclasses are to populate an appropriate set

private List<SecurityPart> partsToSecure; // Holds the names of XML elements to secure (e.g. SOAP Body)

public AbstractWSSecurityPreProcessor() throws ParserConfigurationException {
super();
docBuilder = factory.newDocumentBuilder();
cryptoProps = new Properties();
cryptoProps.setProperty("org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin");
cryptoProps.setProperty(PREFIX+KEYSTORE_TYPE, "jks");
}

static String getKeyIdentifierLabelForType(int keyIdentifierType) {
for (Map.Entry<String, Integer> id : keyIdentifiers.entrySet()) {
if (id.getValue() == keyIdentifierType)
return id.getKey();
}
return null;
}

protected Sampler getSampler() {
return getThreadContext().getCurrentSampler();
}

/* The JMeter API lacks an interface for samplers that have a content or payload,
* so we have to use class specific methods.
*/
protected String getSamplerContent() {
Sampler sampler = getSampler();

if (sampler instanceof HTTPSamplerBase) {
HTTPSamplerBase httpSampler = ((HTTPSamplerBase)sampler);
if (!httpSampler.getPostBodyRaw()) {
log.error("Raw post body required.");
return null;
}
return httpSampler.getArguments().getArgument(0).getValue();
}
else if (sampler instanceof JMSSampler) {
return ((JMSSampler)sampler).getContent();
}
log.warn("Cannot get sampler content of "+sampler.getName());
return null;
}

protected void setSamplerContent(String content) {
Sampler sampler = getSampler();

if (sampler instanceof HTTPSamplerBase) {
((HTTPSamplerBase)sampler).getArguments().getArgument(0).setValue(content);
}
else if (sampler instanceof JMSSampler) {
((JMSSampler)sampler).setContent(content);
}
else {
log.warn("Cannot set sampler content of "+sampler.getName());
}
}

/* The main method that is called before the sampler.
* This will get, parse, secure (sign or encrypt) and then replace
* the sampler's payload.
* A new crypto instance needs to be created for every iteration
* as the config could contain variables which may change.
*/
@Override
public void process() {
String xml = getSamplerContent();
if (xml == null) return;

try {
Document doc = docBuilder.parse(new ByteArrayInputStream(xml.getBytes()));

WSSecHeader secHeader = new WSSecHeader(doc);
secHeader.insertSecurityHeader();

Crypto crypto = CryptoFactory.getInstance(cryptoProps);

doc = this.build(doc, crypto, secHeader); // Delegate in abstract method

setSamplerContent(XMLUtils.prettyDocumentToString(doc));
}
catch (Exception e) {
log.error(e.toString());
}
}

// Subclasses are to implement the actual creation of the signature or encryption,
// as WSSecBase does not define a build method.
protected abstract Document build(Document document, Crypto crypto, WSSecHeader secHeader)
throws WSSecurityException;

// Accessors
public String getKeystoreFile() {
return cryptoProps.getProperty(PREFIX+KEYSTORE_FILE);
}

public void setKeystoreFile(String keystoreFile) {
cryptoProps.setProperty(PREFIX+KEYSTORE_FILE, keystoreFile);
}

public String getKeystorePassword() {
return cryptoProps.getProperty(PREFIX+KEYSTORE_PASSWORD);
}

public void setKeystorePassword(String keystorePassword) {
cryptoProps.setProperty(PREFIX+KEYSTORE_PASSWORD, keystorePassword);
}

public String getCertAlias() {
return certAlias;
}

public void setCertAlias(String certAlias) {
secBuilder.setUserInfo(this.certAlias = certAlias, certPassword);
}

public String getCertPassword() {
return certPassword;
}

public void setCertPassword(String certPassword) {
secBuilder.setUserInfo(certAlias, this.certPassword = certPassword);
}

public String getKeyIdentifier() {
return getKeyIdentifierLabelForType(secBuilder.getKeyIdentifierType());
}

public void setKeyIdentifier(String keyIdentifier) {
secBuilder.setKeyIdentifierType(keyIdentifiers.get(keyIdentifier));
}

public List<SecurityPart> getPartsToSecure() {
return partsToSecure;
}

public void setPartsToSecure(List<SecurityPart> partsToSecure) {
this.partsToSecure = partsToSecure;
secBuilder.getParts().clear();
for (SecurityPart part : partsToSecure) {
secBuilder.getParts().add(part.getPart());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package nz.co.breakpoint.jmeter.modifiers;

import java.beans.PropertyDescriptor;
import org.apache.jmeter.testbeans.BeanInfoSupport;
import org.apache.jmeter.testbeans.gui.FileEditor;
import org.apache.jmeter.testbeans.gui.TableEditor;
import org.apache.jmeter.testbeans.gui.PasswordEditor;

public class AbstractWSSecurityPreProcessorBeanInfo extends BeanInfoSupport {

public AbstractWSSecurityPreProcessorBeanInfo(Class<? extends AbstractWSSecurityPreProcessor> clazz) {
super(clazz);

createPropertyGroup("Certificate", new String[]{
"keystoreFile", "keystorePassword", "certAlias", "certPassword"
});
PropertyDescriptor p;

p = property("keystoreFile");
p.setPropertyEditorClass(FileEditor.class);
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");

p = property("keystorePassword");
p.setPropertyEditorClass(PasswordEditor.class);
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");

p = property("certAlias");
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");

p = property("certPassword");
p.setPropertyEditorClass(PasswordEditor.class);
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");

p = property(getPartsToSecurePropertyName());
p.setPropertyEditorClass(TableEditor.class);
p.setValue(TableEditor.CLASSNAME, SecurityPart.class.getName());
p.setValue(TableEditor.HEADERS, new String[]{"Name", "Namespace", "Encode"});
p.setValue(TableEditor.OBJECT_PROPERTIES, new String[]{"name", "namespace", "modifier"});
}

// Parts should go at the bottom, but then subclasses will have to add the element.
protected String getPartsToSecurePropertyName() {
return "partsToSecure";
}
}
Loading

0 comments on commit 7f982aa

Please sign in to comment.