Java and SSL certificates - java

I am trying to establish a connection with a PHP script of mine in Java using the secure socket layer (HTTPS), but I have found out that to ensure maximum security/validity I have to import the SSL certificate that my website uses into my application... Something I don't know how to do.
If it helps, my SSL Certificate is not self signed, rather provided by StartSSL AND I am using Eclipse IDE.
Could anybody point me in the right direction? i.e. What files do I need, where should I import them and what code do I need in Java, etc?

I have found out that to ensure maxium security/validity I have to import the SSL certificate that my website uses into my application
You are partially right when you make that statement. You do not need to import your SSL certificate. It is sufficient that the StartSSL CA certificate be imported.
Additionally, there is no such thing as importing a certificate into a Java application. SSL support in Java relies on the concept of keystores and truststores, and not on some certificate packaged within your application. If you are publishing your application to be downloaded and executed by end-users, there is no need for your to publish your certificate or for that matter your private key in your application. The private key, and the associated certificate would be stored in a keystore, that only you may access.
The end-users of your application would rely on the SSL support within the Java runtime, that would enable the application to establish SSL connections to sites, after the server-certificate is verified. The Java runtime ships with a default set of CA certificates in a truststore, and the only prerequisite for SSL connections to be successfully established is that the SSL certificate of the server be issued by one of the CAs in the truststore. The certificates of StartSSL are not present in the truststore of the Java runtime, atleast as of version 6, and therefore:
You could instruct your end users to perform the activity of importing the StartSSL CA certificate into the Java truststore. Links that may help include this StartSSL forum thread (only the first 4 steps are needed to import the CA certs into a truststore), a GitHub project, and this blog post; a disclaimer - I haven't attempted using any of those and you ought to be using it at your own risk.
Or, you could initialize your application with your own truststore using the -Djavax.net.ssl.trustStore=<path_to_truststore> -Djavax.net.ssl.trustStorePassword=<truststore_password> JVM startup flags, or execute the following code before initializing SSL connections:
System.setProperty("javax.net.ssl.trustStore","<path_to_truststore>");
System.setProperty("javax.net.ssl.trustStorePassword","<truststore_password>");
This is a viable approach only if your application is a Java SE application that does not happen to be an applet (or an application with similar restrictions on how the truststore is specified).
It would also help to read up the Java keytool documentation.

The following method loads the default (cacerts) keystore, checks to see if a certificate is installed, and installs it if not. It eliminates the need to manually run the keystore command on any servers.
It assumes that the default keystore password (changeit) is unchanged, update CACERTS_PASSWORD if not. Note that the method saves the keystore after adding a certificate, so after being run once the certificate will permanently be in the store.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
/**
* Add a certificate to the cacerts keystore if it's not already included
*/
public class SslUtil {
private static final String CACERTS_PATH = "/lib/security/cacerts";
// NOTE: DO NOT STORE PASSWORDS IN PLAIN TEXT CODE, LOAD AT RUNTIME FROM A SECURE CONFIG
// DEFAULT CACERTS PASSWORD IS PROVIDED HERE AS A QUICK, NOT-FOR-PRODUCTION WORKING EXAMPLE
// ALSO, CHANGE THE DEFAULT CACERTS PASSWORD, AS IT IMPLORES YOU TO!
private static final String CACERTS_PASSWORD = "changeit";
/**
* Add a certificate to the cacerts keystore if it's not already included
*
* #param alias The alias for the certificate, if added
* #param certInputStream The certificate input stream
* #throws KeyStoreException
* #throws NoSuchAlgorithmException
* #throws CertificateException
* #throws IOException
*/
public static void ensureSslCertIsInKeystore(String alias, InputStream certInputStream)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException{
//get default cacerts file
final File cacertsFile = new File(System.getProperty("java.home") + CACERTS_PATH);
if (!cacertsFile.exists()) {
throw new FileNotFoundException(cacertsFile.getAbsolutePath());
}
//load cacerts keystore
FileInputStream cacertsIs = new FileInputStream(cacertsFile);
final KeyStore cacerts = KeyStore.getInstance(KeyStore.getDefaultType());
cacerts.load(cacertsIs, CACERTS_PASSWORD.toCharArray());
cacertsIs.close();
//load certificate from input stream
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
final Certificate cert = cf.generateCertificate(certInputStream);
certInputStream.close();
//check if cacerts contains the certificate
if (cacerts.getCertificateAlias(cert) == null) {
//cacerts doesn't contain the certificate, add it
cacerts.setCertificateEntry(alias, cert);
//write the updated cacerts keystore
FileOutputStream cacertsOs = new FileOutputStream(cacertsFile);
cacerts.store(cacertsOs, CACERTS_PASSWORD.toCharArray());
cacertsOs.close();
}
}
}
Use it like so:
SslUtil.ensureSslCertIsInKeystore("startssl", new FileInputStream("/path/to/cert.crt"));

