I'm trying to use JSch with a private key configuration. I've generated a public and private key using PuTTYgen but am unsure what to do with both of the files.
Which key (public/private) needs transferring to the server?
Code Snippet for connection using PuTTy private Key (.ppk)
JSch jsch=new JSch();
jsch.setKnownHosts("~\.ssh\know_hosts");
jsch.addIdentity("~\sshkey.ppk");
Session session=jsch.getSession("ec2-user", "54.12.11.90", 22);
session.setConfig("PreferredAuthentications", "publickey");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Channel channel=session.openChannel("shell");
channel.setInputStream(System.in);
channel.setOutputStream(System.out);
channel.connect(3*1000);
Have used 0.1.54 version of Jsch
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
First, you need to register your PuTTYgen-generated public key on the server. See Getting ready for public key authentication or (my) Set up SSH public key authentication.
And finally see Can we use JSch for SSH key-based communication? for details on using the private key in JSch.
Make sure you use the latest version of JSch, as older versions do not support the .ppk format natively.
Related
I'd like to connect to a MySQL/MariaDB RDBMS using Connector/J (or another compatible driver) and provide the keystore and truststore directly to the driver, rather than supplying a filename for an on-disk keystore/truststore.
I'm not storing my keys and certificates on the disk any longer and I'd like to avoid having to drop them into a temporary file just for this purpose.
This answer seems to be no. As the way to configure KeyStore and TrustStore is by providing an URL, not a class/factory.
I think it is possible, if you are willing to go through a log of hoops to make it happen.
Because e.g. clientCertificateKeyStoreUrl will accept a URL, you can supply it one with a custom protocol handler, like mykeysupplier://mykey.
You can write a custom URLConnection class for mekeysupplier and then return whatever bytes you want from the getInputStream() method. You have to register a protocol handler and stuff like that, too.
So, it's kind of horrifically ugly, but I think it can be made to work and will surely try at least a PoC of this idea, because it will likely solve my problem (I posted the original question).
There may be another option.
I re-read the configuration reference for Connector/J and there is a URL property that can be specified for connections: socketFactory. This must be the fully-qualified name of a class which implements Connector/J's interface called SocketFactory (note that this is similar but unrelated to the standard library class javax.net.SocketFactory).
This interface is pretty small:
public interface SocketFaactory {
Socket afterHandshake() throws SocketException, IOException;
Socket beforeHandshake() throws SocketException, IOException;
Socket connect(String host, int portNumber, Properties props) throws SocketException, IOException;
}
Later versions of the driver add a fourth int loginTimeout parameter to the connect method.
At any rate, it looks like this might be the basis for a solution.
Unfortunately, MySQL does not use vanilla TLS connections so it might not be as simple as returning a Socket from a standard customized javax.net.ssl.SSLSocketFactory.
UPDATE
The SSL/TLS magic happens after connection due to the way MySQL manages its protocol (it's not just plain-TLS).
In the 5.1-era drivers, it's done in a class called ExportControlled in a method called getSSLSocketFactoryDefaultOrConfigured. In later versions (I have 8.0-era source in front of me), it's done in the same class but in a different method called performTlsHandshake.
Without significant hacking of the driver source or re-implementation of a ton of code, I suspect that the better solution is to implement the URL-based keystore-loading from this answer.
Yes, it's possible to programmatically provide a Java KeyStore and TrustStore to Connector/J. You can use the java.security.KeyStore and java.security.TrustStore classes to create the keystore and truststore in memory, and then pass them to the Connector/J Driver and Connection classes via the javax.net.ssl.keyStore and javax.net.ssl.trustStore system properties, respectively.
HERE IS A CODE SAMPLE
import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
// ...
// Load the keystore and truststore data into memory
byte[] keystoreData = ...;
byte[] truststoreData = ...;
// Create the keystore and truststore objects
KeyStore keyStore = KeyStore.getInstance("JKS");
KeyStore trustStore = KeyStore.getInstance("JKS");
keyStore.load(new ByteArrayInputStream(keystoreData), keystorePassword);
trustStore.load(new ByteArrayInputStream(truststoreData), truststorePassword);
// Set the system properties to use the in-memory keystore and truststore
System.setProperty("javax.net.ssl.keyStore", keyStore);
System.setProperty("javax.net.ssl.trustStore", trustStore);
// Connect to the database using Connector/J
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection(url, username, password);
I have a Spring Boot Application that I've created instantiates an instance of ActiveMqSslBroker. I am attempting to connect to this broker using HermesJMS as a client.
I've configured the connection factory in Hermes as follows:
Class: org.apache.activemq.ActiveMQSslConnectionFactory
brokerURL: ssl://localhost:61616
keyStore: /path/to/client-keystore-containing-client-cert.ks
keyStoreKeyPassword: *****
keyStoreType: PKCS12
trustStore: /path/to/trust-store-containing-broker-cert.ts
trustStorePassword: ****
trustStoreType: PKCS12
The broker is configured in my spring-boot application as follows:
SSL Connector:
brokerUrl: ssl://localhost:61616
KeyManagers:
returned from KeyManagerFactory.getKeyManagers()
KeyStore: /path/to/key-store-containing-broker-cert.ks
TrustManagers:
returned from TrustManagerFactory.getTrustManagers()
TrustStore: /path/to/trust-store-containing-client-cert.ks
The broker is rejecting the connection requests from Hermes with the following error:
javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown
So apparently HermesJMS is not sending the client certificate that is contained in its configured keyStore. Does the key have to have a specific alias to be picked up and used by Hermes? Is there a property I can set to specify the alias from the keyStore to use?
Turns out it was user error. I had a couple of different session configured, and while flipping back and forth between my IDE and Hermes, I somehow ended up testing on a session that was using the wrong connection factory.
After switching to the right session, things started to work.
For completeness here is how I got things working:
In my Spring-Boot application I defined my BrokerService bean as follows:
#Bean
public BrokerService broker(
#Value("${spring.activemq.broker-url}") String brokerUrl,
#Qualifier("brokerTrustManagerFactory") TrustManagerFactory trustManagerFactory,
#Qualifier("brokerKeyManagerFactory") KeyManagerFactory keyManagerFactory,
#Qualifier("secureRandom") SecureRandom secureRandom
){
SslBrokerService brokerService = new SslBrokerService();
brokerService.addSslConnector(
brokerUrl,
keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(),
secureRandom
);
return brokerService;
}
Here is how a connection factory could be configured in a client application:
#Bean
ConnectionFactory connectionFactory(
#Value("${spring.activemq.broker-url}") String brokerUrl,
#Value("${spring.activemq.trustStorePath}") String trustStorePath,
#Value("${spring.activemq.trustStorePass}") String trustStorePass,
#Value("${spring.activemq.keyStorePath}") String keyStorePath,
#Value("${spring.activemq.keyStorePass}") String keyStorePass,
#Value("${client.key.pass}") String clientKeyPass
) {
ActiveMQSslConnectionFactory connectionFactory =
new ActiveMQSslConnectionFactory(brokerUrl);
connectionFactory.setTrustStore(trustStorePath);
connectionFactory.setTrustStorePassword(trustStorePass);
connectionFactory.setTrustStoreType("PKCS12");
connectionFactory.setKeyStore(keyStorePath);
connectionFactory.setKeyStorePassword(keyStorePass);
connectionFactory.setKeyStoreKeyPassword(clientKeyPass);
connectionFactory.setKeyStoreType("PKCS12");
return connectionFactory;
}
Hopefully someone will find this answer useful. Please note, the "spring.activemq.*" property names are not official property names recognized by Spring-Boot. They're just names that seemed to be used by a lot of the spring-boot activemq tutorials on the web.
Thanks,
Dave
I am trying to connect to a sftp server in order to store some files. I want to connect via the SSH.
My question is almost identical with:
Camel SFTP component - SSH private key URI works with privateKeyFile, doesn't work with privateKey
and the following answer: Apache Camel - How to set a private key in a dinamic sftp endpoint
But I still I didn't manage to connect.
My working route:
from(mytopic)
.to(sftp://username#my.host:22/folder/?privateKeyFile=path/to/privateRsa)
My NON working route:
from(mytopic)
.to(sftp://username#my.host:22/folder/?privateKey=byteArrayRsa)
I debugged the com.jcraft.jsch.KeyPair class -> following method:
public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException
And seems that instead of having the correct byte[] prvkey, I got its address.
How can I send the correct byte array? I tried to create a bean as it is suggested in one of the links, but it didn't worked. (I am using camel 3.1)
This doesn't work, you cannot simply use a ByteArray reference in a route definition. It works when you use a Registry like org.apache.camel.impl.SimpleRegistry to store the key reference.
simpleRegistry.put("MySshKey", byteArrayRsa)
camelCtx.setRegistry(simpleRegistry)
Then you can use a reference in the route:
privateKey=#MySshKey
Maybe you could also use Spring Injection.
I have successfully connected SFTP server using apache camel's SFTP component using the ssh private key. My requirement was to download files from SFTP server. Similarly, It can be applied to use SFTP to upload file, Sample code is as follows :
public class MySFTPFileTransferRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("sftp://my_host/?username=user_name&password=&preferredAuthentications=publickey&useUserKnownHostsFile=false&privateKeyFile=/Users/XXXX/.ssh/id_rsa")
.to("file:/Users/XXXX/Downloads/source/").
log("Upload file ${file:name} is complete.");
}
}
I'm deploying some spring-boot app to Swisscom AppCloud which uses a MariaDB service. The service gets automatically configured in my app using the CloudFoundry connectors and the connection works fine.
However: since I heavily use ZonedDateTime-Objects in my Java-Code, I also included in the pom.xml...
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
</dependency>
...to properly preserve the ZonedDateTimes in the database.
This works fine on my local MariaDB, when I add...
...?useLegacyDatetimeCode=false
...to the connect string (as described here: https://moelholm.com/2016/11/09/spring-boot-controlling-timezones-with-hibernate/ -> "BONUS TIP: Getting the Hibernate configuration to work with MariaDB / MySQL").
How can I add this flag (and maybe others, too) to the connection to the MariaDB service on Swisscom AppCloud?
If you use Spring on CloudFoundry in conjunction with a MariaDB binding, the datasource is automatically configured by this mechanism: https://docs.cloudfoundry.org/buildpacks/java/spring-service-bindings.html
This is powered by the Spring cloud connectors project, which you can customize to your needs.
I did not test it, but you should be able to set the driver properties as follows:
#Bean
public DataSource dataSource() {
PoolConfig poolConfig = new PoolConfig(5, 30, 3000);
ConnectionConfig connConfig = new ConnectionConfig("useLegacyDatetimeCode=false");
DataSourceConfig dbConfig = new DataSourceConfig(poolConfig, connConfig);
return connectionFactory().dataSource(dbConfig);
}
maybe it dumb question but I couldn't find explanation by googling.
There is settings in server.xml file of Websphere Liberty 8.5
<keyStore id="sampleJKSKeyStore"
location="MyKeyStoreFile.jks"
type="JKS" password="myPassword" />
What can I do with this keystore? Can I for example programatically save to and load from PrivateKey / PublicKey which I create by using the following code:
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
final KeyPair key = keyGen.generateKeyPair();
final PrivateKey privateKey1=key.getPrivate();
final PublicKey publickey1=key.getPublic();
Thank you in advance!
The keystore configuration essentially just tells the Liberty profile runtime where the keystore is and how to get certificates from it. It can then be associated with the httpEndpoint to store the certificates for https. There are other ways it is used by the Liberty runtime, but the usage is by the runtime, not for applications. If you would like to get application access to it you can raise a request for the product to be enhanced here.