Subscribing to an Azure Service Bus Topic with Spring Boot and AMQP - java

I have an Azure Service bus topic set up called "state-changed" and it has a subscription called "reverb". I'm trying to set up a method with #JmsListener to subscribe to the topic but am getting an error:
2017-03-22 18:34:41.049 WARN 23356 --- [enerContainer-6] o.s.j.l.DefaultMessageListenerContainer : Setup of JMS message listener invoker failed for destination 'state-changed' - trying to recover. Cause: The messaging entity 'sb://[MySERVICEBUS].servicebus.windows.net/state-changed' could not be found. TrackingId:d2b442f79e0f44bdb449861ea57155ce_G44, SystemTracker:gateway6, Timestamp:3/22/2017 6:34:37 PM
javax.jms.JMSException: The messaging entity 'sb://[MySERVICEBUS].servicebus.windows.net/state-changed' could not be found. TrackingId:d2b442f79e0f44bdb449861ea57155ce_G44, SystemTracker:gateway6, Timestamp:3/22/2017 6:34:37 PM
at org.apache.qpid.amqp_1_0.jms.impl.TopicSubscriberImpl.createClientReceiver(TopicSubscriberImpl.java:111) ~[qpid-amqp-1-0-client-jms-0.32.jar:0.32]
at org.apache.qpid.amqp_1_0.jms.impl.MessageConsumerImpl.<init>(MessageConsumerImpl.java:129) ~[qpid-amqp-1-0-client-jms-0.32.jar:0.32]
at org.apache.qpid.amqp_1_0.jms.impl.TopicSubscriberImpl.<init>(TopicSubscriberImpl.java:46) ~[qpid-amqp-1-0-client-jms-0.32.jar:0.32]
at org.apache.qpid.amqp_1_0.jms.impl.SessionImpl.createDurableSubscriber(SessionImpl.java:544) ~[qpid-amqp-1-0-client-jms-0.32.jar:0.32]
at org.apache.qpid.amqp_1_0.jms.impl.SessionImpl.createDurableSubscriber(SessionImpl.java:59) ~[qpid-amqp-1-0-client-jms-0.32.jar:0.32]
at org.springframework.jms.listener.AbstractMessageListenerContainer.createConsumer(AbstractMessageListenerContainer.java:870) ~[spring-jms-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.createListenerConsumer(AbstractPollingMessageListenerContainer.java:215) ~[spring-jms-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.initResourcesIfNecessary(DefaultMessageListenerContainer.java:1189) ~[spring-jms-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1165) ~[spring-jms-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1158) ~[spring-jms-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1055) ~[spring-jms-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_77]
I have been using this blog post to try and get everything up and running: http://ramblingstechnical.blogspot.co.uk/p/using-azure-service-bus-with-spring-jms.html
I can add messages to the topic with JmsTemplate and read messages from it using the plain old Java JMS libraries outlined in the Azure docs: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-java-how-to-use-jms-api-amqp so I know my topic does work and is accessable, it just seems to be when I configure it with Spring that I'm doing something wrong.
My configuration looks like:
#Configuration
public class JmsConfiguration
{
#Bean
public JmsListenerContainerFactory topicJmsListenerContainerFactory() throws NamingException
{
DefaultJmsListenerContainerFactory returnValue = new DefaultJmsListenerContainerFactory();
Context context = context();
ConnectionFactory cf = connectionFactory(context);
returnValue.setConnectionFactory(cf);
returnValue.setSubscriptionDurable(Boolean.TRUE);
return returnValue;
}
private Context context() throws NamingException
{
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory");
env.put(Context.PROVIDER_URL, "src/main/resources/servicebus.properties");
Context context = new InitialContext(env);
return context;
}
/**
* #param context
* #return
* #throws NamingException
*/
private ConnectionFactory connectionFactory(Context context) throws NamingException
{
ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
return cf;
}
}
servicebus.properties (with username and password etc redacted):
# servicebus.properties - sample JNDI configuration
# Register a ConnectionFactory in JNDI using the form:
# connectionfactory.[jndi_name] = [ConnectionURL]
connectionfactory.SBCF=amqps://[USER]:[PASSWORD]#[MYSERVICEBUS]
# Register some queues in JNDI using the form
# queue.[jndi_name] = [physical_name]
# topic.[jndi_name] = [physical_name]
queue.workflow = workflow
topic.state-changed = stage-changed
And finally my listener class:
#Component
public class TestListener
{
Logger logger = LoggerFactory.getLogger(LoggingWorkflowEventHandler.class);
#JmsListener(destination = "state-changed", containerFactory = "topicJmsListenerContainerFactory", subscription = "reverb")
public void onMessage(String message)
{
logger.info("Received message from topic: {}", message);
}
}
If anyone has ever managed to get this working I'd be grateful for some pointers.

Your error message indicates the name of your destination is not found (message entity not found).
Note that you need to tell Azure your Subscription name in a specific way like this:
<TopicName>/Subscriptions/<SubscriptionName>
In your case:
state-changed/Subscriptions/reverb
Hope that helps
Cheers
Seb

If you use Spring Boot you can use prepared Azure ServiceBus JMS Spring Boot Starter where it works out of box.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-servicebus-jms-spring-boot-starter</artifactId>
<version>2.3.5</version>
</dependency>
See a tutorial page https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-service-bus

Create or change a Trustrore:
As we want to establish a secured amqps connection to the Service Bus, we need to have all required SSL certificates stored in a truststore. As it seems that none of the existing ones contains the required certificates I - for transparency - created a new one like this:
Acquire the needed certificates by visiting https://<URL-Of-Your-Servicebus>, e.g. https://XXXXX.servicebus.cloudapi.de in a browser. Then click on the "lock" in the URL (or whatever your browser shows for a secure connection) and open the certificate from there.
Save the current certificate:
[]
When asked for the format to export, choose "DER binary" and save it as a ".cer" file, e.g. "1.cer"
Most likely you can see that your certificate is based on a certificate chain, which means it depends on other certificates. For each of those click "show certificate":
[]
And save that one in the same way as the one before. Repeat until you reach the root-certificate. In this example you will end up with three *.cer files. For further reference I will call them 1.cer, 2.cer and 3.cer
You should now create a new Truststore file for those certificates
/opt/webMethods9/jvm/jvm/bin/keytool -import -file
/opt/webMethods9/IntegrationServer/instances/default/packages/DEV_fse/resources/1.cer -keystore azureTruststore.jks -alias "D-TRUST Root Class 3 CA 2 2009"
/opt/webMethods9/jvm/jvm/bin/keytool -import -file
/opt/webMethods9/IntegrationServer/instances/default/packages/DEV_fse/resources/2.cer -keystore azureTruststore.jks -alias "D-TRUST SSL Class 3 CA 1 2009"
/opt/webMethods9/jvm/jvm/bin/keytool -import -file
/opt/webMethods9/IntegrationServer/instances/default/packages/DEV_fse/resources/3.cer -keystore azureTruststore.jks -alias "servicebus.cloudapi.de"
You will be asked to set the password for this newly created truststore the first time. Now move the truststore to /opt/webMethods9/IntegrationServer/config/certs/trusted (for later reference). You might add it to IS as a truststore (by using the admin-UI "Security > Keystore " and "Create Truststore Alias"), but there is no technical need to do it, as in our case the IS is not using the truststore - it's only used by QPID.
Create a properties file for JNDI You need to create a servicebus.properties file to act as the data source for a pseudo JNDI Server. You can technically place that file wherever you want, but I would recommend putting it in the "resources" folder of the "XXXXXXConnection" package. This should be the content of that file:
# servicebus.properties - sample JNDI configuration
# Register a ConnectionFactory in JNDI using the form:
# connectionfactory.[jndi_name] = [ConnectionURL]
connectionfactory.SBCF = amqps://XXXXXX.servicebus.cloudapi.de?jms.username=xxxxx&jms.password=xxxxxxx&amqp.idleTimeout=120000&jms.receiveLocalOnly=true&transport.trustStoreLocation=/opt/webMethods9/IntegrationServer/config/certs/trusted/azureTruststore.jks
# Register some queues in JNDI using the form
# queue.[jndi_name] = [physical_name]
# topic.[jndi_name] = [physical_name]
queue.QUEUE = myqueue
​
Some Explanations:​
SBCF will be the JNDI-Lookup name for the connection factory. This name is later needed in your JMS-Connection
xxxxxx.servicebus.cloudapi.de is the URL to your Service Bus
jms.username will be provided by your friendly Azure administrator
jms.password will be provided by your friendly Azure administrator. But be aware that you will need to URL-Encode what you will get from the admin before you can use it in this URL. This can for example be done by calling the IS Service pub.string:URLEncode manually in Designer.​​
amqp.idleTimeout needs to be set to 120000 (or higher) because of otherwise you cannot connect to SB
jms.receiveLocalOnly needs to be set to true because of otherwise you cannot connect to SB
transport.trustStoreLocation needs to point the truststore that contains all SSL Certificates that are required to create a secured (AMQPS) connection to SB
queue.QUEUE: QUEUE is the JNDI-Lookup name you will use later in your JMS-Client to send messages or in your JMS-Trigger to receive them. You should set to a something more meaningful probably. The value of this ("myqueue" in the example) is the name of the queue on the SB and has to be provided by your Azure Admin.
[]
The only two important values are:
"Initial Context Factory": org.apache.qpid.jms.jndi.JmsInitialContextFactory
"Provider URL": Must point to your created servicebus.properties, e.g. file:/opt/webMethods9/IntegrationServer/instances/default/packages/XXXXXXConnection/resources/servicebus.properties

Related

Creating a LdapContext from valid ldap service ticket using GSSAPI

Please Note:- I just want to validate whether the following can be achieved using the JAAS/GSSAPI. I am not able to find any pointers.
Let me first clear the constraints on my application:
We can't have a static krb.conf file. It is dynamic and constantly changing. Hence, caching LoginContext objects in memory is not possible as the Subject's credentials are invalidated once the krb.conf file is modified.
We want to manage large set of realms of which we don't have any prior information, hence static krb.conf file is not possible.
"sun.security.krb5.internal.tools.Kinit" is proprietary and always get a warning it might get removed in future releases. So we can't use this to generate the cache because of the risk of getting a runtime issues. Note: Cached TGTs don't expire even if the krb.conf file is changed natively. But the problem here is, we will have to make our code of generating and maintaining the krb.conf file much complex. We want to avoid this
We are keeping the option of running the kinit tool using Runtime#exec(...) as the last resort to generate the TGT cache files (which will reduce the dependency on sun.security.krb5.internal.tools.Kinit package) but will make our krb.conf generation and maintenance logic more complex.
I am in search of figuring out a more easy solution.
Our current implementation is very straightforward: We change our krb.conf file to suit the current realm and domain information and then delete the file. Everything being very dynamic, it is very efficient but the problem is, we are not able to cache the TGTs and service tickets which increases the load on KDC server and results in performance hit!
With the thorough introduction, let us move to the PoC that I am trying out. Please provide pointers or on:
Whether I am moving in the right direction or not?
Are the things that I am envisioning even possible or not?
If anyone of you have come across such a scenario, what strategy have you employed?
We are connecting to Active Directory DCs to form a LdapContext/ DirContext to fetch the AD object Data.
When there is not cached data available, proceed normally to create the LoginContext as usual:
System.setProperty("java.security.krb5.conf", "C:\\Windows\\krb5.ini");
System.setProperty("sun.security.jgss.debug","true");
LoginContext lc = null;
try {
lc = new LoginContext(LdapCtxGSSAPIEx.class.getName(),
new LoginCallbackHandler(username,password));
// Attempt authentication
// You might want to do this in a "for" loop to give
// user more than one chance to enter correct username/password
lc.login();
} catch (LoginException le) {
System.err.println("Authentication attempt failed" + le);
System.exit(-1);
}
Solution #1:
Once the valid LoginContext is created, use GSSAPI to get the service ticket for ldap. I know of the other way (which actually is our current implementation, and shown in Solution#2) to form the LdapContext in the PrivilegedAction#run(). But that is not helping in caching. Hence, trying a PoC on storing the Service Ticket instead of the TGT. Getting the ldap service ticket is as follows:
// servicePrincipalName = ldap/ad01.example.lab#EXAMPLE.LAB
GSSManager manager = GSSManager.getInstance();
GSSName serverName = manager.createName( servicePrincipalName,
GSSName.NT_HOSTBASED_SERVICE);
final GSSContext context = manager.createContext( serverName, krb5OID, null,
GSSContext.DEFAULT_LIFETIME);
// The GSS context initiation has to be performed as a privileged action.
byte[] serviceTicket = Subject.doAs( subject, new PrivilegedAction<byte[]>() {
public byte[] run() {
try {
byte[] token = new byte[0];
// This is a one pass context initialisation.
context.requestMutualAuth( false);
context.requestCredDeleg( false);
return context.initSecContext( token, 0, token.length);
}
catch ( GSSException e) {
e.printStackTrace();
return null;
}
}
});
Questions:
How can I use this service ticket to form the LdapContext ?
Can I store this ticket in a file (encoded) and then later use the ticket in the same way to form the LdapContext ?
Solution #2:
Creating the LdapContext as seen in most of the tutorials and also in our current implementation:
DirContext ctx = Subject.doAs(lc.getSubject(), new JndiAction<DirContext>(args,providerURL ));
class JndiAction<DirContext> implements java.security.PrivilegedAction<DirContext> {
.......
public DirContext run() {
return performJndiOperation(args, this.providerURL);
}
public DirContext performJndiOperation(String[] args, String providerURL){
......
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// Must use fully qualified hostname
env.put(Context.PROVIDER_URL, providerURL);
// Request the use of the "GSSAPI" SASL mechanism
// Authenticate by using already established Kerberos credentials
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
// Request mutual authentication
env.put("javax.security.sasl.server.authentication", "true");
DirContext ctx = new InitialDirContext(env);
return ctx;
......
}
.......
}
When this line: DirContext ctx = new InitialDirContext(env); in the above code is executed: the Subject is populated with a new PrivateCredential with the service principal : ldap/ad01.example.lab#EXAMPLE.LAB
Question: Can I store this service ticket for further reference to create the LdapContext instead of again performing all the steps of authentication again and again?
What are delegation credentials? Would they help in solving my issue?
UPDATE:
Why store the service ticket and not the TGT in cache? ---> To avoid kinit explicitly. Storing the service ticket will fit in with our current solution easily. Also, this is going to be a temporary solution as we are going to ask our customers the krb.conf file as per their needs and get off the responsibility of creating a krb.conf file. But for now, we have to do this!
Please Help!

Using external SSL Trust/Key stores in Spring Boot

I have a Spring Boot app which I have deployed on a docker container on Google Compute Engine which uses a TrustStore and KeyStore for TLS. Currently, these p12 files are directly on the /src/main/resources/ path within the jar files. I'd like to externalize them and pull them from Google SecretManager on application load. The application utilizes Undertow.
I have the following settings:
server:
ssl:
enabled: true
trust-store-password: change-me
#trust-store: # to be set programmatically during launch
client-auth: need
#key-store: # to be set programmatically during launch
key-store-type: PKCS12
key-alias: my-alias
key-store-password: change-me
key-password: change-me
trust-store-type: PKCS12
And I'm attempting to do something along the lines of this in order to set the trust-store property.
#Configuration
class SSLStoreConfig {
private final String trustStore = "my-trust-store.p12"
#Autowired
SecretManagerTemplate secretManagerTemplate
#PostConstruct
private void configureSSL() {
byte[] secretBytes = secretManagerTemplate.getSecretBytes("sm://trust-store")
Path filePath = Paths.get(trustStore)
try {
Files.write(filePath, secretBytes)
} catch (IOException e) {
System.out.println("Failed to write bytes" + e.toString())
}
String value = filePath.toAbsolutePath().toString()
System.setProperty("server.ssl.trust-store", value)
}
#PreDestroy
private void removeStores() {
Path filePath = Paths.get(trustStore)
Files.delete(filePath)
}
}
Obviously, this is just for the trust-store. Once I'm able to set this successfully I can add the KeyStore as well.
How can I pull these files from GCP SecretManager and successfully reference them for the server.ssl configuration properties?

Modify LDAP attribute fails from Java, but succeeds with ldapmodify command

I manage to perform a STARTTLS ldapmodify ( -ZZ) of an attribute, but fail to perform the same modification using javax.naming.ldap coding
The server is OpenLDAP. It has SSigned SSL Certificate for modification, and unsecured reading is allowed. Classical combination.
I run the following command from the same server on which I will try to execute the Java code (later):
ldapmodify -H ldap://my.ldap.server:389 -D "myldapadmin" -W -ZZ
Enter LDAP Password:
dn: CN=John Westood,OU=L100,DC=l,DC=woods
changetype: modify
replace: uPwd
uPwd:: 234WF34TG2U
modifying entry "CN=John Westood,OU=L100,DC=l,DC=woods"
... as you can see, it asks me for myldapadmin's password, I enter it, and the modification happens.
Here is what I am doing from Java, I want to do the same modification, I am running it on the same server. I have imported the SSigned LDAP SSL certificate into java first.
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://my.ldap.server:389");
// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, "myldapadmin");
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, "myadminpass");
// Start TLS (STARTTLS is also used by the console command ldapmodify)
StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new
StartTlsRequest());
SSLSession sess = tls.negotiate();
//Modification of 'uPwd' attribute
ModificationItem[] mods = new ModificationItem[1];
Attribute mod0 = new BasicAttribute("uPwd", "4G45G435G436UJWG");
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
ctx.modifyAttributes("CN=John Westood,OU=L100,DC=l,DC=woods", mods);
// Stop TLS
tls.close();
// Context close
ctx.close();
I get exception in method ctx.modifyAttributes. The Exception is the following:
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 0000052D: SvcErr: DSID-031A12D2, problem 5003 (WILL_NOT_PERFORM), data 0
Anybody have idea why modify uPwd works with STARTLS from commandline, but not from Java ?
Appears you are trying to change perhaps a password against Microsoft Active Directory (Even though you show openLDAP)
(I have no idea what uPwd could be.)
Changing Microsoft Active Directory Passwords has several constraints.
Once you are confident you have an acceptable connection to Microsoft Active Directory, We have an example for Changing Microsoft Active Directory Password with JNDI
FYI: By default startTLS would use 389.

How to accept/find the certificate from an opc ua server?

I am new to opc ua and not a pro in java. While setting up a client in java I'm having trouble with the certificate dealing. I want to connect to the server via Basic 256, SignAndEncrypt. As I understand, in this stage of security a certificate, created or loaded by the client, is send to the server, where it must be accepted. The server then sends a certificate back to the client, which then needs to be accepted by the client. Please, correct me, if I'm wrong.
Creating/Loading a certificate on the client side and sending it to the server does already work fine (see code below) and I can then accept it on the server side manually. But after that I'm stuck: How can I see this certificate validation in my code and how can I find the server certificate, let alone accept it?
I used the SampleConsoleClient of opc ua for some orientation during implementation. But in contrast to there, I do not use any user input.
Here's some of my code so far.
Initialization:
try {
client = new UaClient(serverUri);
} catch (final URISyntaxException e) {
throw new InitializationException("The server uri has an invalid syntax.", e);
}
try {
client.setApplicationIdentity(createApplicationIdentity());
} catch (final SecureIdentityException e) {
throw new InitializationException(
"Application Identity could not be created due to a Security Identity Exception.", e);
} catch (final IOException e) {
throw new InitializationException("Application Identity could not be created due to an IO Exception.",
e);
}
createApplicationIdentity():
final ApplicationDescription appDescription = new ApplicationDescription();
appDescription.setApplicationName(new LocalizedText(APPLICATION_NAME, Locale.ENGLISH));
appDescription.setApplicationUri(APPLICATION_URI);
appDescription.setProductUri(PRODUCT_URI);
appDescription.setApplicationType(ApplicationType.Client);
// Setting security features
client.setSecurityMode(SecurityMode.BASIC256_SIGN_ENCRYPT);
client.setCertificateValidator(validator);
validator.setValidationListener(myValidationListener); //myValidationListener is similar to most lines in MyCertificateValidationListener in the opc ua samples
final File privatePath = new File(validator.getBaseDir(), "private");
final KeyPair issuerCertificate = null;
final int[] keySizes = null;
final ApplicationIdentity identity = ApplicationIdentity.loadOrCreateCertificate(appDescription,
"Sample Organisation", "opcua", privatePath, issuerCertificate, keySizes, true);
identity.setApplicationDescription(appDescription);
return identity;
After initializing, I try to connect like this (with annotation, how I imagine the connection could work properly):
final String securityPolicy = client.getEndpoint() == null
? client.getSecurityMode().getSecurityPolicy().getPolicyUri()
: client.getEndpoint().getSecurityPolicyUri();
client.setSessionName(String.format("%s#%s/Session%d", APPLICATION_NAME,
ApplicationIdentity.getActualHostNameWithoutDomain(), ++sessionCount));
try {
//Idea: catch the server certificate and accept it. Only if that was possible: connect
client.connect();
} catch (final ServiceException e) {
e.printStackTrace();
}
client.setKeepSubscriptions(false);
// After that resolving namespace index (works fine)
}
And the error, that is thrown:
WARN (?:?): /<IPofServer> Error org.opcfoundation.ua.common.ServiceResultException: Bad_SecurityChecksFailed
(0x80130000) "An error occurred verifying security." at
org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(Unknown Source)
com.prosysopc.ua.client.ConnectException: Failed to create secure channel to server: : opc.tcp://<IPofServer>
[http://opcfoundation.org/UA/SecurityPolicy#Basic256,SignAndEncrypt]
ServiceResult=Bad_SecurityChecksFailed (0x80130000) "An error occurred verifying security."
at com.prosysopc.ua.client.UaClient.n(Unknown Source)
at com.prosysopc.ua.client.UaClient.connect(Unknown Source)
at *lineOfCode*
Caused by: org.opcfoundation.ua.common.ServiceResultException:
Bad_SecurityChecksFailed (0x80130000) "An error occurred verifying security."
at org.opcfoundation.ua.transport.tcp.io.TcpConnection$ReadThread.run(Unknown Source)
With the lineOfCode being client.connect().
Thanks in advance for the help!!
The server will send it's certificate to the client. The client then has to
verify the validity of the certificate. This amounts to verifying the signature of the certificate, checking the validity, whether the hostname in the certificate matches the hostname in the endpoint, checking CRLs and so forth. Usually the SDK (the validator) should do this for you, but you might need to feed some parameters into the validator which checks should actually be performed. The security policy Basic256 imposes some minimal requirements on the certificate which the certificate should meet, of course. You can check the requirements here: http://opcfoundation-onlineapplications.org/profilereporting/ -- go to Security Category -> Facets -> Security policy.
check whether the server certificate is trusted. This usually amounts to checking whether a copy of the (puclic key) certificate has been put into some certicate store chosen as a trust store. If you write the client it's up to you to say which store to choose, but you will need to tell the validator where to look. I don't know that much about OPc UA development in Java, but you should check which certificate stores the validator expects. Maybe there is a default keyfile.
(On server side the same happens with the client certificate).
This asssumes you are starting out with self-signed certificates. If you are using certificates signed by a CA both applications (server and client) will need to be able to verify the whole chain of the other party. It can be stored locally in some store or can be send by the other party. At least one certificate in the chain has to be trustest (has to be put into the trust store).
For a general description on how UA security works have a look at this link:
https://opcfoundation.org/wp-content/uploads/2014/08/11_OPC_UA_Security_How_It_Works.pdf
For a detailed account you should consult the specification, available at GitHub.
Edit: one addtional remark which may help here: you seem to be using some SDK for the purpose in question. While validation of certificates, i.e. doing the signature checks etc, is usually covered by such an SDK the configuration of the application is the task of the application (programmer). This includes the location where you store trusted certificates and where and how you gather together missing parts of certificate chains. You might first try to check how demo clients and servers deal with this task, in other words check out the configuration tasks for such applications by trying to create a secure connection from, say, UA Expert to the sample servers from the OPC foundation. In the .Net SDK of the OPC foundation the location for the trust store defaults to a certain directory in the file system (a subfolder of C:\ProgramData\OpcFoundation, it's Windows only). You can, however, overwrite this when you initialize the validator. Other clients use their own directory structure for the storage of trusted certificates
You are obviously referring to the Prosys OPC UA Java SDK.
What always happens first, when you try to establish a secure connection for the first time, is that the server will deny access to your client application - and returns Bad_SecurityChecksFailed.
Only, after you have told the server to trust (the certificate of) your client application, will you get to the phase where the client application will try to verify the server's certificate - and your 'validationListener' will be triggered.
Thank you all for your answers. In the meantime, I tried basically copy/paste and modifying the connect() and initalize() methods from the SampleConsoleClient in the Prosys SDK samples And that worked. Guess it had something to do with renewing some information, but I'm not quite sure of that... Fact is, my application is now working, but thanks for your efforts!

Websphere MQ unable to create initial JNDI context

I am trying to run a demo class (JMSJNDIProducer.java) that comes with Websphere MQ 7.0 installation at 'websphere installation location'\tools\jms\samples. I have Websphere up and running, below are my websphere configurations...
Queue Manager Name: JMSDEMO
Queue Manager Port: 1414
Channel (default): SYSTEM.DEF.CLNTCONN
Queue Name: JMSDEMO.QL
let me know if more info needed...
My code is failing during the initial context creation, I'm very new to Websphere MQ and not sure what the initialContextUrl needs to be?
public class JMS_JNDI_Websphere_Sample {
private static String initialContextUrl = "tcp://localhost:1414";
public static void main(String args[]) {
// Instantiate the initial context
String contextFactory = "com.sun.jndi.fscontext.RefFSContextFactory";
Hashtable<String, Object> environment = new Hashtable<String, Object>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
environment.put(Context.PROVIDER_URL, initialContextUrl);
try {
Context context = new InitialDirContext(environment);
} catch (NamingException e) {
e.printStackTrace();
}
System.out.println("Initial context found!");
}
}
I am getting the below exception.
javax.naming.InvalidNameException: tcp://localhost:1414 [Root exception is java.net.MalformedURLException: unknown protocol: tcp]
at com.sun.jndi.fscontext.FSContextFactory.getFileNameFromURLString(FSContextFactory.java:119)
at com.sun.jndi.fscontext.RefFSContextFactory.createContext(RefFSContextFactory.java:41)
at com.sun.jndi.fscontext.RefFSContextFactory.createContextAux(RefFSContextFactory.java:47)
at com.sun.jndi.fscontext.FSContextFactory.getInitialContext(FSContextFactory.java:49)
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:667)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.init(InitialContext.java:223)
at javax.naming.InitialContext.<init>(InitialContext.java:197)
at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:82)
at com.hcsc.jms.websphere.jndi.JMS_JNDI_Websphere_Sample.main(JMS_JNDI_Websphere_Sample.java:32)
Caused by: java.net.MalformedURLException: unknown protocol: tcp
at java.net.URL.<init>(URL.java:574)
at java.net.URL.<init>(URL.java:464)
at java.net.URL.<init>(URL.java:413)
at com.sun.jndi.fscontext.FSContextFactory.getFileNameFromURLString(FSContextFactory.java:117)
... 9 more
private static String initialContextUrl = "tcp://localhost:1414";
First off, I don't think "tcp" is a valid value and if it is, do you have something running on port 1414 to reply JNDI lookup requests?
Secondly, I think you are confusing MQ port 1414 with JNDI lookup.
Third, why don't you just follow the example in JmsJndiProducer.java and use a file-based JNDI.
i.e. Use MQ Explorer and select "JMS Administered Objects" then do file-based JNDI.
Once you create your file-based JNDI then that value for your initial context.
You need to separate out the concept of messaing as provided by the JMS API, and the lookup of an object via JNDI. As Roger said the issue is confusion between the MQ listener and the JNDI URL
JNDI is split into the interface used to bind and lookup objects in the directory and the 'service providers' that take the object and put into some persistent store. The com.sun.jndi.fscontext.RefFSContextFactory is a service provider that uses the file system, so the URL for this needs to be an EXISTING directory. When objects are 'bound' into that the .bindings file is created (or updated if objects are already there). You don't need to create the .bindings file; that file is created for you by the File System Context. Also don't modify this by hand.
Typically you would use a LDAP backed JNDI service provider for production usage. The 'lookup' APIs used in the application are the same; what would change are the provider URL (as the location of the LDAP server would be provided) and possible the object name.
Would suggest reviewing Oracle's JNDI tutorial ..

Categories

Resources