Apparently the mailgun engineers for some reason don't want to give us clear instructions on how to solve this. This is what I did
We run tomcat8 and connect via jersey web services to the mailgun API. I followed this users instructions and it worked fine. Hope this helps someone.
On 1/22, we updated our SSL certificates due to Symantec's PKI infrastructure set to become untrusted. Some older versions of Java do not have the "DigiCert Global Root G2" CA.
There are several options:
Import the "DigiCert Global Root G2" CA into your "cacerts" file.
Upgrade your JRE to 8u91 (or higher), which includes this root.
To import the "DigiCert Global Root G2" You can download the root from https://www.digicert.com/digicert-root-certificates.htm. Ensure you are downloading the correct root certificate.
Once the certificate is downloaded, you'll need to import it into with a command like the following:
keytool -import -trustcacerts -keystore /path/to/cacerts -storepass changeit -noprompt -alias digicert-global-root-g2 -file /path/to/digicert.crt
You will need to set the path to your Java Keystore and the location of the root certificate you downloaded.
So
1. /path/to/digicert.crt is the file you just downloaded.
2. /path/to/cacerts - This is in your JRE path. I "find / -name cacerts -print" this will help you quickly find all the java cacerts on your filesystem. For me it was /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/security/cacerts

Have a look at the following article: http://stilius.net/java/java_ssl.php
It contains code example which may help in case if you are trying to access your script from code.
Note that you either should use system properties
javax.net.ssl.keyStore
javax.net.ssl.keyStorePassword
to pass the SSL certificate to JVM or import it into JRE keystore by using keytool tool

I have found out that to ensure maxium security/validity I have to
import the SSL certificate
No you don't. You only need that step if your clients don't already trust the signer of the server certificate, which only arises if the server certificate is self-signed or signed e.g. by an internal CA.

That article contains the code for changing trust store's password and adding other certificates there:
thetechawesomeness.ideasmatter.info

Related

Accessing URL from Java requiring certificate [duplicate]

This question already has answers here:
How to convert .pfx file to keystore with private key?
(7 answers)
Closed 5 years ago.
I am fairly ignorant regarding certificates, and would appreciate any guidance as to what I do wrong... Unfortunately I wasn't able to find a solution on the rest of StackOverflow or elsewhere on the web.
I have a URL (using HTTPS) from which I should get the response. (An image in my case, but I'm sure that's less important.)
Here's what I did:
1. Accessing URL from web browser
It response with HTTP code 403 "Forbidden".
I am given a certificate (*.pfx file) which is needed to access the URL.
2. Installing certificate and trying again from browser
Double-clicking on the PFX file opens the "Certificate Import Wizard" (Windows 10). Follow along, then reboot.
Now Chrome retrieves the resource fine.
Firefox still shows a 403, so under Options > Advanced > Certificates > View Certificates I import the PFX. Now FF also returns the resource fine.
3. Installing certificate under JVM
I now want to call the same URL using Java. I'm still on Java 7 for this project for legacy purposes. I understand the cert should be imported into the Java keystore using the keytool CLI command. However this command wants a cert in DER X509 format. So I export the cert again from Chrome into this format.
Now run the command: keytool -import -alias MY_ALIAS -keystore C:/PROGRA~1/Java/jdk1.7.0_80/jre/lib/security/cacerts -file my_cert.cer
Do keytool -list -v -keystore C:/PROGRA~1/Java/jdk1.7.0_80/jre/lib/security/cacerts and inspect for inclusion of required cert.
Now I run my Java program:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
public class TestUrlRead {
private static final String PROXY_URL = ...;
private static final int PROXY_PORT = ...;
private static final String IMAGE_URL = ...;
public static void main(String[] args) throws Exception {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_URL, PROXY_PORT));
URL url = new URL(IMAGE_URL);
URLConnection con = url.openConnection(proxy);
con.setDoOutput(true);
con.connect();
InputStream webIn = con.getInputStream(); // <-- causes IOException
// read and use webIn
inReader.close();
}
}
The exception thrown is:
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 403 for URL: ...
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1627)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
at TestUrlRead.main(TestUrlRead.java:33)
The response and timing suggest that this is similar to the browser without certificate. What am I doing wrong?
I export the cert again from Chrome into this format.
At this point you lost the private key that was contained in the .pfx file, so you were unable to send a certificate to the server. You should have imported the .pfx file directly as a keystore of its own, type PKCS12, or indeed just use it directly via javax.net.ssl.keyStore/keyStoreType/keyStorePassword. See this question for how to do that.

Client Authentication Tomcat - trustAnchors parameter must be non-empty

I know this question appears no stackoverflow a lot, and the exception is to do with a missing truststore - but here is my issue:
Using Apache Tomcat v7.0.56 as server on Windows with Java 8.0.71
modified tomcat/conf/server.xml to include SSL Connector with truststoreType="PKCS12", truststoreFile="conf/regtomcat.truststore" truststorePass="password"
I launch tomcat and I know it is reading this truststore file correctly as I originally did not have the file in pkcs12 format and tomcat reported an error on startup. I also have tried breaking the file location just to see tomcat startup fail as it can not find the file. So tomcat launches okay - it is reading the truststore which must mean it exists and can be read.
I load a p12 file into my browser certificate, and go to my application URL. I then get the tomcat error:
handling exception: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParameterException:
the trustAnchors parameter must be non-empty
Given that my trustore exists, and contains my certificate - what could the problem be?
I generated all certificates and truststores using keytool. The truststore was created using the command
keytool -importcert -alias regClient -storetype PKCS12 -keystore regtomcat.truststore -file regClient.cer
One probable reason might be that if we use same .jks file as a keystore and as a truststore, then the server start up is failing with following error : java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty.
Keystore is to store the ServerCerts and TrustStore is to store the CA certs. On adding at least one CA certificate in the .jks, above issue will be resolved.
Okay - the problem was the certificate was created using JDK 1.8 and the tomcat was running with JRE 1.7 - for whatever reason this caused an issue, so the fix was to use JRE 1.8 and now everything works okay.
Faced same issue. My keystore only had private key pair of the website. Imported the public certificates from the key pair all the way down to the root and it worked.
Faced same issue. The cause was far more basic: the .jks file must be readable for the account under which tomcat service runs. Though we have it as a systemctl service (hence started/stopped from root) the account was tomcat, so a simple chown tomcat:tomcat for the .jks store did the job!
For me the issue was I was passing null to the keyword as password wherein the password was changeit
ks.load(fis, "changeit".toCharArray());

WsImport unable to find imported certificate

Apologies for yet another "unable to find certificate" question.
I'm developing on a Windows 7 machine. I am using multiple Java versions and because of that am explicit about paths to the used java version (here Java6). I achieve this by the following two lines:
set path=c:\Program Files\Java\jdk1.6.0_45\bin;%path%
set java_home=c:\Program Files\Java\jdk1.6.0_45
I need to use a 3rd party web service https://service.gov/Service.svc?wsdl that provides a certificate.PFX certificate (both service URI and certificate file are renamed as a way to protect the 3rd party's interests). I have made sure that after importing the certificate file in Windows I can open the WSDL file in my browser.
I first import the certificate in my keystore (using Administrator Command Prompt to get access to write in the system folder):
keytool -importkeystore -srckeystore certificate.pfx -srcstoretype pkcs12 -keystore "c:\Program Files\Java\jdk1.6.0_45\jre\lib\security\cacerts"
I get a success notification. Still, I make sure that the new certificate is present in the output of:
keytool -list -keystore "c:\Program Files\Java\jdk1.6.0_45\jre\lib\security\cacerts"
Then I create a new folder containing blank subfolders called src and classes. Once this is done, I run wsimport from that new folder (using Java class instead of binary to make sure I am explicit about the truststore being used):
java -classpath "c:\Program Files\Java\jdk1.6.0_45\lib\tools.jar" -Djavax.net.ssl.trustStore="c:\Program Files\Java\jdk1.6.0_45\jre\lib\security\cacerts" -Djavax.net.ssl.trustStorePassword=changeit com.sun.tools.internal.ws.WsImport https://service.gov/Service.svc?wsdl -s src -d classes
The output is the following:
parsing WSDL...
[ERROR] sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Failed to read the WSDL document: https://service.gov/Service.svc?wsdl, because 1) could not find the document; /2) the document could not be read; 3) the root element of the document is not <wsdl:definitions>.
[ERROR] failed.noservice=Could not find wsdl:service in the provided WSDL(s):
At least one WSDL with at least one service definition needs to be provided.
Failed to parse the WSDL.
The WSDL file contains and is used by other organisations, so the problem is pretty certainly not on the 3rd party's side.
Am I missing something? To me it all seems obvious by now, but it still doesn't work. I have also tried this with Java8, and the result is pretty much the same. The only difference is that in Java8, the WsImport class no longer exists, so I am using the wsimport.exe binary.
Thanks in advance for any ideas or hints.
The pfx file (which contains a certificate and also a private key) is for client authentication, while a truststore is for validating the server certificate. It is important to understand the difference between a keystore and a truststore.
You have imported the client certificate (and key) into the default truststore (cacerts). What you should have done instead is:
Import the issuer (CA) of the SSL certificate of the server into cacerts. You can skip this step if the CA certificate is already in cacerts, which is probably the case here.
Use the pfx file as your keystore for client authentication. The easiest way is to convert it to jks: https://stackoverflow.com/a/3054034/2672392 The properties to pass to wsimport are "javax.net.ssl.keyStore" and "javax.net.ssl.keyStorePassword".
See this answer for a list of important SSL properties: https://stackoverflow.com/a/5871352/2672392

