Can I avoid putting key store password on command-line with JSSE?
Asked Answered
C

4

9

We are using Maven 2 and have a maven repository manager secured with SSL client authentication. In order for Maven to access the repository the following system properties must be passed to Java:

javax.net.ssl.trustStore=trust.jks
javax.net.ssl.trustStorePassword=<trustPass>
javax.net.ssl.keyStore=keystore.p12
javax.net.ssl.keyStoreType=pkcs12
javax.net.ssl.keyStorePassword=<keyStorePass>

See this mini-guide for more details.

In order to set these system properties in Maven, I have to use the MAVEN_OPTS environment variable (or pass them directly on the command-line). Either way, when Maven actually executes, all of these properties become visible to other users on the system (via ps), including my key store password.

Is there a way to set these properties so that the password doesn't get exposed on the command-line?

Calan answered 9/3, 2011 at 20:55 Comment(8)
Are you running on one particular platform?Detrition
I know nothing about Maven, but can't you somehow read properties from a file?Mollie
@Detrition We are using linux (ubuntu) and Mac as development platforms.Calan
@Paulo If I had control over Maven's http client, I could read the properties from a file and set the properties programmatically. Without writing a plugin, I don't think this is possible, and maybe you wouldn't be able to do it with a plugin either.Calan
On Mac OS X, you could put the key pairs in the Apple "keychain" and then grant maven access to the keychain. See some related info here: #728312. Windows has a similar bridge to Java key stores, but I haven't found a satisfactory solution for Linux yet.Detrition
@Paulo I just tried setting the properties in a plugin and it doesn't run early enough. It also just occurred to me that even if it did, it would be a chicken and egg problem because the plugin would need to be downloaded before being able to run it.Calan
@Detrition We definitely need a linux solution. Also, I don't know how I would tell Maven to look at the keychain.Calan
Simply setting the keyStoreType should be enough to get it to look at the keychain, but if Linux support is required, it's still not an answer.Detrition
C
4

OSX

The solution I came up with on OSX is the following .mavenrc. It uses a python script to access the password in the keychain in order to open the client certificate and then generates a random passphrase and a temporary certificate with that random password.

Put this in ~/.mavenrc and add your client certificate to the OSX keychain. Make sure and set MAVEN_CLIENT_CERT to the location of your client certificate.

~/.mavenrc

export MAVEN_CLIENT_CERT=<PATH.TO.CLIENT.CERTIFICATE>

# Retrieve secret from keychain
export SECRET=$(python <<END
from subprocess import Popen, PIPE
import re, sys, os

passlabel = os.environ.get("MAVEN_CLIENT_CERT", None)

p = Popen(['security', 'find-generic-password', '-l',passlabel,'-g'], stdout=PIPE, stderr=PIPE, stdin=PIPE)

sys.stdout.write(re.compile('password:\\s"(.*)"').match(p.stderr.read()).group(1))
sys.exit(0)
END)

TMPDIR=/tmp
TMPTMPL=mvn-$(id -u)-XXXXXXXXXX
PASSPHRASE=$(openssl rand -base64 32)
export PASSPHRASE TMPDIR

pemfile=$(mktemp $TMPDIR/$TMPTMPL)
openssl pkcs12 -in $MAVEN_CLIENT_CERT -passin env:SECRET -out $pemfile -passout env:PASSPHRASE
p12file=$(mktemp $TMPDIR/$TMPTMPL)
openssl pkcs12 -export -in $pemfile -out $p12file -passin env:PASSPHRASE -passout env:PASSPHRASE

sh -c "while kill -0 $$ 2>/dev/null; do sleep 1; done; rm -f $pemfile; rm -f $p12file;" &

MAVEN_OPTS="$MAVEN_OPTS -Djavax.net.ssl.keyStore=$p12file -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.keyStorePassword=$PASSPHRASE"

Linux

On Linux, the following .mavenrc will work with gnome keyring (make sure and add the cert password to your login keyring and set the id variable KEYRING_ID):

~/.mavenrc

MAVEN_CLIENT_CERT=<PATH.TO.CLIENT.CERTIFICATE>

export KEYRING_NAME="login"
export KEYRING_ID=<KEYRING.ID>

