SSL Validation with the ViPR Java Controller Client

With release 2.1 of the ViPR Controller Java Client, the certificates presented by your ViPR controller will now be strongly validated by default to prevent man-in-the-middle attacks.  The downside here is that by default ViPR installs a self-signed certificate to identify the ViPR nodes and Java will not trust a self-signed certificate (again, by default).

 

If you have certificate validation enabled and Java can't validate your certificate, you'll get an exception.  To demonstrate this, I've created a runnable JAR that uses the ViPR Controller Java Client to connect and show the name of the current tenant.  If you run it with the default trust store (cacerts), you'll get a SSLHandshakeException:

 

$ java -jar ViprConnectTest.jar

Exception in thread "main" 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


 

This means that Java couldn't trace ViPR's self-signed certificate up to a trusted root certificate.  To remedy this, you have a few options:

 

  1. Procure a new SSL certificate from a trusted provider (e.g. Verisign) and install it on your ViPR controller nodes.
  2. Register ViPR's certificate as "trusted".
  3. Turn off certificate validation.

 

Registering a Trusted Certificate

The most secure solution is to procure a certificate from a trusted provider.  This ensures that clients can independently verify the server's certificate through a trust chain using the CA public keys that are installed on client machines.  The process of getting a certificate will vary based on the provider you select and your company's IT policies.  You can install your certificate by following these instructions.

 

Trusting ViPR's Certificate

The second solution involves downloading ViPR's certificate and installing it as a trusted certificate in your Java environment.  You can download the certificate using openssl:

 

$ export VIPR_VIP=xxx.xxx.xxx.xxx
$ echo | openssl s_client -connect $VIPR_VIP:443 2>&1 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > cert.pem


 

After you've downloaded the certificate, you can use keytool to install it in a trust store.  You have a few options here:

  1. Install it in your Java's "cacerts" file.  This will make the certificate trusted by all applications.  Requires administrative privileges.
  2. Install it in some other file and set your application's trust store to that file.  This has the side-effect of removing all the default certificates from your default trust store, but does not require admin privileges.  You could also copy cacerts to your local directory and add your certificate.  This will retain the default certificates in the trust store, but will not update that trust store if Java
  3. Write your own SSL socket factory to validate ViPR's certificate.  This is beyond the scope of this article, but you can register your socket factory with the setSocketFactory method on your ClientConfig.

 

Installing in the "cacerts" File.

Inside your Java JRE there is a file lib/security/cacerts file, for example JDK7 on Mac:

 

$ keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 79 entries

digicertassuredidrootca, Apr 16, 2008, trustedCertEntry,
Certificate fingerprint (SHA1): 05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43
trustcenterclass2caii, Apr 29, 2008, trustedCertEntry,
Certificate fingerprint (SHA1): AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E
thawtepremiumserverca, Dec 11, 2009, trustedCertEntry,
Certificate fingerprint (SHA1): E0:AB:05:94:20:72:54:93:05:60:62:02:36:70:F7:CD:2E:FC:66:66
...


 

You can add your certificate to this file:

 

$ sudo keytool -importcert -file cert.pem -alias vipr_controller  -keystore /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit
Password:
Owner: CN=xxx.xxx.xxx.xxx
Issuer: CN=xxx.xxx.xxx.xxx
Serial number: 613aa6eba76bd975
Valid from: Fri Oct 10 11:48:48 CDT 2014 until: Mon Oct 07 11:48:48 CDT 2024
Certificate fingerprints:
  MD5:  C5:37:E9:79:CF:E6:61:F3:B3:04:A8:3D:D9:36:9E:C2
  SHA1: ED:17:A6:4B:1F:78:E5:72:65:B7:49:58:AB:13:7F:F7:28:77:29:35
  SHA256: B9:EA:0C:5E:35:F7:BA:79:DB:43:73:C0:02:A4:4F:81:B3:7E:52:47:D3:B2:F4:04:F5:7F:33:0F:C6:10:8A:78
  Signature algorithm name: SHA256withRSA
  Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 0B 50 70 47 E9 AA 66 F2   09 96 5A 9F 7D FA 09 56  .PpG..f...Z....V
