Skip to content

Commit

Permalink
Remove local failure switch and private key field
Browse files Browse the repository at this point in the history
  • Loading branch information
tilln committed Aug 12, 2019
1 parent 8694cdc commit bdb73cd
Show file tree
Hide file tree
Showing 12 changed files with 53 additions and 108 deletions.
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,19 @@ but only if it is an immediate child node of the SOAP Header.
### Post-Processor

The SOAP Message Decrypter takes a sampler's response data as input, expecting a SOAP message with WS-Security header,
and decrypts the payload based on the content of a given keystore. This requires the Private Key Password
and decrypts the payload based on the content of a given keystore. This requires the private key password
of the encryption certificate.

Note: Due to the way the underlying wss4j library is implemented, any other, not encryption related, security tokens
Until plugin version 1.6 this password is expected in the field "Private Key Password". As of version 1.7 it needs to be
provided in the table *Credentials for WSS Processing*, along with the alias of the keystore entry.

Note: Due to the way the underlying wss4j library is implemented, any other, not encryption related security tokens
in the response message will also be processed, for example signature tokens.
Any such processing will fail if key information is not present. For example, should the response message
include a symmetric signature token, the SOAP Message Decrypter needs the secret key that was used to generate the token.

The key(s) may be provided in the configured keystore, and the secret key password(s) can be listed
in the table *Credentials for processing of WSS Header Tokens*.
in the table *Credentials for WSS Processing*.
Likewise, if a response were to contain a Username Token, the password(s) for the expected username(s) can be listed in
that table, so that the Post-Processor is able to validate the token.