# Try to get secret from the gnome keyring 
export SECRET=$(python <<END
import sys, os
# Test for gtk
try:
  import gtk #ensure that the application name is correctly set
  import gnomekeyring as gk
except ImportError:
  gtk = None
if gtk:
  id = os.environ.get("KEYRING_ID", None)
  name = os.environ.get("KEYRING_NAME", None)
  try:
    if id:
      info = gk.item_get_info_sync(name, int(id))
      attr = gk.item_get_attributes_sync(name, int(id))
      sys.stdout.write(str(info.get_secret()))
    else:
      params = {}
      types = {'secret': gk.ITEM_GENERIC_SECRET, 'network': gk.ITEM_NETWORK_PASSWORD, 'note': gk.ITEM_NOTE}
      eparams = os.environ.get("KEYRING_PARAMS", None)
      etype = os.environ.get("KEYRING_ITEMTYPE", None)
      if eparams and etype:
        list = eparams.split(',')
        for i in list:
          if i:
            k, v = i.split('=', 1)
            if v.isdigit():
              params[k] = int(v)
            else:
              params[k] = v
        matches = gk.find_items_sync(types[etype], params)
        # Write 1st out and break out of loop. 
        # TODO: Handle more then one secret.
        for match in matches:
          sys.stdout.write(str(match.secret))
          break
    sys.exit(0)
  except gk.Error:
    pass
sys.exit(1)
END
)

TMPDIR=/dev/shm
TMPTMPL=mvn-$(id -u)-XXXXXXXXXX
PASSPHRASE=$(openssl rand -base64 32)
export PASSPHRASE TMPDIR

pemfile=$(mktemp $TMPDIR/$TMPTMPL)
openssl pkcs12 -in $MAVEN_CLIENT_CERT -passin env:SECRET -out $pemfile -passout env:PASSPHRASE
p12file=$(mktemp $TMPDIR/$TMPTMPL)
openssl pkcs12 -export -in $pemfile -out $p12file -passin env:PASSPHRASE -passout env:PASSPHRASE

sh -c "while kill -0 $$ 2>/dev/null; do sleep 1; done; rm -f $pemfile; rm -f $p12file;" &

MAVEN_OPTS="$MAVEN_OPTS -Djavax.net.ssl.keyStore=$p12file -Djavax.net.ssl.keyStoreType=pkcs12 -Djavax.net.ssl.keyStorePassword=$PASSPHRASE"
Calan answered 17/5, 2012 at 23:52 Comment(5)
Try this: run Maven, then ps -ef -- if I'm not much mistaken, your keystore password will be an argument passed to java. This isn't a great fix...Crosstie
I think you might have misread the solution. The certificate and password are temporary. Your actual passphrase is not visible while mvn is running.Calan
You're right, it's visible while the openssl command is running, but not while Maven is running. I guess that's better, since openssl should be faster? Still, you have to put the un-obfuscated, actual certificate password on some command line, which (IMHO) is still not great.Crosstie
I totally agree but I think without some support in maven itself, there isn't another good solution.Calan
This isn't technically Maven's fault, it's just that Java is behind the times on providing robust credential storage / retrieval for client certificates with core / out-of-the-box classes. Apple Java has the KeychainStore provider, Microsoft integrates well with the WINDOWS-MY provider -- at this point, I think the only shortcoming is that no particular Linux component has felt the need to pick up the slack and offer a similar provider tied to, for example, gnome-keyring.Crosstie
T
3

On OSX, you can use your the keychain as a keystore (as far as I know, there is still a bug, so it only works if there is only one "identity", that is one combination of cert+private key).

Do use it, use -Djavax.net.ssl.keyStore=NONE, -Djavax.net.ssl.keyStoreType=KeychainStore and -Djavax.net.ssl.keyStorePassword=-.

The keychain will then prompt you to approve the use of the private key when it's required.

Trunk answered 18/5, 2012 at 0:31 Comment(0)
W
2

You can define these properties in your Maven Settings file. They will be accessible in the same way as when you provide them on the command line. Here is an example for your Maven Settings file:

<profiles>
    <profile>
        <id>repo-ssl</id>
        <properties>
            <javax.net.ssl.trustStore>trust.jks</javax.net.ssl.trustStore>
            <javax.net.ssl.trustStorePassword>SET_TRUSTSTORE_PASSWORD</javax.net.ssl.trustStorePassword>
            <javax.net.ssl.keyStore>keystore.p12</javax.net.ssl.keyStore>
            <javax.net.ssl.keyStoreType>pkcs12</javax.net.ssl.keyStoreType>
            <javax.net.ssl.keyStorePassword>SET_KEYSTORE_PASSWORD</javax.net.ssl.keyStorePassword>
        </properties>
    </profile>
</profiles>

<activeProfiles>
    <activeProfile>repo-ssl</activeProfile>
</activeProfiles>

Although I have not done the same thing you are attempting I have used this same technique when dealing with secrets.

Wolenik answered 8/10, 2011 at 1:30 Comment(3)
This doesn't seem to work. Calls to System.getProperty() (using exec:java) for javax.net.ssl.* return null when the properties are defined as you've shown.Calan
@Calan It assumes the program will always be run via Maven. Rather a limiting assumption.Diapedesis
The OP indicated use in Maven which is why I proposed solution.Wolenik
D
0

Yes, you can either use System.setProperty() in your code, before getting the initial SSLContext, or you can go through the agony and pain of creating your own KeyManager etc etc etc as described with examples in the JSEE Reference Guide.

Diapedesis answered 8/10, 2011 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.