0010: 48 60 A5 4F                                        H`.O
]
]

#2: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  IPAddress: xxx.xxx.xxx.xxx
  DNSName: vipr3
  IPAddress: xxx.xxx.xxx.xxx
  DNSName: vipr2
  IPAddress: xxx.xxx.xxx.xxx
  DNSName: vipr1
  IPAddress: xxx.xxx.xxx.xxx
]

#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 0B 50 70 47 E9 AA 66 F2   09 96 5A 9F 7D FA 09 56  .PpG..f...Z....V
0010: 48 60 A5 4F                                        H`.O
]
]

Trust this certificate? [no]:  yes
Certificate was added to keystore


 

Creating a New Trust Store

To create your own trust store, you first use keytool to create a new certificate store and load your certificate.  You can do this all at once; if the store does not exist keytool will create it.  In this example, I create a new certificate store called "mycerts" in my home directory:

 

$ keytool -importcert -file cert.pem -alias vipr_controller  -keystore mycerts -storepass changeit
Owner: CN=xxx.xxx.xxx.xxx
Issuer: CN=xxx.xxx.xxx.xxx
Serial number: 613aa6eba76bd975
Valid from: Fri Oct 10 11:48:48 CDT 2014 until: Mon Oct 07 11:48:48 CDT 2024
Certificate fingerprints:
  MD5:  C5:37:E9:79:CF:E6:61:F3:B3:04:A8:3D:D9:36:9E:C2
  SHA1: ED:17:A6:4B:1F:78:E5:72:65:B7:49:58:AB:13:7F:F7:28:77:29:35
  SHA256: B9:EA:0C:5E:35:F7:BA:79:DB:43:73:C0:02:A4:4F:81:B3:7E:52:47:D3:B2:F4:04:F5:7F:33:0F:C6:10:8A:78
  Signature algorithm name: SHA256withRSA
  Version: 3

Extensions:

#1: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 0B 50 70 47 E9 AA 66 F2   09 96 5A 9F 7D FA 09 56  .PpG..f...Z....V
0010: 48 60 A5 4F                                        H`.O
]
]

#2: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  IPAddress: xxx.xxx.xxx.xxx
  DNSName: vipr3
  IPAddress: xxx.xxx.xxx.xxx
  DNSName: vipr2
  IPAddress: xxx.xxx.xxx.xxx
  DNSName: vipr1
  IPAddress: xxx.xxx.xxx.xxx
]

#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 0B 50 70 47 E9 AA 66 F2   09 96 5A 9F 7D FA 09 56  .PpG..f...Z....V
0010: 48 60 A5 4F                                        H`.O
]
]

Trust this certificate? [no]:  yes
Certificate was added to keystore


 

After creating your new trust store, you can start your application and override the default trust store with the Java system properties javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword:

 

$ java -Djavax.net.ssl.trustStore=mycerts -Djavax.net.ssl.trustStorePassword=changeit \ 
-jar ViprConnectTest.jar
Current tenant: Provider Tenant

As you can see, the application runs without error and prints out the name of the current tenant.

 

Turning Off Certificate Validation

The final solution is to simply disable certificate validation.  This is not recommended for production because it will blindly trust any certificate sent to the client, allowing a man-in-the-middle attacker to intercept API calls to the controller.  However, it is useful for development so you can get up and running quickly.  To disable certificate validation, pass true as the second argument to your ViPRCoreClient constructor:

 

ViPRCoreClient client = new ViPRCoreClient("vipr-vip", true).withLogin("user", "password"); 


 

Summary

As you can see, SSL validation is not trivial, but getting it correctly configured is critical to your application's security.  For most users, the ViPR portal will validate the certificate from the controllers using the internal database, but if you're leveraging the APIs to automate your own scripts, you will need to do this as well.  As stated above, the best solution would be to get a trusted certificate, but trusting ViPR's certificate is also acceptable for many organizations.  If your company has an IT security organization, please consult with them about recommended practices.

 

For reference, the test application in ViprConnectTest.jar is:

 

import com.emc.storageos.model.tenant.TenantResponse;
import com.emc.vipr.client.ViPRCoreClient;


public class ViprConnectTest {
  public static void main(String[] args) {
    // Construction by hostname only 
    ViPRCoreClient client = new ViPRCoreClient("xxx.xxx.xxx.xxx", false).withLogin("user", "password");

    TenantResponse tenant = client.tenants().current();
    System.out.println("Current tenant: " + tenant.getName());
    client.auth().logout();
  }
}