I have a .cer file provided from some other party. I need to create a saml credential with this .cer file.
For this, I imported .cer file to jks file using following command. ( Password is same as password. It asked from prompt to accept certificate. I gave y then it said certificate is added to keystore )
keytool -importcert -file xyz.cer -keystore test.jks -alias "testsp"
Then I used this jks file to create credential as below.
private Credential getCredential() {
KeyStore keystore = readKeystoreFromFile("C:\\Users\\WTC\\Downloads\\icicistage\\test.jks", "password");
Map<String, String> passwordMap = new HashMap<String, String>();
passwordMap.put("testsp", "password");
KeyStoreCredentialResolver resolver = new KeyStoreCredentialResolver(keystore, passwordMap);
Criteria criteria = new EntityIDCriteria("testsp");
CriteriaSet criteriaSet = new CriteriaSet(criteria);
Credential credential = null;
try {
credential = resolver.resolveSingle(criteriaSet);
} catch (SecurityException e) {
e.printStackTrace();
}
return credential;
}
private static KeyStore readKeystoreFromFile(String pathToKeyStore, String keyStorePassword) {
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream inputStream = new FileInputStream(pathToKeyStore);
keystore.load(inputStream, keyStorePassword.toCharArray());
inputStream.close();
return keystore;
} catch (Exception e) {
throw new RuntimeException("Something went wrong reading keystore", e);
}
}
The below line gives me the following error in try block.
credential = resolver.resolveSingle(criteriaSet);
java.lang.UnsupportedOperationException: trusted certificate entries are not password-protected
Can anyone please guide me to solve this issue ?
Solved the issue.
We no need to give the password in the password map. Since certificate contains only public key. It wont take the password.
Removed the below line from code and it works fine.
passwordMap.put("testsp", "password");
Related
My network code is written in NDK (cURL + OpenSSL) and I'd like to use a certificate from Android's credential storage as a client certificate for a SSL connection. Moreover, I'd like to offer a list of available certificates to the user, so he can choose the certificate for the connection. Unfortunately, I cannot obtain a certificate from the key storage.
I installed a client certificate to "Credential storage" (Settings -> Secutrity -> ...) on my Android device (5.0.2), but I'm not able to access it from Java. I tried to call following code, but the key storage is empy, athough the certificate is installed in the Credential storage:
//KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
String alias = (String)aliases.nextElement();
Log.i("app", "alias name: " + alias);
Certificate certificate = ks.getCertificate(alias);
Log.i("app", certificate.toString());
}
What am I doing wrong?
User credentials installed on the device are available through Android KeyChain, not Android KeyStore
The KeyChain class provides access to private keys and their corresponding certificate chains in credential storage.
Use choosePrivateKeyAlias to prompt the user for selecting the certificate. The system launches an Activity for the user to select the alias and returns it via a callback. Then use getPrivateKey and getCertificate to recover the key and the corresponding certificate chain
KeyChain.choosePrivateKeyAlias(activity, new KeyChainAliasCallback() {
public void alias(String alias) {
//do something with the selected alias
}
},
new String[] { KeyProperties.KEY_ALGORITHM_RSA, "DSA"}, // List of acceptable key types. null for any
null, // issuer, null for any
null, // host name of server requesting the cert, null if unavailable
-1, // port of server requesting the cert, -1 if unavailable
""); // alias to preselect, null if unavailable
PrivateKey privateKey = KeyChain.getPrivateKey(activity, alias);
X509Certificate chain[] = KeyChain.getCertificateChain(activity, alias);
Try something like this:
X509TrustManager manager = null;
FileInputStream fs = null;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try
{
fs = new FileInputStream(System.getProperty("javax.net.ssl.trustStore"));
keyStore.load(fs, null);
}
finally
{
if (fs != null) { fs.close(); }
}
trustManagerFactory.init(keyStore);
TrustManager[] managers = trustManagerFactory.getTrustManagers();
for (TrustManager tm : managers)
{
if (tm instanceof X509TrustManager)
{
manager = (X509TrustManager) tm;
break;
}
}
I'm trying to create an array of TrustManagers populated with CA certificates decoded from Base64-encoded PEM Strings to pass it in SSLSocketFactory. Here is my code:
public static TrustManager[] getCustomTrustManagers(List<CertObject> certObjects)
{
TrustManagerFactory tmf;
try
{
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
if (AndroidBuild.getSdkVersion() >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH)
{
KeyStore trustStore;
try
{
trustStore = KeyStore.getInstance("BKS", "BC");
trustStore.load (null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
for (CertObject certObject : certObjects)
{
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(certObject.getData().getBytes());
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(decoded));
trustStore.setCertificateEntry(certObject.getName(), cert);
}
}
catch (KeyStoreException | NoSuchAlgorithmException | IOException e)
{
trustStore = null;
}
tmf.init(trustStore);
}
else
{
tmf.init((KeyStore) null);
}
}
catch (NoSuchAlgorithmException | KeyStoreException e)
{
Log.e(TAG, e.getMessage);
}
return tmf.getTrustManagers();
}
The certificate is successfully generated, I see the valid instance of X509Certificate, but then it fails in
trustStore.setCertificateEntry(certObject.getName(), cert);
I have the following error:
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference
at java.util.Collections.secondaryHash(Collections.java:3405)
at java.util.Hashtable.get(Hashtable.java:265)
at com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi.engineSetCertificateEntry(BcKeyStoreSpi.java:638)
at java.security.KeyStore.setCertificateEntry(KeyStore.java:393)
at com.xxx.app.core.Utils.getCustomTrustManagers(Utils.java:58)
Any ideas what is wrong? How should I add certificates to the keystore? certObject.getName() and certObject.getData() are both non-empty Strings. certObject.getData() contains PEM certificate data encoded with Base64 without -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- Lines.
Thanks in advance!
Try using a different alias looks like certObject.getName() is null.
trustStore.setCertificateEntry("MyAlias", cert);
MyAlias you have to replace with some dynamic String. I just used as an example.
I am trying to verify the public key of a certificate. The certificate has been imported into a keystore using this command:
keytool -importcert -file cert.cer -keystore kstore.jks -alias mycert -storepass changeit
This is the java code I use to verify the public key:
File keyStore = new File("kstore.jks");
String keyStorePassword = "changeit";
KeyStore ks = null;
try {
ks = KeyStore.getInstance("jks");
ks.load(keyStore.toURI().toURL().openStream(), keyStorePassword.toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
try {
Certificate cert = ks.getCertificate("mycert");
PublicKey pk = cert.getPublicKey();
cert.verify(pk);
//cert.verify(pk, "SunRsaSign");
System.out.println("Keys verified");
} catch (Exception e) {
e.printStackTrace();
}
The exception I get is:
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:446)
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:389)
at VerifyEBXMLSignature.runIt3(VerifyEBXMLSignature.java:62)
at VerifyEBXMLSignature.main(VerifyEBXMLSignature.java:41)
The certificate contains a public key and I do not have access to the private key.
Is it at all possible to verify the public key against this certificate that I import into a keystore? The public key comes from the certificate itself, so it should be correct.
What more should I look for with the certificate?
I just got some more iformation about the certificate: It is exported from the private key. Is there anything in that process that may have be done wrong?
You shouldn't be passing in the public key that you extracted from the certificate. You should be passing in the public key of the issuer's certificate to verify the signature.
So, as Robert pointed out in comments, your above code only works if it's a self-signed certificate (the certificate is signed with itself).
The public key verify method internally uses X509 Certificate implementation.
So it can only verify those certificates which are generated as per X509 standards.
For more info Visit http://en.wikipedia.org/wiki/X.509
I have created my own SSLSocketFactory as explained in this question
private SSLSocketFactory newSslSocketFactory() {
try {
// Get an instance of the Bouncy Castle KeyStore format
KeyStore trusted = KeyStore.getInstance("BKS");
// Get theraw resource, which contains the keystore with
// your trusted certificates (root and any intermediate certs)
Context context = this.activity;
Resources _resources = context.getResources();
InputStream in = _resources.openRawResource(R.raw.mystore);
try {
// Initialize the keystore with the provided trusted certificates
// Also provide the password of the keystore
trusted.load(in, "mypassword".toCharArray());
} finally {
in.close();
}
// Pass the keystore to the SSLSocketFactory. The factory is responsible
// for the verification of the server certificate.
SSLSocketFactory sf = new SSLSocketFactory(trusted);
// Hostname verification from certificate
sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
return sf;
} catch (Exception e) {
throw new AssertionError(e);
}
}
Actually i need to set this SSLSocketFactory on the HttpsURLConnection before connecting. But when i try to set it on HttpsURLConnection by calling
(HttpsURLConnection )connection.setSSLSocketFactory(trusted);
At this point am facing cast class error between 2 packages org.apache.http.conn.ssl.SSLSocketFactory and javax.net.ssl.SSLSocketFactory.
How to solve this?
Am not getting any exception with the above piece of code.
But the problem is that, when am trying to set the SSLSocketFactory on the HttpsURLConnection using:
(HttpsURLConnection )connection.setSSLSocketFactory(trusted)
It is showing "The method setSSLSocketFactory(SSLSocketFactory) in the type HttpsURLConnection is not applicable for the arguments(SSLSocketFactory)".
Because the method setSSLSocketFactory is there in both the packages.
I have a REST server made in Grizzly that uses HTTPS and works wonderfully with Firefox. Here's the code:
//Build a new Servlet Adapter.
ServletAdapter adapter=new ServletAdapter();
adapter.addInitParameter("com.sun.jersey.config.property.packages", "My.services");
adapter.addInitParameter(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS, SecurityFilter.class.getName());
adapter.setContextPath("/");
adapter.setServletInstance(new ServletContainer());
//Configure SSL (See instructions at the top of this file on how these files are generated.)
SSLConfig ssl=new SSLConfig();
String keystoreFile=Main.class.getResource("resources/keystore_server.jks").toURI().getPath();
System.out.printf("Using keystore at: %s.",keystoreFile);
ssl.setKeyStoreFile(keystoreFile);
ssl.setKeyStorePass("asdfgh");
//Build the web server.
GrizzlyWebServer webServer=new GrizzlyWebServer(getPort(9999),".",true);
//Add the servlet.
webServer.addGrizzlyAdapter(adapter, new String[]{"/"});
//Set SSL
webServer.setSSLConfig(ssl);
//Start it up.
System.out.println(String.format("Jersey app started with WADL available at "
+ "%sapplication.wadl\n",
"https://localhost:9999/"));
webServer.start();
Now, I try to reach it in Java:
SSLContext ctx=null;
try {
ctx = SSLContext.getInstance("SSL");
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
ClientConfig config=new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(null,ctx));
WebResource service=Client.create(new DefaultClientConfig()).resource("https://localhost:9999/");
//Attempt to view the user's page.
try{
service
.path("user/"+username)
.get(String.class);
}
And get:
com.sun.jersey.api.client.ClientHandlerException: 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
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:128)
at com.sun.jersey.api.client.Client.handle(Client.java:453)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:557)
at com.sun.jersey.api.client.WebResource.get(WebResource.java:179)
From examples that I've found on the web, it seems like I would need to setup a Truststore then setup some sort of TrustManager. This seems like a lot of code and setup work for my simple little project. Is there an easier way to just say..I trust this cert and point to a .cert file?
When you say "is there an easier way to... trust this cert", that's exactly what you're doing by adding the cert to your Java trust store. And this is very, very easy to do, and there's nothing you need to do within your client app to get that trust store recognized or utilized.
On your client machine, find where your cacerts file is (that's your default Java trust store, and is, by default, located at <java-home>/lib/security/certs/cacerts.
Then, type the following:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to cacerts>
That will import the cert into your trust store, and after this, your client app will be able to connect to your Grizzly HTTPS server without issue.
If you don't want to import the cert into your default trust store -- i.e., you just want it to be available to this one client app, but not to anything else you run on your JVM on that machine -- then you can create a new trust store just for your app. Instead of passing keytool the path to the existing, default cacerts file, pass keytool the path to your new trust store file:
keytool -import -alias <Name for the cert> -file <the .cer file> -keystore <path to new trust store>
You'll be asked to set and verify a new password for the trust store file. Then, when you start your client app, start it with the following parameters:
java -Djavax.net.ssl.trustStore=<path to new trust store> -Djavax.net.ssl.trustStorePassword=<trust store password>
Easy cheesy, really.
Here's the painful route:
SSLContext ctx = null;
try {
KeyStore trustStore;
trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("C:\\truststore_client"),
"asdfgh".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance("SunX509");
tmf.init(trustStore);
ctx = SSLContext.getInstance("SSL");
ctx.init(null, tmf.getTrustManagers(), null);
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
ClientConfig config = new DefaultClientConfig();
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new HTTPSProperties(null, ctx));
WebResource service = Client.create(config).resource(
"https://localhost:9999/");
service.addFilter(new HTTPBasicAuthFilter(username, password));
// Attempt to view the user's page.
try {
service.path("user/" + username).get(String.class);
} catch (Exception e) {
e.printStackTrace();
}
Gotta love those six different caught exceptions :). There are certainly some refactoring to simplify the code a bit. But, I like delfuego's -D options on the VM. I wish there was a javax.net.ssl.trustStore static property that I could just set. Just two lines of code and done. Anyone know where that would be?
This may be too much to ask, but, ideally the keytool would not be used. Instead, the trustedStore would be created dynamically by the code and the cert is added at runtime.
There must be a better answer.
Something to keep in mind is that this error isn't only due to self signed certs. The new Entrust CA certs fail with the same error, and the right thing to do is to update the server with the appropriate root certs, not to disable this important security feature.
Check this out: http://code.google.com/p/resting/. I could use resting to consume
HTTPS REST services.
The answer of delfuego is the simplest way to solve the certificate problem. But, in my case, one of our third party url (using https), updated their certificate every 2 months automatically. It means that I have to import the cert to our Java trust store manually every 2 months as well. Sometimes it caused production problems.
So, I made a method to solve it with SecureRestClientTrustManager to be able to consume https url without importing the cert file.
Here is the method:
public static String doPostSecureWithHeader(String url, String body, Map headers)
throws Exception {
log.info("start doPostSecureWithHeader " + url + " with param " + body);
long startTime;
long endTime;
startTime = System.currentTimeMillis();
Client client;
client = Client.create();
WebResource webResource;
webResource = null;
String output = null;
try{
SSLContext sslContext = null;
SecureRestClientTrustManager secureRestClientTrustManager = new SecureRestClientTrustManager();
sslContext = SSLContext.getInstance("SSL");
sslContext
.init(null,
new javax.net.ssl.TrustManager[] { secureRestClientTrustManager },
null);
DefaultClientConfig defaultClientConfig = new DefaultClientConfig();
defaultClientConfig
.getProperties()
.put(com.sun.jersey.client.urlconnection.HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new com.sun.jersey.client.urlconnection.HTTPSProperties(
getHostnameVerifier(), sslContext));
client = Client.create(defaultClientConfig);
webResource = client.resource(url);
if(headers!=null && headers.size()>0){
for (Map.Entry entry : headers.entrySet()){
webResource.setProperty(entry.getKey(), entry.getValue());
}
}
WebResource.Builder builder =
webResource.accept("application/json");
if(headers!=null && headers.size()>0){
for (Map.Entry entry : headers.entrySet()){
builder.header(entry.getKey(), entry.getValue());
}
}
ClientResponse response = builder
.post(ClientResponse.class, body);
output = response.getEntity(String.class);
}
catch(Exception e){
log.error(e.getMessage(),e);
if(e.toString().contains("One or more of query value parameters are null")){
output="-1";
}
if(e.toString().contains("401 Unauthorized")){
throw e;
}
}
finally {
if (client!= null) {
client.destroy();
}
}
endTime = System.currentTimeMillis();
log.info("time hit "+ url +" selama "+ (endTime - startTime) + " milliseconds dengan output = "+output);
return output;
}