Skip to content

Instantly share code, notes, and snippets.

@cipherboy
Last active March 31, 2020 20:52
Show Gist options
  • Save cipherboy/308229616364946df94b382cc93bb5a3 to your computer and use it in GitHub Desktop.
Save cipherboy/308229616364946df94b382cc93bb5a3 to your computer and use it in GitHub Desktop.
JSSProvider - Old and New JSS

JSS Changes

Old JSS (< v4.6.4, unreleased)

Under older JSS versions, it is possible to do the following:

In java.security:

...
security.provider.n = org.mozilla.jss.JSSProvider
...

for some n. Other providers may be on either side.

Then, in the code before anything else is done:

public static void main(String[] args) {
    InitializationValues ivs = new InitializationValues(args[0]);
    ivs.installJSSProvider = false;
    CryptoManager.initialize(ivs);
    
    // Rest of the application ...
}

Because CryptoManager.initialize(...) doesn't use any Security methods, loading will be successful. It also means there's exactly one Mozilla-JSS provider loaded in the correct spot.

However, CryptoManager.initialize(...) must be called, otherwise Java will crash. This is the behavior exhibited by Candlepin (using netscape.security without calling CryptoManager.initialize(...).

New JSS

Under new code, there's two ways to load JSS:

  1. Directly via CryptoManager.initialize(...) which ignores java.security, or
  2. From java.security, passing a config file option, which calls the above.

In particular, the pull request allows a CryptoManager.getInstance(...) call to return a CryptoManager instance from the java.security loader, if CryptoManager.initialize(...) hasn't yet been called.

It also introduces a new initialize method which takes two parameters, the InitializationValues and whether or not to prefer the global JDK configuration.

In certain tests, we know we want to ignore the global JDK configuration, so we set this boolean to false.

For backwards compatibility, we've set this value to false for the time being, however, we could change this to true at a later point, when use via java.security becomes more widespread.

More documentation is going to land in a separate pull request.

Minimizing Unnecessary Code Changes

Because java.security is, by default, only a file admins can write changes to, system-wide (and a file passed by -Djava.security=/some/path complements it, with == providing a strict override), this means that admins can upgrade JSS seamlessly, with the application being unaware that they're using JSS. However, if the application was written for an older version of JSS (implying the users running the app weren't aware of the upgrade or restrictions placed by their administrators -- as would be the case if JSS were shipped as the default FIPS compliant crypto implementation somewhere), this could cause breakage.

There's three scenarios we want to support:

New App, New JSS

Trivial; given the options above, choose one.

Old App, Old JSS

Trivial; continues working.

Old App, New JSS

This is the harder case. Assuming the new JSS version doesn't break any APIs from the previous target version (e.g., a v4.6.3 -> v4.6.4 change even!), the new JSS should continue to function. This is a reasonable assumption, IMO: we try to break as little as necessary and only add new features / fix bugs.

There's two cases here:

  1. No java.security; trivial; depends on what the app uses.
  2. java.security: complicated.

In particular, for 2, we have the question of what does the following code do?

In java.security:

security.provider.n = org.mozilla.jss.JSSProvider

In the code:

public static void main(String[] args) {
    CryptoManager.initialize("/path/to/nssdb");
    
    // Rest of the application ...
}

My view is that, since JSS is new enough, we should load JSS out of the java.security file instead of the CryptoManager.initialize(...) call. However, since it is a technically a breaking change, to give applications a chance to upgrade, we've defaulted to the false form, i.e., prefer local configuration instead.

This should behave as if JSS was loaded from CryptoManager.initialize(...). Detection logic is given below if the application wishes to prefer java.security and maintain fallback code.

Upgrading Old Code

There are thus two paths to upgrade a legacy code base to use a newer JSS version:

  1. Continue using CryptoManager.initialize(...) as before. This gives the local application dynamic control over the NSS DB path.

  2. Switch to using java.security-based configuration (either via local policy with -Djava.security.properties=/path or via system-wide policy with modifying $JAVA_HOME/conf/security/java.security) and remove the call to CryptoManager.initialize(...). If this call is necessary for backwards-compatibility reasons (to support multiple JSS versions), it would be sufficient to check the value of CryptoManager.getInstance(...) before excuting configuration:

    try {
        cm = CryptoManager.getInstance();
    } catch (NotInitializedException nie) {
        CryptoManager.initialize(...);
        cm = CryptoManager.getInstance();
    }

    This gives the user control over NSS DB path via modifying either of those two configuration files (or by providing a local override).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment