I'm trying to connect from Android to a Apache Vysper XMPP server in localhost. I'm using Smack framework to perform XMPP operations:
AbstractXMPPConnection connection = new XMPPTCPConnection("bigdestroyer", "", ip);
try {
connection.setPacketReplyTimeout(10000);
connection.connect();
} catch (SmackException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (XMPPException e) {
e.printStackTrace();
}
But I get this error:
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException: Trust anchor for
certification path not found.
I guess it is related to SSL certificate but I don't know what I have to do. Can you help me?
I have tried to put cert
file (the same as the server has) in assets
folder and create the connection this way:
XMPPTCPConnectionConfiguration connectionConfiguration = configuration.setConnectTimeout(10000)
.setUsernameAndPassword("admin", "admin")
.setHost(ip)
.setServiceName(ip)
.setKeystorePath("file:///android_asset/bogus_mina_tls.cert")
.build();
XMPPTCPConnection connection = new XMPPTCPConnection(connectionConfiguration);
But it still doesn't work. Any suggestion?
The KeystorePath should point to a Keystore, not a simple certificate. Android uses KeystoreType BKS by default, so you should create one and import your certificate into it as described by http://blog.antoine.li/2010/10/22/android-trusting-ssl-certificates/
keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret
You can also do this using Portecle (http://portecle.sourceforge.net/), if you don't want to mess with the command line.
Android Training might solve this exact problem:
Common Problems Verifying Server Certificates This can happen for several reasons, including:
- The CA that issued the server certificate was unknown
- The server certificate wasn't signed by a CA, but was self signed
- The server configuration is missing an intermediate CA
The following sections discuss how to address these problems while
keeping your connection to the server secure.
Unknown certificate authority
In this case, the SSLHandshakeException occurs because you have a CA
that isn't trusted by the system. It could be because you have a
certificate from a new CA that isn't yet trusted by Android or your
app is running on an older version without the CA. More often a CA is
unknown because it isn't a public CA, but a private one issued by an
organization such as a government, corporation, or education
institution for their own use.
Fortunately, you can teach HttpsURLConnection to trust a specific set
of CAs. The procedure can be a little convoluted, so below is an
example that takes a specific CA from an InputStream, uses it to
create a KeyStore, which is then used to create and initialize a
TrustManager. A TrustManager is what the system uses to validate
certificates from the server and—by creating one from a KeyStore with
one or more CAs—those will be the only CAs trusted by that
TrustManager.
Given the new TrustManager, the example initializes a new SSLContext
which provides an SSLSocketFactory you can use to override the default
SSLSocketFactory from HttpsURLConnection. This way the connection will
use your CAs for certificate validation.
Here is the example in full using an organizational CA from the
University of Washington:
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
With a custom TrustManager that knows about your CAs, the system is
able to validate that your server certificate come from a trusted
issuer.
Caution: Many web sites describe a poor alternative solution which is
to install a TrustManager that does nothing. If you do this you might
as well not be encrypting your communication, because anyone can
attack your users at a public Wi-Fi hotspot by using DNS tricks to
send your users' traffic through a proxy of their own that pretends to
be your server. The attacker can then record passwords and other
personal data. This works because the attacker can generate a
certificate and—without a TrustManager that actually validates that
the certificate comes from a trusted source—your app could be talking
to anyone. So don't do this, not even temporarily. You can always make
your app trust the issuer of the server's certificate, so just do it.
The page continues with additional possibilities, but this one seems like it may be the most relevant.
There are 2 approaches to solve your problem:
Fix your server by having it's certificates signed by a trusted 3rd party.
Have your client accept the self-signed certificate you are using right now.