Search code examples
javasslmavenjsse

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


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?


Solution

  • 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"