Expand All @@ -150,8 +153,7 @@ while trying to decrypt or validate a response message will cause the sampler to
[assertion](http://jmeter.apache.org/usermanual/component_reference.html#assertions) result,
effectively behaving like an implicit assertion.

If this behaviour is not desired, it may be turned off for a SOAP Message Decrypter via setting
*Fail Sampler on WSS Exception* to "False", or globally via setting the JMeter property `jmeter.wssecurity.failSamplerOnWSSException=false`.
If this behaviour is not desired, it may be turned off via setting the JMeter property `jmeter.wssecurity.failSamplerOnWSSException=false`.

### Support for 3rd party samplers

Expand Down
Binary file modified docs/decryption.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ protected String[] getTableHeadersWithDefaults(String resourceName, String[] def
defaults;
}

protected void createCertificateProperties() {
createPropertyGroup("Certificate", new String[]{
"keystoreFile", "keystorePassword", "certAlias", "certPassword"
});
protected void createCertificateProperties(boolean includeCertificateCredentials) {
createPropertyGroup("Certificate", includeCertificateCredentials ?
new String[]{ "keystoreFile", "keystorePassword", "certAlias", "certPassword"} :
new String[]{ "keystoreFile", "keystorePassword" });
PropertyDescriptor p;

p = property("keystoreFile");
Expand All @@ -42,13 +42,15 @@ protected void createCertificateProperties() {
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");

p = property("certAlias");
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");
if (includeCertificateCredentials) {
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("certPassword");
p.setPropertyEditorClass(PasswordEditor.class);
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, "");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class CryptoTestElement extends AbstractTestElement {
private static final Logger log = LoggingManager.getLoggerForClass();

private final Properties cryptoProps = new Properties(); // Holds configured attributes for crypto instance
protected String certAlias, certPassword;

public CryptoTestElement() {
cryptoProps.setProperty(PREFIX+KEYSTORE_TYPE, "JCEKS"); // compatible with JKS and PKCS12/PFX
Expand Down Expand Up @@ -64,14 +63,6 @@ public static String getSecretKeyDigest(byte[] keyBytes) {
return null;
}

public String getCertAlias() { return certAlias; }

public void setCertAlias(String certAlias) { this.certAlias = certAlias; }

public String getCertPassword() { return certPassword; }

public void setCertPassword(String certPassword) { this.certPassword = certPassword; }

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

public void setKeystoreFile(String keystoreFile) { cryptoProps.setProperty(PREFIX+KEYSTORE_FILE, keystoreFile); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,6 @@ protected Crypto getCrypto() throws WSSecurityException {
}

// Accessors
public String getCertAlias() { return crypto.getCertAlias(); }

public void setCertAlias(String certAlias) { crypto.setCertAlias(certAlias); }

public String getCertPassword() { return crypto.getCertPassword(); }

public void setCertPassword(String certPassword) { crypto.setCertPassword(certPassword); }

public String getKeystoreFile() { return crypto.getKeystoreFile(); }

public void setKeystoreFile(String keystoreFile) { crypto.setKeystoreFile(keystoreFile); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ public class CryptoWSSecurityPostProcessorBeanInfo extends AbstractWSSecurityPos
public CryptoWSSecurityPostProcessorBeanInfo(Class<? extends CryptoWSSecurityPostProcessor> clazz) {
super(clazz);

createCertificateProperties();
createCertificateProperties(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
public abstract class CryptoWSSecurityPreProcessor extends AbstractWSSecurityPreProcessor {

protected CryptoTestElement crypto = new CryptoTestElement();
protected String certAlias, certPassword; // Keystore alias to use for this preprocessor's crypto operation

protected List<SecurityPart> partsToSecure; // Holds the names of XML elements to secure (e.g. SOAP Body)
protected String keyIdentifier;
Expand Down Expand Up @@ -51,13 +52,13 @@ protected Crypto getCrypto() throws WSSecurityException {
}

// Accessors
public String getCertAlias() { return crypto.getCertAlias(); }
public String getCertAlias() { return certAlias; }

public void setCertAlias(String certAlias) { crypto.setCertAlias(certAlias); }
public void setCertAlias(String certAlias) { this.certAlias = certAlias; }

public String getCertPassword() { return crypto.getCertPassword(); }
public String getCertPassword() { return certPassword; }

public void setCertPassword(String certPassword) { crypto.setCertPassword(certPassword); }
public void setCertPassword(String certPassword) { this.certPassword = certPassword; }

public String getKeystoreFile() { return crypto.getKeystoreFile(); }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class CryptoWSSecurityPreProcessorBeanInfo extends AbstractWSSecurityPreP
public CryptoWSSecurityPreProcessorBeanInfo(Class<? extends CryptoWSSecurityPreProcessor> clazz) {
super(clazz);

createCertificateProperties();
createCertificateProperties(true);
}

// This may be added to a property group by subclasses (if desired)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public class WSSDecryptionPostProcessor extends CryptoWSSecurityPostProcessor {
private static final Logger log = LoggingManager.getLoggerForClass();

protected List<Credential> credentials = new ArrayList<>();
protected boolean failOnWSSException = false;

public WSSDecryptionPostProcessor() throws ParserConfigurationException {
super();
Expand Down Expand Up @@ -64,7 +63,7 @@ protected Document process(Document document) throws WSSecurityException {
switch (pwcb.getUsage()) {
case DECRYPT:
log.debug("Providing callback with private key password for "+pwcb.getIdentifier());
pwcb.setPassword(getCertPassword());
pwcb.setPassword(getPasswordForAlias(pwcb.getIdentifier()));
break;
case USERNAME_TOKEN:
log.debug("Providing callback with password for username "+pwcb.getIdentifier());
Expand Down Expand Up @@ -101,15 +100,7 @@ protected Document process(Document document) throws WSSecurityException {
}
}
});
try {
WSHandlerResult results = secEngine.processSecurityHeader(document, requestData);
} catch (WSSecurityException e) {
if (isFailOnWSSException()) {
throw e;
} else {
log.debug("Suppressing exception", e);
}
}
WSHandlerResult results = secEngine.processSecurityHeader(document, requestData);
return document;
}

Expand Down Expand Up @@ -137,27 +128,11 @@ protected void updateSampleResult(org.apache.wss4j.common.ext.Attachment attachm
}

// Accessors
public boolean isFailOnWSSException() {
return failOnWSSException;
}
public List<Credential> getCredentials() { return credentials; }

public void setFailOnWSSException(boolean failOnWSSException) {
this.failOnWSSException = failOnWSSException;
}
public void setCredentials(List<Credential> credentials) { this.credentials = credentials; }

public List<Credential> getCredentials() {
return credentials;
}
public List<Attachment> getAttachments() { return attachments; }

public void setCredentials(List<Credential> credentials) {
this.credentials = credentials;
}

public List<Attachment> getAttachments() {
return attachments;
}

public void setAttachments(List<Attachment> attachments) {
this.attachments = attachments;
}
public void setAttachments(List<Attachment> attachments) { this.attachments = attachments; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,15 @@ public class WSSDecryptionPostProcessorBeanInfo extends CryptoWSSecurityPostProc
public WSSDecryptionPostProcessorBeanInfo() {
super(WSSDecryptionPostProcessor.class);

property("certAlias").setHidden(true); // not needed for decryption, key reference is in response
createPropertyGroup("Decryption", new String[] { "credentials", "attachments" });

createPropertyGroup("Validation", new String[] { "failOnWSSException", "credentials" });

PropertyDescriptor p = property("failOnWSSException");
p.setValue(NOT_UNDEFINED, Boolean.TRUE);
p.setValue(DEFAULT, Boolean.TRUE);
p.setValue(NOT_OTHER, Boolean.TRUE);

p = property("credentials");
PropertyDescriptor p = property("credentials");
p.setPropertyEditorClass(TableEditor.class);
p.setValue(TableEditor.CLASSNAME, Credential.class.getName());
p.setValue(TableEditor.HEADERS, getTableHeadersWithDefaults("credentials.tableHeaders",
new String[]{"Identifier", "Password"}));
p.setValue(TableEditor.OBJECT_PROPERTIES, new String[]{"name", "password"});

createPropertyGroup("Decryption", new String[] { "attachments" });

p = property("attachments");
p.setPropertyEditorClass(TableEditor.class);
p.setValue(TableEditor.CLASSNAME, Attachment.class.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,17 @@ displayName=SOAP Message Decrypter
Header.displayName=WSS Header Settings
actor.displayName=SOAP Actor
actor.shortDescription=The SOAP actor attribute is used to address the Header element to a specific endpoint.
Certificate.displayName=Certificate Settings
Certificate.displayName=Keystore Settings
keystoreFile.displayName=Keystore File
keystoreFile.shortDescription=The keystore containing the private key to use when decrypting the message
keystorePassword.displayName=Keystore Password
keystorePassword.shortDescription=The password for the keystore
certPassword.displayName=Private Key Password
certPassword.shortDescription=The password for the private key to use for decryption
Validation.displayName=Validation Settings
failOnWSSException.displayName=Fail Sampler on WSS Exception
failOnWSSException.shortDescription=Generate Sampler failure if WSS Exception occurs during decryption or validation of WSS header tokens
credentials.displayName=Credentials for processing of WSS Header Tokens
Decryption.displayName=Decryption Settings
credentials.displayName=Credentials for WSS Processing
credentials.shortDescription=List of passwords for keystore entries or username tokens for validation
credentials.tableHeaders=Identifier (Username or Key Alias)|Password
Decryption.displayName=Decryption Settings
attachments.displayName=Attachments to Decrypt
attachments.shortDescription=
attachments.tableHeaders=Content-ID|Bytes (base64-encoded)
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ public void setUp() throws Exception {
mod.setThreadContext(context);
mod.setKeystoreFile("src/test/resources/keystore.jks");
mod.setKeystorePassword("changeit");
mod.setCertPassword("changeit");
mod.setFailOnWSSException(true);
mod.setCredentials(Arrays.asList(new Credential("rsa", "changeit")));
result = new SampleResult();
result.setSuccessful(true);
context.setPreviousResult(result);
Expand Down Expand Up @@ -67,25 +66,19 @@ public void testFailureOnWSSException() throws Exception {
+"</SOAP-ENV:Envelope>",
"UTF-8");

for (boolean global: new boolean[]{false, true}) {
for (boolean local: new boolean[]{false, true}) {
JMeterUtils.setProperty(AbstractWSSecurityPostProcessor.FAIL_ON_WSS_EXCEPTION, String.valueOf(global));
mod.setFailOnWSSException(local);
mod.process();

if (global && local) {
assertFalse(result.isSuccessful());
AssertionResult[] assertionResults = result.getAssertionResults();
assertEquals(1, assertionResults.length);
assertEquals("WSSecurityException", assertionResults[0].getName());
assertTrue(assertionResults[0].isError());
assertThat(assertionResults[0].getFailureMessage(), containsString("Any SIG_KEY_INFO MUST contain exactly one child element"));
} else {
assertTrue(result.isSuccessful());
assertEquals(0, result.getAssertionResults().length);
}
}
}
JMeterUtils.setProperty(AbstractWSSecurityPostProcessor.FAIL_ON_WSS_EXCEPTION, "false");
mod.process();
assertTrue(result.isSuccessful());
assertEquals(0, result.getAssertionResults().length);

JMeterUtils.setProperty(AbstractWSSecurityPostProcessor.FAIL_ON_WSS_EXCEPTION, "true");
mod.process();
assertFalse(result.isSuccessful());
AssertionResult[] assertionResults = result.getAssertionResults();
assertEquals(1, assertionResults.length);
assertEquals("WSSecurityException", assertionResults[0].getName());
assertTrue(assertionResults[0].isError());
assertThat(assertionResults[0].getFailureMessage(), containsString("Any SIG_KEY_INFO MUST contain exactly one child element"));
}

@Test
Expand Down Expand Up @@ -220,6 +213,7 @@ public void testTimestampValidation() {
public void testComplexHeader() throws Exception {
result.setResponseData(Files.readAllBytes(Paths.get("src/test/resources/complex-header.xml")));
mod.setCredentials(Arrays.asList(
new Credential("rsa", "changeit"),
new Credential("hmac", "changeit"),
new Credential("jmeter", "jmeter")
));
Expand Down

0 comments on commit bdb73cd

Please sign in to comment.