I am using wss4jSecurityInterceptor for validation and securement actions on my webservice. The SOAP messages back and forth are secured with Signature, Encryption and Timestamp. The problem I am having is, the recipient is unable to verify my response signature and gets the following error:
oracle.wsm.security.SecurityException: WSM-00061 : Signature digest verification failure. The system property xml.debug.verify should be enabled for the details about the digest calculations during verification phase (note xml.debug.verify slows down the signature verification for very large messages).
Here is the code that I am using to perform validation and securement actions:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static Log log = LogFactory.getLog(WebServiceConfig.class);
#Bean
public KeyStoreCallbackHandler securityCallbackHandler() {
KeyStoreCallbackHandler callbackHandler = new KeyStoreCallbackHandler();
callbackHandler.setPrivateKeyPassword("changeit");
return callbackHandler;
}
#Bean
public Wss4jSecurityInterceptor securityInterceptor() throws Exception {
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
// validate incoming request
securityInterceptor.setValidationActions("Timestamp Signature Encrypt");
securityInterceptor.setValidationSignatureCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationDecryptionCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationCallbackHandler(securityCallbackHandler());
// encrypt the response
securityInterceptor.setSecurementEncryptionUser("client_pub"); // alias of client public key
securityInterceptor.setSecurementEncryptionParts("{Content}{}Body");
securityInterceptor.setSecurementEncryptionKeyIdentifier("DirectReference");
securityInterceptor.setSecurementEncryptionCrypto(getCryptoFactoryBean().getObject());
// sign the response
securityInterceptor.setSecurementSignatureAlgorithm(WSS4JConstants.RSA_SHA1);
securityInterceptor.setSecurementSignatureDigestAlgorithm(WSS4JConstants.SHA1);
securityInterceptor.setSecurementSignatureKeyIdentifier("DirectReference");
securityInterceptor.setSecurementActions("Encrypt Signature Timestamp");
securityInterceptor.setSecurementUsername("secret"); // alias of the private key
securityInterceptor.setSecurementPassword("changeit");
securityInterceptor.setSecurementSignatureCrypto(getCryptoFactoryBean().getObject());
return securityInterceptor;
}
#Bean
public CryptoFactoryBean getCryptoFactoryBean() throws IOException {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStorePassword("changeit");
cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("cert/myKeystore.jks"));
return cryptoFactoryBean;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
try {
interceptors.add(securityInterceptor());
} catch (Exception e) {
throw new RuntimeException("could not initialize security interceptor");
}
}
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> registerMessageDispatcherServlet(
ApplicationContext context) {
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(context);
messageDispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<MessageDispatcherServlet>(messageDispatcherServlet, "/ws/*");
}
The receiving side suspects that the error is caused by the fact that the certificate sent as has been corrupted by carriage return character sequence. Here is what it looks like:
<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-124b3a03-73e3-4838-a254-f20883ff4184">MIIG1zCCBb+gAwIBAgIRAIVi4APh2tBtAAAAAFDvSXwwDQYJKoZIhvcNAQELBQAwgboxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu
bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1
dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBMMUswHhcNMTkwMzIyMTY1ODA5WhcNMjAwMzIyMTcyODA4WjCBgjELMAkGA1UEBhMCVVMx
EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCEJlcmtlbGV5MS4wLAYDVQQKEyVMYXdyZW5j
ZSBCZXJrZWxleSBOYXRpb25hbCBMYWJvcmF0b3J5MRswGQYDVQQDExJtc2cudWNwLWl0LmxibC5n
b3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9fSfO1cKz1D34dQaQF1ScY0eEGALm
s3hkRRWMYPWTH8gZRpfP4iFLF9Sx09vagGFHUrru1xf8BnVyB2CsZnvHtE+bDrK4kp4hQpO8aXqf
VEpn+SpY38aQDfZUqjRK6HJM5qxF/2xjNKSw41MPkXALK04yga07IwSUE+GpPt8i2noQPKKJufzf
guA8Plmsnf9uNcpxFezNXC1k+HQBKeO0V+CZ4K5FKyckq64eT8VWbawF5CFoNigtbxTuHHClECV0
VrzNNf5jj/cpymwOu51ljnPhGEY/f73DlEBZg8jpFcDSnAyYyJH2GFgz2wa0TiOpdxItTWHMXalb
YtDH9VvRAgMBAAGjggMMMIIDCDAdBgNVHREEFjAUghJtc2cudWNwLWl0LmxibC5nb3YwggF/Bgor
BgEEAdZ5AgQCBIIBbwSCAWsBaQB2AFWB1MIWkDYBSuoLm1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAAB
aaZyHvYAAAQDAEcwRQIhAJtC0LBFOfupRDy6BOvRrH6pAlvAJFFUWxMbbSRV59nOAiB/c2D5VOwS
aBeC4czjDjUAAfeunN/npVLyX7i6v69aLgB3AId1v+dZfPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9e
oIMPAAABaaZyHwQAAAQDAEgwRgIhAJj/g/g+UjWaoHCl7ldHG08zuUrL8XbAkR8Op4G+//nvAiEA
sLoq29TrwnOtWa+O1+PRHCCllKD22yeKxdMUNrR2Pu0AdgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6
cOeySVMt74uQXgAAAWmmch7YAAAEAwBHMEUCIBVGxbR2c/ByOtfDRMlpIFPRYA5+dGkB4zJ7tjQ6
moYYAiEAmFbeJdg9rCZt6qUqhRH2mxJlotRjp+mZmiHyRFmulykwDgYDVR0PAQH/BAQDAgWgMB0G
A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
LmVudHJ1c3QubmV0L2xldmVsMWsuY3JsMEsGA1UdIAREMEIwNgYKYIZIAYb6bAoBBTAoMCYGCCsG
AQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAIBgZngQwBAgIwaAYIKwYBBQUHAQEE
XDBaMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBggrBgEFBQcwAoYnaHR0
cDovL2FpYS5lbnRydXN0Lm5ldC9sMWstY2hhaW4yNTYuY2VyMB8GA1UdIwQYMBaAFIKicHTdvFM/
z3vU981/p2DGCky/MB0GA1UdDgQWBBRzakStBRG3O6NBdLt1XUkvwwKn4jAJBgNVHRMEAjAAMA0G
CSqGSIb3DQEBCwUAA4IBAQAqt00w3oV4XgcdhcanJdgY/SUCtmN9I4jdras8vRson+wK6WJy6Em8
EF5wyRDLOwUD75Rm9Ak23lKjYOcDTWQGG3YXrYh7xe3J8C+plEY1NAwNPXflCzxkDcdJ4pX0LDfj
aC5FniAvKJ9ET7ZQvpCjBRd7wgOrkuhf3YjCFC3hM4E6D7cGb6DEhCh9nOy+e9woQ/C1LnDil3NX
Vgqk3PMZYkUeHjxqItnDcOAeJaeqsAUiTxZbC8sQQ9ae/CPahghgSWEwL7tMrAH4nGT3yeBQl0I9
O7H9xWj0ooQ8Wt6nO7pq64X5uc7W6iA3/1Of5uCntmMrsqPlvSscP6QVuPw6</wsse:BinarySecurityToken>
My keystore is in PKCS format and I have also converted to jks. No dice. The certs in the keystore verify just fine with openssl command, so I know they are good.
I do my builds on a mac and deploy on CentOS. I have also copied the sources to CentOS and run the build there. Again, made no difference.
Has anyone run into this issue and solved it? Would appreciate any help!
Thanks much!
xmlsec library upgraded to 2.1.3.
wss4j-ws-security-dom upgraded to 2.2.3
Details: The problem was narrow and very obscure. I haven't put everything I did back to the way it was to see if the problem returns, but could do that easily. We use spring-ws-security and WSS4Jfrom apache to perform the validation and securement actions. Both libraries include xmlsec.jar which provides this functionality. In fact wss4j includes it twice since it also embeds opensaml , which in turn contains xmlsec. Spring-ws-security also contains its own xmlsec. They are all different versions. The problem with carriage returns is reported for xmlsec versions 2.1.0 and above.
https://issues.apache.org/jira/browse/SANTUARIO-482
https://issues.apache.org/jira/browse/SANTUARIO-494
I updated wss4j to ver 2.2.3 (where this is ostensibly fixed) and also added the fixed version of xmlsec.jar directly for good measure.
Hope it helps someone else out. :-)
Related
I have a SOAP service implemented using Spring. The service accepts the username/password via the <UsernameToken> element in the SOAP header. That all works fine.
However, the client consuming this SOAP service requests that I include in the WSDL file that the username/password is required via a <Policy> in the <wsdl:binding> element.
I have a method in my code like:
#EnableWs
#Configuration
public class SoapWebServiceConfig extends WsConfigurerAdapter {
#Bean
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema s) {
var wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("MyPort");
wsdl11Definition.setLocationUri("/soap");
wsdl11Definition.setTargetNamespace("http://myservice.com/");
wsdl11Definition.setSchema(s);
return wsdl11Definition;
}
...
}
This produces a WSDL file, but without information in it that the <UsernameToken> is required.
How can I persuade Spring to include the necessary <Policy> information in the WSDL file?
I have looked at the Spring documentation but was unable to determine the incantation necessary. I have looked through the Spring source code but was also not able to see an obvious hook to add the extra information. What I am looking for is something similar to https://stackoverflow.com/a/19726325/220627 but for Spring.
What I do now is a bit of a hack, but I'll put it here in case it's helpful for someone. I'll leave the question open in the hope that someone comes along with a better solution!
private Source addWsdlUsernameTokenPolicy(#Source xml) {
try {
var originalDocumentDOMResult = new DOMResult();
var transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(xml, originalDocumentDOMResult);
var doc = (Document) originalDocumentDOMResult.getNode();
// ... alter the document using DOM methods as necessary ...
return new DOMSource(doc);
}
catch (TransformerConfigurationException | TransformerException e) {
throw new RuntimeException(e);
}
}
#Bean
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema s) {
var wsdl11Definition = new DefaultWsdl11Definition() {
// This is the trick. The getSource() returns the XML of the WSDL.
// You can convert this "Source" (of XML) into a DOM XML structure
// then alter the XML as you like with normal DOM operations.
#Override public Source getSource() {
return addWsdlUsernameTokenPolicy(super.getSource());
}
};
...
return wsdl11Definition;
}
Please I have the following bean definitions
#Bean
public RestTemplate produceRestTemplate(ClientHttpRequestFactory requestFactory){
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.setErrorHandler(restTemplateErrorHandler);
return restTemplate;
}
#Bean
public ClientHttpRequestFactory createRequestFactory() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(maxTotalConn);
connectionManager.setDefaultMaxPerRoute(maxPerChannel);
RequestConfig config = RequestConfig.custom().setConnectTimeout(100000).build();
CloseableHttpClient httpClient = HttpClients.createDefault();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
The code works well but the problem is that fortify flags the code above as being potentially problematic with the following
"The function createRequestFactory() sometimes
fails to release a socket allocated by createDefault() on line 141."
Please anyone with any ideas as to how to correctly do this without fortify raising alarms
Thanks in advance
I am pretty sure that you don't need to do anything. It looks to be a fortify issue that it might not be updated to this usage scenario. There is a mechanism to take exceptions when working with code analyzers - these tools are not always correct.
A Bit of Discussion
Imagine , you are using CloseableHttpClient in a scenario where there would be no #Bean or HttpComponentsClientHttpRequestFactory , then I would say that fortify is correct because that is the very intention of using a java.io.Closeable .
Spring beans are usually singleton with an intention of instance reuse so fortify should know that you are not creating multiple instances and close() method on AutoCloseable would be called when factory is destroyed at shutdown.
if you look at code of - org.springframework.http.client.HttpComponentsClientHttpRequestFactory , this is there.
/**
* Shutdown hook that closes the underlying
* {#link org.apache.http.conn.HttpClientConnectionManager ClientConnectionManager}'s
* connection pool, if any.
*/
#Override
public void destroy() throws Exception {
if (this.httpClient instanceof Closeable) {
((Closeable) this.httpClient).close();
}
}
Your fortify is looking at code in isolation and not in integrated way so its flagging.
Check this 2 points, for solving the problem.
If you never call the httpClient.close() method, sometime you can effectivlely
run out of socket.
If you're code call this method automatically somewhere there is no vuln and problem.
Anyway, this could be a FalsePositiv depending of the Version of the Java and Lib you use
I'm having issues start my spring web app with camel and ActiveMQ.
The particular error I'm getting is not very descriptive:
16:18:53.552 [localhost-startStop-1] ERROR o.a.a.b.BrokerService - Failed to start Apache ActiveMQ ([activemq.myworkingdomain.com, ID:Ricardos-MacBook-Air.local-65257-1453738732697-0:2], {})
java.io.EOFException: null
at java.io.DataInputStream.readBoolean(DataInputStream.java:244) ~[na:1.8.0_60]
at org.apache.activemq.openwire.v11.SubscriptionInfoMarshaller.looseUnmarshal(SubscriptionInfoMarshaller.java:133) ~[activemq-client-5.13.0.jar:5.13.0]
at org.apache.activemq.openwire.OpenWireFormat.doUnmarshal(OpenWireFormat.java:366) ~[activemq-client-5.13.0.jar:5.13.0]
at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:277) ~[activemq-client-5.13.0.jar:5.13.0]
at org.apache.activemq.store.kahadb.KahaDBStore$KahaDBTopicMessageStore$1.execute(KahaDBStore.java:755) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.kahadb.page.Transaction.execute(Transaction.java:769) ~[kahadb-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBStore$KahaDBTopicMessageStore.getAllSubscriptions(KahaDBStore.java:749) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBStore$KahaDBTopicMessageStore.<init>(KahaDBStore.java:663) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBStore.createTopicMessageStore(KahaDBStore.java:920) ~[activemq-core-5.7.0.jar:5.7.0]
at org.apache.activemq.store.kahadb.KahaDBPersistenceAdapter.createTopicMessageStore(KahaDBPersistenceAdapter.java:100) ~[activemq-core-5.7.0.jar:5.7.0]
I'm sticking with java and simple spring stuff no xml:
#Bean
public CamelContext camelContext() {
final CamelContext camelContext = new DefaultCamelContext();
camelContext.addComponent("activemq", activeMQComponent());
try {
CamelConfigurator.addRoutesToCamel(camelContext);
camelContext.start();
} catch (final Exception e) {
LOGGER.error("Failed to start the camel context", e);
}
LOGGER.info("Started the Camel context and components");
return camelContext;
}
#Bean
public ActiveMQComponent activeMQComponent() {
final ActiveMQComponent activeMQComponent = new ActiveMQComponent();
activeMQComponent.setConfiguration(jmsConfiguration());
activeMQComponent.setTransacted(true);
activeMQComponent.setCacheLevelName("CACHE_CONSUMER");
return activeMQComponent;
}
#Bean
public JmsConfiguration jmsConfiguration() {
final JmsConfiguration jmsConfiguration = new JmsConfiguration(pooledConnectionFactory());
jmsConfiguration.setConcurrentConsumers(CONCURRENT_CONSUMERS);
return jmsConfiguration;
}
#Bean
public PooledConnectionFactory pooledConnectionFactory() {
final PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory(
activeMQConnectionFactory());
pooledConnectionFactory.setMaxConnections(MAX_CONNECTIONS_TO_POOL_FACTORY);
// pooledConnectionFactory.start();
return pooledConnectionFactory;
}
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
return new ActiveMQConnectionFactory(username, password, activeMQBrokerURL);
}
I've been trying to change the order in which things load also the routes and what's included in them, deleting the kahadb local folder but nothing seems to work or even point me in the right location.
Your stacktrace shows that your application in using
activemq-client-5.13.0
activemq-core-5.7.0
I am pretty sure that the version mismatch is responsible for this error.
Can you just import activemq-all 5.13.0 and try again?
According to your stacktrace, seems like you are experiencing issues with KahaDB (the persistence engine by default for Apache ActiveMQ).
I believe KahaDB has a bug within it and you can find information about it on the Apache issues Web Page.
I had this issue once, but it strange since it seems like it happens at random, sometimes it works, sometime it fails.
I managed to fix this issue by choosing a different persistence engine for ActiveMQ. Maybe you can give it a try and tell me if it works for you. Hope this information can help you.
I have JAX-WS container-less service (published via Endpoint.publish() right from main() method). I want my service to validate input messages. I have tried following annotation: #SchemaValidation(handler=MyErrorHandler.class) and implemented an appropriate class. When I start the service, I get the following:
Exception in thread "main" javax.xml.ws.WebServiceException:
Annotation #com.sun.xml.internal.ws.developer.SchemaValidation(outbound=true,
inbound=true, handler=class mypackage.MyErrorHandler) is not recognizable,
atleast one constructor of class
com.sun.xml.internal.ws.developer.SchemaValidationFeature
should be marked with #FeatureConstructor
I have found few solutions on the internet, all of them imply the use of WebLogic container. I can't use container in my case, I need embedded service. Can I still use schema validation?
The #SchemaValidation annotation is not defined in the JAX-WS spec, but validation is left open. This means you need something more than only the classes in the jdk.
As long as you are able to add some jars to your classpath, you can set this up pretty easily using metro (which is also included in WebLogic. This is why you find solutions that use WebLogic as container.). To be more precise, you need to add two jars to your classpath. I'd suggest to
download the most recent metro release.
Unzip it somewhere.
Add the jaxb-api.jar and jaxws-api.jar to your classpath. You can do this for example by putting them into the JAVA_HOME/lib/endorsed or by manually adding them to your project. This largely depends on the IDE or whatever you are using.
Once you have done this, your MyErrorHandler should work even if it is deployed via Endpoint.publish(). At least I have this setup locally and it compiles and works.
If you are not able to modify your classpath and need validation, you will have to validate the request manually using JAXB.
Old question, but I solved the problem using the correct package and minimal configuration, as well using only provided services from WebLogic. I was hitting the same problem as you.
Just make sure you use correct java type as I described here.
As I am planning to expand to a tracking mechanism I also implemented the custom error handler.
Web Service with custom validation handler
import com.sun.xml.ws.developer.SchemaValidation;
#Stateless
#WebService(portName="ValidatedService")
#SchemaValidation(handler=MyValidator.class)
public class ValidatedService {
public ValidatedResponse operation(#WebParam(name = "ValidatedRequest") ValidatedRequest request) {
/* do business logic */
return response;
}
}
Custom Handler to log and store error in database
public class MyValidator extends ValidationErrorHandler{
private static java.util.logging.Logger log = LoggingHelper.getServerLogger();
#Override
public void warning(SAXParseException exception) throws SAXException {
handleException(exception);
}
#Override
public void error(SAXParseException exception) throws SAXException {
handleException(exception);
}
#Override
public void fatalError(SAXParseException exception) throws SAXException {
handleException(exception);
}
private void handleException(SAXParseException e) throws SAXException {
log.log(Level.SEVERE, "Validation error", e);
// Record in database for tracking etc
throw e;
}
}
I followed the gwt 2.4 validation sample and implemented the whole stuff into my own App. The client side works great.
private void verifyRegistrationData(final RegistrationTO registration) throws ConstraintViolationException {
final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
final Set<ConstraintViolation<RegistrationTO>> violations = validator.validate(registration);
if (violations.size() > 0) {
final Set<ConstraintViolation<?>> temp = new HashSet<ConstraintViolation<?>>(violations);
throw new ConstraintViolationException(temp);
...
but if I do the same on the server side:
public void update(final RegistrationTO registration) throws IllegalArgumentException, ConstraintViolationException, TestException {
final Set<ConstraintViolation<RegistrationTO>> violations = validator.validate(registration);
if (!violations.isEmpty()) {
final Set<ConstraintViolation<?>> temp = new HashSet<ConstraintViolation<?>>(violations);
throw new ConstraintViolationException(temp);
}
...
the whole thing crashes with the following exception:
javax.servlet.ServletContext log: Exception while dispatching incoming RPC call
com.google.gwt.user.client.rpc.SerializationException: Type 'org.hibernate.validator.engine.PathImpl' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.
That's how PathImpl looks like hibernate-validator-4.1.0.Final-sources.jar
public class PathImpl implements Path, Serializable {
private static final long serialVersionUID = 7564511574909882392L;
...
looks OK (at least to me)
I am using GWT 2.4, validation-api-1.0.0.GA, hibernate-validator-4.1.0.Final, gwt-servlet-deps ...
Thanks in advance!
Is there an explicitly defined a default constructor? i.e.,
public PathImpl() { } ? This is required by GWT's serialization mechanism; if it isn't in the source, serializing an RPC response will fail.
A custom serializer does exist for PathImpl, it's just that unless that class is explicitly referenced in your service API, it's not going to be added to the serialization policy.
The current work around is to add a dummy PathImpl field somewhere in your service API. The ValidationSupport class exists to group this and other such classes together to make this a bit easier.
I change the whole thing to RequestFactory as Thomas Broyer recommended. It was by far not so easy as GWT-RPC. This was the reason for me to collect all kind of informations and to build a sample program. For those who are interested - here you can find a sample with documentation and source. (Single line client logger is also implemented) (Documentation is in German but logging-output aso. is in English...)