Install a renewed SSL certificate in Tomcat

I successfully installed a GoDaddy SSL certificate in Tomcat last year.
When the certificate expired, I renewed it. I did this without generating a new keystore or CSR. GoDaddy delivered three files to me, gd_bundle.crt, gd_intermediate.crt, and a third .crt file with a random number as the name.
I don't know what to do next. I assume I have to incorporate these files into my existing keystore. GoDaddy provides this help page:
http://support.godaddy.com/help/article/5355/ssl-certificate-renewal-tomcat-4-x5-x6-x
The page says I can do this one of two ways. I tried the first way and all I got was an error message saying "-inkey: no such file or directory". I tried the second way, but I do not have any file with the name it references, and also any attempt to import the new certs into the existing keystore results in an error "certificate not imported, alias already exists".
What is the correct way to update my keystore file with the new certificate?
Thanks.
You need to use the keytool to import the numbered file into the existing KeyStore, using the same alias you're already using, with the -importcert option.

Import Keystore in order to Sign Applet

One of the security reps at my company gave me a keystore to use when I sign my applet. However, I'm having issues actually importing the keystore. I tried executing the following, but nothing happened... well almost nothing... The keytool application prompted me for my password which I entered and then hit return. The tool responded by placing the cursor on the next line and it just sat there for like 10 minutes at which time I killed the process. I'm assuming that I'm doing something wrong and am hoping that someone can point me in the right direction.
C:/program files/java/jdk1.6.0_19/bin/keytool" -import -alias company -keystore D:/companysig.jks
Thanks,
Jeremy
If you have a keystore, you can just use that keystore with the jarsigner command. For an example, see here (you inform the keystore to be used for signing, as well as the alias of the key to use):
http://www.owasp.org/index.php/Signing_jar_files_with_jarsigner
When you use they keytool import option, you will be importing a key you specify (that's in a file) to the keystore you specify. If you don't specify the file, the command probably wants to read the key from standard input, and the process was waiting for the key that never arrived. But in any case, in your case I don't see why you'd need to use this.
If you wish to import the whole keystore into another keystore you need to use the importkeystore option:
-importkeystore [-v]
[-srckeystore <srckeystore>] [-destkeystore <destkeystore>]
[-srcstoretype <srcstoretype>] [-deststoretype <deststoretype>]
[-srcstorepass <srcstorepass>] [-deststorepass <deststorepass>]
[-srcprotected] [-destprotected]
[-srcprovidername <srcprovidername>]
[-destprovidername <destprovidername>]
[-srcalias <srcalias> [-destalias <destalias>]
[-srckeypass <srckeypass>] [-destkeypass <destkeypass>]]
[-noprompt]
[-providerclass <provider_class_name> [-providerarg <arg>]] ...
[-providerpath <pathlist>]

Categories