SSLHandshakeException - handshake failed on Android 6.0 - java

I am working on an android application which need to download through HTTPS connection. But I got SSLHandshakeException - handshake failed.
What I have done is as below
First,using openssl to get server certificate chain
openssl s_client -connect www.coles.com.au:443
And got
Certificate chain
0 s:/C=AU/ST=Victoria/L=Hawthorn East/O=Coles Supermarkets Australia Pty Ltd/OU=Web Team/CN=coles.com.au
i:/C=US/O=thawte, Inc./CN=thawte SSL CA - G2
1 s:/C=US/O=thawte, Inc./CN=thawte SSL CA - G2
i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c) 2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIE3zCCA8egAwIBAgIQfc9y5DSMNxERU663wd4m+DANBgkqhkiG9w0BAQsFADBB
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3RlLCBJbmMuMRswGQYDVQQDExJ0
aGF3dGUgU1NMIENBIC0gRzIwHhcNMTUxMDMwMDAwMDAwWhcNMTgxMDI5MjM1OTU5
WjCBkTELMAkGA1UEBhMCQVUxETAPBgNVBAgMCFZpY3RvcmlhMRYwFAYDVQQHDA1I
YXd0aG9ybiBFYXN0MS0wKwYDVQQKDCRDb2xlcyBTdXBlcm1hcmtldHMgQXVzdHJh
bGlhIFB0eSBMdGQxETAPBgNVBAsMCFdlYiBUZWFtMRUwEwYDVQQDDAxjb2xlcy5j
b20uYXUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBoLhSH1Q/3YGQ
d0d5FEza3EaW4bWYDfHtg0oReJ8OvONWYpPgKZ0HZPUi6m6CDhWKXRmxZbiRDGQR
ihRYpAcRnw/DbkN6+eDSHU0I5eZD4YpdbhvAzeVOkiSyQXipvAJZFvomHsjCIvIu
g/2EInwAeTkgDSJY+PUbiiy+Ooko/H2XpjV0+C3eb2RY/pNV+Df5FG1bzVtEWNna
ncqYK9tJlyfDNM9TENzZdxmLtJsFYzlniJcxVgjPw4uSI4N3Cnr3n3PuSKSQuavl
D7k4H7wuwPvOn6l1wAj7fbgq16ZfF9vUvLXsn/yc99V1/7oYIVA4KDxgLhU10IVp
k2JPDZN9AgMBAAGjggGAMIIBfDApBgNVHREEIjAgghB3d3cuY29sZXMuY29tLmF1
ggxjb2xlcy5jb20uYXUwCQYDVR0TBAIwADBuBgNVHSAEZzBlMGMGBmeBDAECAjBZ
MCYGCCsGAQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUuY29tL2NwczAvBggrBgEF
BQcCAjAjDCFodHRwczovL3d3dy50aGF3dGUuY29tL3JlcG9zaXRvcnkwDgYDVR0P
AQH/BAQDAgWgMB8GA1UdIwQYMBaAFMJPSFf80U+awF04fQ4F29kutVJgMCsGA1Ud
HwQkMCIwIKAeoByGGmh0dHA6Ly90ai5zeW1jYi5jb20vdGouY3JsMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjBXBggrBgEFBQcBAQRLMEkwHwYIKwYBBQUH
MAGGE2h0dHA6Ly90ai5zeW1jZC5jb20wJgYIKwYBBQUHMAKGGmh0dHA6Ly90ai5z
eW1jYi5jb20vdGouY3J0MA0GCSqGSIb3DQEBCwUAA4IBAQAkm5BJW0u7ZJFGVWip
xo/poAFHNMImBfofz8WJGN/npJwLKV0mV8FX6dRPEAjPWWT+I/JonwX7VVxAgUWj
3bl+XdIU3hj8cRh3yDnEvhKVKs3orr/jQnxSNx4JZCwmPUIuKK58R1dYCRV/hT7l
YYDMXj+RrAjhrgu7IgZJDCV9kcznBqMdbHLOb/P4PX6j8CsCufS6iGjKhBHTL/49
uuL9ACqH6BgGVctm4kELGcyS6e+VJ9rHI3P+1muOk4wKEVjps9rbPOmpUTk8WhXb
RghAwHrtQZEZMIAVMg2ksCkl/NGRl64Q1W/lW2JHun6ApdXPsGzTvzp3+Kmhrf5p
mLNK
-----END CERTIFICATE-----
subject=/C=AU/ST=Victoria/L=Hawthorn East/O=Coles Supermarkets Australia Pty Ltd/OU=Web Team/CN=coles.com.au
issuer=/C=US/O=thawte, Inc./CN=thawte SSL CA - G2
---
No client certificate CA names sent
Peer signing digest: SHA1
Then go to thawte website(https://search.thawte.com/support/ssl-digital-certificates/index?page=content&id=SO26817&actp=RSS&viewlocale=en_US#links) to download CA certificate(tell me if I did wrong or from wrong place to download) which is thawte_SSL_CA_G2.cer
Third, I have below code to connect
String url = "https://www.coles.com.au";
String strContent = "";
HttpURLConnection connection;
AssetManager assetManager = context.getAssets();
InputStream caInput = null;
try{
CertificateFactory cf = CertificateFactory.getInstance("X.509");
caInput = assetManager.open("thawte_SSL_CA_G2.cer");
Certificate ca = cf.generateCertificate(caInput);
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
URL urlObj = new URL(url);
HttpsURLConnection urlConnection =
(HttpsURLConnection)urlObj.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while(null != (strContent= reader.readLine())){
System.out.println(strContent);
}
}
catch (IOException e){
String exception = e.getMessage();
System.out.println(exception);
}
I use wireshark to capture and observe below happened
-> Client Hello
<- Server Hello
<- Certificate
<- Server Key Exchange
-> Client Key Exchange, Change Cipher Spec, Hello Request, Hello Request
<- FIN
Please help me on why I got handshake failed.

You should add all the ICA (intermediate certificates) and CA.
You could do the following
openssl s_client -showcerts -connect www.coles.com.au:443
Add all the certificates you receive to your trust store. (Strictly speaking only ICA and CA are enough in trust store)

Related

SSL server socket and handshake with known certificate

I am new to SSl server sockets. All I am tying to do is to read data over SSL.
My application listens on port 8000. Please give me few steps on how I can do this. When I have a certificate (on my disc), how can I establish the SSL server socket and read from client ?
Here are my steps
1) reading server.crt from file and making X509Certificate (has public certificate and private key)
2) Getting instance of JKS keystore
3) Get instance of context
4) create server socket over the port (8000)
InputStream in = new DataInputStream(new FileInputStream(new File("server.crt")));
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
in.close();
ks.setCertificateEntry("dts", cert);
char[] newpass = "password".toCharArray();
String name = "mykeystore.ks";
FileOutputStream output = new FileOutputStream(name);
ks.store(output, newpass);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "password".toCharArray());
try{
System.setProperty("javax.net.ssl.keyStore","mykeystore.ks");
System.setProperty("javax.net.ssl.keyStorePassword","password");
System.setProperty("javax.net.debug","all");
SSLContext context = SSLContext.getInstance("TLSv1.2");
context.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory sslServerSocketfactory = context.getServerSocketFactory();
SSLServerSocket sslServerSocket = (SSLServerSocket)sslServerSocketfactory.createServerSocket(8000);
SSLSocket sslSocket = (SSLSocket)sslServerSocket.accept();
InputStream dataIN = sslSocket.getInputStream();
byte[] hello = new byte[20];
dataIN.read(hello);
System.out.println(new String(hello));
dataIN.close();
} catch (IOException e){
e.printStackTrace();
}
I got the answer for my question, I did research on how to setup my own keystore with self signed certificate. This way helped me.
ping me for a detailed solutions.

Communication with server that support ssl in java

I have a question for sslConnectionin java , I write below code for client side ( this application have to connect to server, I have server that support ssl )but I get this error” javax.net.ssl.SSLException: Connection has been shutdown: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target”
How can I resolve my problem ?
public static void main(String[] args) {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
PrintStream out = System.out;
SSLSocketFactory f =
(SSLSocketFactory) SSLSocketFactory.getDefault();
try {
SSLSocket c =
(SSLSocket) f.createSocket("192.168.10.38", 7701);
printSocketInfo(c);
System.out.println("End printSocketInfo");
c.startHandshake();
System.out.println("HandShake oK");
BufferedWriter w = new BufferedWriter(
new OutputStreamWriter(c.getOutputStream()));
BufferedReader r = new BufferedReader(
new InputStreamReader(c.getInputStream()));
String m = null;
// String m=
while ((m=r.readLine())!= null) {
System.out.println("11111");
out.println(m);
m = in.readLine();
System.out.println("M is: "+ m);
w.write(m,0,m.length());
w.newLine();
w.flush();
}
w.close();
r.close();
c.close();
} catch (IOException e) {
System.err.println(e.toString());
}
}
private static void printSocketInfo(SSLSocket s) {
System.out.println("Socket class: "+s.getClass());
System.out.println(" Remote address = "
+s.getInetAddress().toString());
System.out.println(" Remote port = "+s.getPort());
System.out.println(" Local socket address = "
+s.getLocalSocketAddress().toString());
System.out.println(" Local address = "
+s.getLocalAddress().toString());
System.out.println(" Local port = "+s.getLocalPort());
System.out.println(" Need client authentication = "
+s.getNeedClientAuth());
SSLSession ss = s.getSession();
System.out.println(" Cipher suite = "+ss.getCipherSuite());
System.out.println(" Protocol = "+ss.getProtocol());
}
I have a certificate file, how have to use this certificate?
Best Regards
Is it Self Signed certificate
If yes then you have two options
First Option :
Import Certificate authority certificate in Global Java Certificate Trust store. This store is located at
%Java Installation%/jre/lib/security/cacerts
To import it you can use Keytool command which comes with java installation
keytool -import -alias keyName -file yourcertificate.crt -keystore cacerts
Advantage:
No code modification needed.
Simple to deploy
Disadvantage:
cacert file will be overwritten in next java update. You have to
import certificate again.
Requires administrative privileges (both on Linux and windows)
Second Option :
If you want to bypass certificate validation follow Java: Overriding function to disable SSL certificate check
Else
create new Trust store for your program
keytool -import -alias keyName -file yourcertificate.crt -keystore yourtruststore
This command will ask for password two times. Enter any password you want and input "yes" for any questions
A file will be created at current directory by name "yourtruststore"
Now you need to use this trust store in your program
SSLSocketFactory sslFactory = null;
InputStream trustStore = null;
KeyStore keyStore = null;
trustStore = new FileInputStream("<your trust store absolute path>");
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(trustStore, "<your trust store password>".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, tmf.getTrustManagers(), null);
sslFactory = ctx.getSocketFactory();
You can use this socket factory to open new sockets
You have to put the HTTPS certificate to your JVM. To get the certificate from the HTTPS, go through a browser, then click on the "lock" logo on the address bar. You should be able to export the certificate.
Solution for Linux: In $JAVA_HOME/jre/lib/security, use this command:
sudo keytool -import -alias keyName -file /[pathForYourKey]/keyName.cert -keystore cacerts
Default password for "cacerts" is changeit.

Programmatically Obtain KeyStore from PEM

How can one programmatically obtain a KeyStore from a PEM file containing both a certificate and a private key? I am attempting to provide a client certificate to a server in an HTTPS connection. I have confirmed that the client certificate works if I use openssl and keytool to obtain a jks file, which I load dynamically. I can even get it to work by dynamically reading in a p12 (PKCS12) file.
I'm looking into using the PEMReader class from BouncyCastle, but I can't get past some errors. I'm running the Java client with the -Djavax.net.debug=all option and Apache web server with the debug LogLevel. I'm not sure what to look for though. The Apache error log indicates:
...
OpenSSL: Write: SSLv3 read client certificate B
OpenSSL: Exit: error in SSLv3 read client certificate B
Re-negotiation handshake failed: Not accepted by client!?
The Java client program indicates:
...
main, WRITE: TLSv1 Handshake, length = 48
main, waiting for close_notify or alert: state 3
main, Exception while waiting for close java.net.SocketException: Software caused connection abort: recv failed
main, handling exception: java.net.SocketException: Software caused connection abort: recv failed
%% Invalidated: [Session-3, TLS_RSA_WITH_AES_128_CBC_SHA]
main, SEND TLSv1 ALERT: fatal, description = unexpected_message
...
The client code :
public void testClientCertPEM() throws Exception {
String requestURL = "https://mydomain/authtest";
String pemPath = "C:/Users/myusername/Desktop/client.pem";
HttpsURLConnection con;
URL url = new URL(requestURL);
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(getSocketFactoryFromPEM(pemPath));
con.setRequestMethod("GET");
con.setDoInput(true);
con.setDoOutput(false);
con.connect();
String line;
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
while((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
con.disconnect();
}
public SSLSocketFactory getSocketFactoryFromPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
PEMReader reader = new PEMReader(new FileReader(pemPath));
X509Certificate cert = (X509Certificate) reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("alias", cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, null);
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
I noticed the server is outputing SSLv3 in the log while the client is TLSv1. If I add the system property -Dhttps.protocols=SSLv3 then the client will use SSLv3 as well, but I get the same error message. I've also tried adding -Dsun.security.ssl.allowUnsafeRenegotiation=true with no change in outcome.
I've googled around and the usual answer for this question is to just use openssl and keytool first. In my case I need to read the PEM directly on the fly. I'm actually porting a C++ program that already does this, and frankly, I'm very surprised how difficult it is to do this in Java. The C++ code:
curlpp::Easy request;
...
request.setOpt(new Options::Url(myurl));
request.setOpt(new Options::SslVerifyPeer(false));
request.setOpt(new Options::SslCertType("PEM"));
request.setOpt(new Options::SslCert(cert));
request.perform();
I figured it out. The problem is that the X509Certificate by itself isn't sufficient. I needed to put the private key into the dynamically generated keystore as well. It doesn't seem that BouncyCastle PEMReader can handle a PEM file with both cert and private key all in one go, but it can handle each piece separately. I can read the PEM into memory myself and break it into two separate streams and then feed each one to a separate PEMReader. Since I know that the PEM files I'm dealing with will have the cert first and the private key second I can simplify the code at the cost of robustness. I also know that the END CERTIFICATE delimiter will always be surrounded with five hyphens. The implementation that works for me is:
protected static SSLSocketFactory getSocketFactoryPEM(String pemPath) throws Exception {
Security.addProvider(new BouncyCastleProvider());
SSLContext context = SSLContext.getInstance("TLS");
byte[] certAndKey = fileToBytes(new File(pemPath));
String delimiter = "-----END CERTIFICATE-----";
String[] tokens = new String(certAndKey).split(delimiter);
byte[] certBytes = tokens[0].concat(delimiter).getBytes();
byte[] keyBytes = tokens[1].getBytes();
PEMReader reader;
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(certBytes)));
X509Certificate cert = (X509Certificate)reader.readObject();
reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(keyBytes)));
PrivateKey key = (PrivateKey)reader.readObject();
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, "changeit".toCharArray(), new Certificate[] {cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, "changeit".toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
return context.getSocketFactory();
}
Update: It seems this can be done without BouncyCastle:
byte[] certAndKey = fileToBytes(new File(pemPath));
byte[] certBytes = parseDERFromPEM(certAndKey, "-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(certAndKey, "-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
...
protected static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
String data = new String(pem);
String[] tokens = data.split(beginDelimiter);
tokens = tokens[1].split(endDelimiter);
return DatatypeConverter.parseBase64Binary(tokens[0]);
}
protected static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey)factory.generatePrivate(spec);
}
protected static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate)factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
Although the answer of Ryan works well I want to provide an alternative for other developers as I faced a similar challenge in the past where I also needed to handle encrypted private keys in pem format. I have created a library to simplify loading pem files and creating SSLSocketFactory or SSLContext out of it, see here: GitHub - SSLContext Kickstart I hope you like it :)
The pem files can be loaded with the following snippet:
var keyManager = PemUtils.loadIdentityMaterial("certificate-chain.pem", "private-key.pem");
var trustManager = PemUtils.loadTrustMaterial("some-trusted-certificate.pem");
var sslFactory = SSLFactory.builder()
.withIdentityMaterial(keyManager)
.withTrustMaterial(trustManager)
.build();
var sslContext = sslFactory.getSslContext();
var sslSocketFactory = sslFactory.getSslSocketFactory();
Coming back to your main question, with the above snippet it is not needed to create a keystore object from the pem files. It will take care of that under the covers and it will map it to a KeyManager instance.

Where to place SSL certificate for java application

Hello all
I want to generate a certificate using keystore than add this to my sevrer and browse my sever using IE. I need the steps for generating the certificate in plain english as all what i read in the internet is hard to be understod. The server socket is:
SSLServerSocketFactory ssf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
SSLServerSocket Server = (SSLServerSocket)ssf.createServerSocket(1234);
String[] cipher = {"SSL_DH_anon_WITH_RC4_128_MD5"};
Server.setEnabledCipherSuites(cipher);
The certificate code is this but not sure where to pu it in my server:
InputStream infil = new FileInputStream("server.cer");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(infil);
infil.close();
KeyStore ks = null;
ks = KeyStore.getInstance("JKS", "SUN");
InputStream is = null;
is = new FileInputStream(new File("./keystore"));
ks.load(is,"rootroot".toCharArray());
See the Javadoc/Security/JSSE Reference.

How to connect to a secure website using SSL in Java with a pkcs12 file?

I have a pkcs12 file. I need to use this to connect to a webpage using https protocol. I came across some code where in order to connect to a secure web page i need to set the following system properties:
System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStore", "new_cert.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "newpass");
I have the p12(pkcs12) file. All I need is a truststore file.
I extracted the certificates using:
openssl.exe pkcs12 -in c:/mykey.p12 -out c:/cert.txt -nokeys -clcerts
Now converted the cert PEM file to der
openssl.exe x509 -in c:/cert.txt -outform DER -out c:/CAcert.der
Now adding the der file to a keystore
keytool -import -file C:/Cacert.der -keystore mytruststore
Now I have the truststore, but when I use it, I get the following error
Exception in thread "main" java.net.SocketException: java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: Default, provider: SunJSSE, class: com.sun.net.ssl.internal.ssl.DefaultSSLContextImpl)
Update:
After removing certain properties and setting only the "trustStore", "trustStorePassword" and "trustStoreType" property, I got the following exception
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
Please Help.
For anyone encountering a similar situation I was able to solve the issue above as follows:
Regenerate your pkcs12 file as follows:
openssl pkcs12 -in oldpkcs.p12 -out keys -passout pass:tmp
openssl pkcs12 -in keys -export -out new.p12 -passin pass:tmp -passout pass:newpasswd
Import the CA certificate from server into a TrustStore ( either your own, or the java keystore in $JAVA_HOME/jre/lib/security/cacerts, password: changeit).
Set the following system properties:
System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStore", "new.p12");
System.setProperty("javax.net.ssl.keyStorePassword", "newpasswd");
Test ur url.
Courtesy# http://forums.sun.com/thread.jspa?threadID=5296333
I cannot comment because of the 50pts threshhold, but I don't think that the answer provided in https://stackoverflow.com/a/537344/1341220 is correct.
What you are actually describing is how you insert server certificates into the systems default truststore:
$JAVA_HOME/jre/lib/security/cacerts, password: changeit)
This works, indeed, but it means that you did not really specify a trust store local to your project, but rather accepted the certificate universially in your system.
You actually never use your own truststore that you defined here:
System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
It appears that you are extracting you certificate from the PKCS #12 key store and creating a new Java key store (with type "JKS"). You don't strictly have to provide a trust store password (although using one allows you to test the integrity of your root certificates).
So, try your program with only the following SSL properties set. The list shown in your question is over-specified and may be causing problems.
System.setProperty("javax.net.ssl.trustStore", "myTrustStore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
Also, using the PKCS #12 file directly as the trust store should work, as long as the CA certificate is detected as a "trusted" entry. But in that case, you'll have to specify the javax.net.ssl.trustStoreType property as "PKCS12" too.
Try with these properties only. If you get the same error, I suspect your problem is not the key store. If it still occurs, post more of the stack trace in your question to narrow the problem down.
The new error, "the trustAnchors parameter must be non-empty," could be due to setting the javax.net.ssl.trustStore property to a file that doesn't exist; if the file cannot be opened, an empty key store created, which would lead to this error.
This is an example to use ONLY p12 file it's not optimazed but it work.
The pkcs12 file where generated by OpenSSL by me.
Example how to load p12 file and build Trust zone from it...
It outputs certificates from p12 file and add good certs to TrustStore
KeyStore ks=KeyStore.getInstance("pkcs12");
ks.load(new FileInputStream("client_t_c1.p12"),"c1".toCharArray());
KeyStore jks=KeyStore.getInstance("JKS");
jks.load(null);
for (Enumeration<String>t=ks.aliases();t.hasMoreElements();)
{
String alias = t.nextElement();
System.out.println("#:" + alias);
if (ks.isKeyEntry(alias)){
Certificate[] a = ks.getCertificateChain(alias);
for (int i=0;i<a.length;i++)
{
X509Certificate x509 = (X509Certificate)a[i];
System.out.println(x509.getSubjectDN().toString());
if (i>0)
jks.setCertificateEntry(x509.getSubjectDN().toString(), x509);
System.out.println(ks.getCertificateAlias(x509));
System.out.println("ok");
}
}
}
System.out.println("init Stores...");
KeyManagerFactory kmf=KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "c1".toCharArray());
TrustManagerFactory tmf=TrustManagerFactory.getInstance("SunX509");
tmf.init(jks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
URL url = new URL("https://test.domain:443");
String keyStore = "server.p12"
String keyStorePassword = "changeit";
String keyPassword = "changeit";
String KeyStoreType= "PKCS12";
String KeyManagerAlgorithm = "SunX509";
String SSLVersion = "SSLv3";
public HttpURLConnection getHttpsURLConnection(URL url, String keystore,
String keyStorePass,String keyPassword, String KeyStoreType
,String KeyManagerAlgorithm, String SSLVersion)
throws NoSuchAlgorithmException, KeyStoreException,
CertificateException, FileNotFoundException, IOException,
UnrecoverableKeyException, KeyManagementException {
System.setProperty("javax.net.debug","ssl,handshake,record");
SSLContext sslcontext = SSLContext.getInstance(SSLVersion);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerAlgorithm);
KeyStore ks = KeyStore.getInstance(KeyStoreType);
ks.load(new FileInputStream(keystore), keyStorePass.toCharArray());
kmf.init(ks, keyPassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
TrustManager[] tm = tmf.getTrustManagers();
sslcontext.init(kmf.getKeyManagers(), tm, null);
SSLSocketFactory sslSocketFactory = sslcontext.getSocketFactory();
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
HttpsURLConnection httpsURLConnection = ( HttpsURLConnection)uRL.openConnection();
return httpsURLConnection;
}
This example shows how you can layer SSL on top of an existing socket, obtaining the client cert from a PKCS#12 file. It is appropriate when you need to connect to an upstream server via a proxy, and you want to handle the full protocol by yourself.
Essentially, however, once you have the SSL Context, you can apply it to an HttpsURLConnection, etc, etc.
KeyStore ks = KeyStore.getInstance("PKCS12");
InputStream is = ...;
char[] ksp = storePassword.toCharArray();
ks.load(is, ksp);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
char[] kp = keyPassword.toCharArray();
kmf.init(ks, kp);
sslContext = SSLContext.getInstance("SSLv3");
sslContext.init(kmf.getKeyManagers(), null, null);
SSLSocketFactory factory = sslContext.getSocketFactory();
SSLSocket sslsocket = (SSLSocket) factory.createSocket(socket, socket
.getInetAddress().getHostName(), socket.getPort(), true);
sslsocket.setUseClientMode(true);
sslsocket.setSoTimeout(soTimeout);
sslsocket.startHandshake();
The following steps will help you to sort your problem out.
Steps:
developer_identity.cer <= download from Apple
mykey.p12 <= Your private key
Commands to follow:
openssl x509 -in developer_identity.cer -inform DER -out developer_identity.pem -outform PEM
openssl pkcs12 -nocerts -in mykey.p12 -out mykey.pem
openssl pkcs12 -export -inkey mykey.pem -in developer_identity.pem -out iphone_dev.p12
Final p12 that we will require is iphone_dev.p12 file and the passphrase.
use this file as your p12 and then try. This indeed is the solution.:)
I realise that this article may be outdated but still I would like to ask smithsv to correct his source code, it contains many mistakes, I managed to correct most of them but still don't know what kind of object x509 could be.Here is the source code as I think is should be:
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
public class Connection2 {
public void connect() {
/*
* This is an example to use ONLY p12 file it's not optimazed but it
* work. The pkcs12 file where generated by OpenSSL by me. Example how
* to load p12 file and build Trust zone from it... It outputs
* certificates from p12 file and add good certs to TrustStore
*/
KeyStore ks = KeyStore.getInstance( "pkcs12" );
ks.load( new FileInputStream( cert.pfx ), "passwrd".toCharArray() );
KeyStore jks = KeyStore.getInstance( "JKS" );
jks.load( null );
for( Enumeration t = ks.aliases(); t.hasMoreElements(); ) {
String alias = (String )t.nextElement();
System.out.println( "#:" + alias );
if( ks.isKeyEntry( alias ) ) {
Certificate[] a = ks.getCertificateChain( alias );
for( int i = 0; i == 0; )
jks.setCertificateEntry( x509Cert.getSubjectDN().toString(), x509 );
System.out.println( ks.getCertificateAlias( x509 ) );
System.out.println( "ok" );
}
}
System.out.println( "init Stores..." );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( ks, "c1".toCharArray() );
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( jks );
SSLContext ctx = SSLContext.getInstance( "TLS" );
ctx.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );
}
}

Categories