package freenet.crypt;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.security.Provider;
import java.security.Security;
import java.security.Signature;
import javax.crypto.KeyAgreement;
import freenet.support.Logger;
import freenet.support.io.Closer;
public class JceLoader {
static public final Provider BouncyCastle;
static public final Provider NSS; // optional, may be null
static public final Provider SUN; // optional, may be null
static public final Provider SunJCE; // optional, may be null
static private boolean checkUse(String prop)
{
return checkUse(prop, "true");
}
static private boolean checkUse(String prop, String def)
{
return "true".equalsIgnoreCase(System.getProperty("freenet.jce."+prop, def));
}
static {
Provider p;
// NSS is preferred over BC, add it first
p = null;
if (checkUse("use.NSS","false")) {
try {
p = (new NSSLoader()).load(checkUse("prefer.NSS"));
} catch(Throwable e) {
// FIXME what about Windows/MacOSX/etc?
final String msg = "Unable to load SunPKCS11-NSScrypto provider. This is NOT fatal error, Freenet will work, but some performance degradation possible. Consider installing libnss3 package.";
Logger.warning(NSSLoader.class, msg, e);
}
}
NSS = p;
p = null;
if (checkUse("use.BC.I.know.what.I.am.doing")) {
try {
p = (new BouncyCastleLoader()).load();
} catch(Throwable e) {
final String msg = "SERIOUS PROBLEM: Unable to load or use BouncyCastle provider.";
System.err.println(msg);
e.printStackTrace();
Logger.error(JceLoader.class, msg, e);
}
}
BouncyCastle = p;
// optional
SUN = checkUse("use.SUN") ? Security.getProvider("SUN") : null;
SunJCE = checkUse("use.SunJCE") ? Security.getProvider("SunJCE") : null;
}
static private class BouncyCastleLoader {
private BouncyCastleLoader() {}
private Provider load() throws Throwable {
Provider p = Security.getProvider("BC");
if (p == null) {
try {
Class<?> c = Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
p = (Provider)c.newInstance();
Security.addProvider(p);
} catch(Throwable e) {
throw e;
}
Logger.debug(BouncyCastleLoader.class, "Loaded BouncyCastle provider: " + p);
} else {
Logger.debug(BouncyCastleLoader.class, "Found BouncyCastle provider: " + p);
}
try {
// We don't want totally unusable provider
KeyAgreement.getInstance("ECDH", p);
Signature.getInstance("SHA256withECDSA", p);
} catch(Throwable e) {
throw new Error("Cannot use required algorithm from BouncyCaste provider", e);
}
return p;
}
}
static private class NSSLoader {
private NSSLoader() {}
private Provider load(boolean atfirst) throws Throwable {
Provider nssProvider = null;
for(Provider p: java.security.Security.getProviders()) {
if (p.getName().matches("^SunPKCS11-(?i)NSS.*$")) {
nssProvider = p;
break;
}
}
if(nssProvider == null) {
File nssFile = File.createTempFile("nss",".cfg");
nssFile.deleteOnExit();
OutputStream os = null;
try {
// More robust than PrintWriter(file), which can hang on out of disk space.
os = new FileOutputStream(nssFile);
OutputStreamWriter osw = new OutputStreamWriter(os, "ISO-8859-1");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("name=NSScrypto\n");
bw.write("nssDbMode=noDb\n");
bw.write("attributes=compatibility\n");
bw.close();
os = null;
} finally {
Closer.close(os);
}
Class<?> c = Class.forName("sun.security.pkcs11.SunPKCS11");
Constructor<?> constructor = c.getConstructor(String.class);
nssProvider = (Provider)constructor.newInstance(nssFile.getPath());
if (atfirst)
Security.insertProviderAt(nssProvider, 1);
else
Security.addProvider(nssProvider);
Logger.debug(NSSLoader.class, "Loaded NSS provider " + nssProvider);
} else {
Logger.debug(NSSLoader.class, "Found NSS provider " + nssProvider);
}
return nssProvider;
}
}
static public void main(String[] args) {
dumpLoaded();
}
static public void dumpLoaded() {
System.out.println("BouncyCastle: "+BouncyCastle);
System.out.println("SunPKCS11-NSS: "+NSS);
System.out.println("SUN: "+SUN);
System.out.println("SunJCE: "+SunJCE);
}
}