KeyManagementException: FIPS mode: only SunJSSE TrustManagers may be used - java

i use custom DummySocketFactory and DummyTrustMAnager to connect to smtp over TLS.
DummySocketFactory:
package XMailMessenger;
public class DummySSLSocketFactory extends SSLSocketFactory {
private SSLSocketFactory factory;
public DummySSLSocketFactory() {
try {
SSLContext sslcontext = SSLContext.getInstance("TLS");
//Security.removeProvider("SunJSSE");
sslcontext.init(null,
new TrustManager[] { new DummyTrustManager()},
null );
factory = (SSLSocketFactory)sslcontext.getSocketFactory();
} catch(Exception ex) {
System.out.println(ex.toString());
}
}
public static SocketFactory getDefault() {
SocketFactory a = new DummySSLSocketFactory();
if ( a == null ) { System.out.println("1"); }
return a;
}
...
DummyTrustManager:
public class DummyTrustManager implements X509TrustManager{
public void checkClientTrusted(X509Certificate[] cert, String authType) {
// everything is trusted
}
public void checkServerTrusted(X509Certificate[] cert, String authType) {
// everything is trusted
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
//return null;
}
}
in sending e-mail i receive exception as in subject, this exception goes from function sslcontext.init in DummySSLSocketFactory. I debug it and noticed , that in code:
private X509TrustManager chooseTrustManager(TrustManager[] tm)
throws KeyManagementException {
// We only use the first instance of X509TrustManager passed to us.
for (int i = 0; tm != null && i < tm.length; i++) {
if (tm[i] instanceof X509TrustManager) {
if (SunJSSE.isFIPS() &&
!(tm[i] instanceof X509TrustManagerImpl)) {
throw new KeyManagementException
("FIPS mode: only SunJSSE TrustManagers may be used");
}
if (tm[i] instanceof X509ExtendedTrustManager) {
return (X509TrustManager)tm[i];
} else {
return new AbstractTrustManagerWrapper(
(X509TrustManager)tm[i]);
}
}
}
// nothing found, return a dummy X509TrustManager.
return DummyX509TrustManager.INSTANCE;
}
exception occures in if (SunJSSE.isFIPS() &&
!(tm[i] instanceof X509TrustManagerImpl)) expression.
I suppose that tm[i] contains my DummyTrustManager , it can not be extended from X509TrustManagerImpl so my question is : How to disable Fips in SunJSSE ?

SunJSSE can be configured to run on FIPS-140 compliant mode as long as it uses a FIPS-140 certified cryptographic hardware or software provider that implements all cryptographic algorithms required by JSSE (ex. Network Security Services – NSS, Sun Cryptographic Accelerator 6000, nCipher, etc).
To enable FIPS mode, edit the file ${java.home}/lib/security/java.security and modify the line that lists com.sun.net.ssl.internal.ssl.Provider and associate the name of the FIPS-140 cryptographic provider (ex. SunPKCS11-NSS). The name of the provider is a string that concatenates the prefix SunPKCS11- with the name of the specified PKCS#11 provider in its configuration file.
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
SunPKCS11-NSS
In case of using NSS as cryptographic software token (Make use of NSS 3.1.1. or above), assuming the libraries are located under the /opt/nss/lib directory and its key database files (with the suffix .db) are under the /opt/nss/fipsdb directory, the sample configuration for representing NSS will be as follows:
# Use NSS as a FIPS-140 compliant cryptographic token
# SunPKCS11-NSS
name = NSS
nssLibraryDirectory = /opt/nss/lib
nssSecmodDirectory = /opt/nss/fipsdb
nssModule = fips
In FIPS mode, SunJSSE will perform SSL/TLS 1.0 based communication and cryptographic operations including symmetric and asymmetric encryption, signature generation and verification, message digests and message authentication codes, key generation and key derivation, random number generation, etc.

To anyone having a giant headache when you need to install a tomcat webapp on a third party server, I lost 1 hour trying to bypass this damn thing...
I solved in this way, without touching anything in the webapp.
Add this java parameter:
-Djava.security.disableSystemPropertiesFile=true
Source:
https://access.redhat.com/documentation/en-us/openjdk/8/pdf/configuring_openjdk_8_on_rhel_with_fips/OpenJDK-8-Configuring_OpenJDK_8_on_RHEL_with_FIPS-en-US.pdf
Also, if the app needs to connect to a Windows Server, you might want to disable FIPS there too:
In Control Panel, click Administrative Tools -> Local Security Policy.
In Security Settings -> Local Policies -> Security Options.
Under Policy in the right pane, double-click System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing, and then click Disabled.
Reboot the server
(bonus)
If you want to uninstall FIPS from the server, follow this giude (I didn't test it):
https://www.bggofurther.com/2021/02/disable-fips-mode-on-centos-7/

Related

How do you use the MongoDB Java Driver in version 4.4 to connect to a replica set using SSL and password authentication?

How do you use the MongoDB Java Driver in version 4.4 to connect to a replica set using SSL and password authentication?
Can you show some examples?
In order to use SSL you will need to have a CA certificate and a private key in a keystore (or if you prefer on two distinct keystores).
Whilst you need to set some Java system properties to declare your keystores and how to access them, all other parameters are setup via the MongoClientSettings.Builder class.
Below you have a small example that you can use to connect to a MongoDB replica set using SSL with password based authentication:
public static MongoClient createMongoClient(MongoDBConnectionParameters parameters) {
MongoClientSettings.Builder builder = MongoClientSettings.builder();
if(parameters.getPassword() != null) {
// User name and password
MongoCredential mongoCredential = com.mongodb.MongoCredential.createCredential(parameters.getUser(),
parameters.getAuthDB(), parameters.getPassword().toCharArray());
builder.credential(mongoCredential);
}
if(parameters.getServerAddressList() != null) {
// using replica set
ArrayList<ServerAddress> serverAddresses = parameters.getServerAddressList().entrySet().stream()
.map(e -> new ServerAddress(e.getKey(), e.getValue())).collect(Collectors.toCollection(ArrayList::new));
builder.applyToClusterSettings(b -> b.hosts(serverAddresses));
} else {
String connectionString = String.format("mongodb://%s:%d/", parameters.getMongoServer(), parameters.getMongoPort());
builder.applyConnectionString(new ConnectionString(connectionString));
}
if(parameters.isTls()) {
// using SSL
builder.applyToSslSettings(b -> b.enabled(true));
setupSsl(parameters);
}
return MongoClients.create(builder.build());
}
private static void setupSsl(MongoDBConnectionParameters parameters) {
System.setProperty("javax.net.ssl.trustStore", parameters.getTrustStore().toString());
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
final String trustStorePassword = "thepassword";
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
System.setProperty("javax.net.ssl.keyStore", parameters.getKeystoreFile().toString());
System.setProperty("javax.net.ssl.keyStoreType", "JKS");
String keystorePassword = "thepassword";
System.setProperty("javax.net.ssl.keyStorePassword", keystorePassword);
}
This is the Maven dependency with the MongoDB driver:
<!-- https://mvnrepository.com/artifact/org.mongodb/mongodb-driver-sync -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.4.0</version>
</dependency>

PKIX path building failure with trusted certificate and all Trusting manage

thanks in advance for the help.
I developed a suite of api tests in Java TestNG against an api deployed to a non-secure internal QA environment. Recently this application was re-deployed to a new, secure environment. When this happened I began to see the following error on every api request, both GET and POST:
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
My first move was to install the appropriate certificate, even though it is signed by DigiCert, so it should be approved by default.
keytool -import -alias ca -file qa4cert.crt -keystore cacerts -storepass changeit
And that had no effect. I also tried adding the certificate via the IDE (Intellij). Again, no effect, still seeing the same error.
Since I was unable to make progress here, and I am still working against an internal QA environment with no sensitive data, I was comfortable dropping the certificate validation and install an all-trusting cert manager. My simple implementation is below:
package test_utils;
import javax.net.ssl.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class SSLTool {
private static boolean isTrustAllCertsInitialized = false;
public static void disableCertificateValidation() {
if (isTrustAllCertsInitialized) {
return;
}
isTrustAllCertsInitialized = true;
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLContext.setDefault(ctx);
System.out.println("new trust manager should be set");
} catch (Exception e) {
e.printStackTrace();
}
return ctx;
}
}
I ran the above code as part of my --before-- implementation in the test suite and verified we reached the end of the try block, so the new All_trusting TrustManager should be set. Unfortunately this also had no effect, I am still seeing the error.
To try and isolate the issue, I processed the same POST request through both Postman and curl, both returned the desired result, no errors.
For sanity's sake I also hit a few public urls both secure and insecure and got the desired results as well.
At this point I'm stumped. Since Postman and curl work, the error must have something to do with my implementation, but I don't understand why it would work with our old environment but not in the new one. A debugging version of my code that makes a failing get request is below. This fails both when run through the ide, and through the command line with maven.
Test File
public class DebugTests extends BaseTest {
#Test
public void debug() {
BaseApi api = new BaseApi();
api.debugGet("<<MYURL>>");
}
BaseTest
#Listeners(Listener.class)
public class BaseTest {
#BeforeMethod
public void before() {
// the below function is used to disable certificate validation. It is ONLY meant to be used in testing environments
// if used in production it exposes our test suite to MITM attacks.
SSLTool.disableCertificateValidation();
}
#AfterMethod
public void after() {
}
}
Relevant Code from the API object
public BaseApi() {
SSLContext ctx = SSLTool.disableCertificateValidation();
client = HttpClients.custom().setSSLContext(ctx).build();
System.out.println("trust manager set");
}
public void debugGet(String endpoint) {
try {
client.execute(this.buildGetConnection(endpoint, false));
} catch (Exception e) {
e.printStackTrace();
}
}
protected HttpGet buildGetConnection(String endpoint) {
return this.buildGetConnection(endpoint, true);
}
protected HttpGet buildGetConnection(String endpoint, boolean auth) {
//TODO build a switch to change testing environments based off command line
HttpGet get = new HttpGet(rootUrl + endpoint);
if(auth) {
StsAuthApi authApi = new StsAuthApi();
get.setHeader("Authorization", "Bearer " + authApi.getToken());
}
get.setHeader("accept","application/json");
get.setHeader("Content-Type","application/xml");
System.out.println("making GET request to " + rootUrl + endpoint);
return get;
}
In my debugging implementation rooturl is an empty string so the url matches the string supplied via the test case.
HttpClients.createDefault() will not use your null trust manager. It internally creates and initialises an SSLContext like this:
final SSLContext sslContext = SSLContext.getInstance(SSLContextBuilder.TLS);
sslContext.init(null, null, null);
You can create the HttpClient like this instead:
HttpClients.custom()
.setSSLContext(ctx)
.build();
Where ctx is the one you created in the disableCertificateValidation() method.
The question about why it didn't work when you added your certificate to cacerts is still open. If you did that correctly then the default client should have used it. You can set the system property javax.net.debug=all if you want to debug that one further (it will print out your truststore when it's first initialised into a context).

Using Eclipse To Connect To Network

I want to connect my Eclipse plug-in to an HTTPS URL, but have a problem because the user would need to accept the certificate. Of course there are a couple of tutorials for how to do this in plain Java, but that might be hard to do inside an Eclipse plug-in and I think I'd reinvent the wheel that way.
Because Eclipse has some built in tooling to connect to sites with different network protocols. An example would be the "Install new Software..." action. The tooling even has a preference page that lists HTTPS separately.
According to the Eclipse Help, the KeyStore is used "as a repository for Certificates used for trust decisions [...] when making SSL connections". Yet I couldn't figure out how to use it.
So my question is: How do I use the Eclipse's build in facilities to connect to my HTTPS site?
Based on this answer here I build my own plug-in which loads just the one certificate I need (lucky me) in its EarlyStartup:
public class EarlyStartup implements IStartup {
private static final String ALIAS = "ACME";
#Override
public void earlyStartup() {
final char[] passphrase = "changeit".toCharArray();
final char separator = File.separatorChar;
final File dir = new File(System.getProperty("java.home") + separator + "lib" + separator + "security");
final File file = new File(dir, "cacerts");
try (InputStream certIn = getClass().getResourceAsStream("acme.org.crt");
final InputStream localCertIn = new FileInputStream(file);) {
final KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(localCertIn, passphrase);
if (keystore.containsAlias(ALIAS)) {
return;
}
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
final Certificate cert = cf.generateCertificate(certIn);
keystore.setCertificateEntry(ALIAS, cert);
try (OutputStream out = new FileOutputStream(file)) {
keystore.store(out, passphrase);
}
} catch (final Exception e) {
e.printStackTrace();
}
}
}

Setting axis.SecureSocketFactory in standalone JRE vs Tomcat

I have a need to replace Axis's (1.4) default SecureSocketFactory with my own implementation. I've done this successfully running from a standalone JRE (1.6), and everything's tested just fine. But when I deploy to Tomcat 5.5 - where this application will eventually reside - as far as I can tell, Axis is still using the JSSESecureSocketFactory. I've tried both:
System.setProperty("org.apache.axis.components.net.SecureSocketFactory", "my.CustomSecureSocketFactory");
and
AxisProperties.setProperty("axis.socketSecureFactory", "my.CustomSecureSocketFactory");
as well as setting the JVM property for Tomcat:
-Dorg.apache.axis.components.net.SecureSocketFactory=my.CustomSecureSocketFactory
The JREs, libraries, key/trust stores, and everything else I can think of are identical. I've even run my standalone test on the server, using Tomcat's JRE and options, and still it works fine.
I'm out of ideas how how to troubleshoot this. Does anyone have any idea of where to look, or an alternate approach to telling Axis to use a specific SecureSocketFactory?
Relevant code snippets:
public class CustomKeyManager extends X509ExtendedKeyManager {
private final X509ExtendedKeyManager base;
public CustomKeyManager(X509ExtendedKeyManager base) {
this.base = base;
}
/* Lots of methods omitted */
static SSLContext SSL_CONTEXT;
static void updateSSL(String keyStoreFile, String keyStorePassword){
System.setProperty("org.apache.axis.components.net.SecureSocketFactory", "com.spanlink.cfg.crypto.HostNameSecureSocketFactory");
AxisProperties.setProperty("axis.socketSecureFactory", "com.spanlink.cfg.crypto.HostNameSecureSocketFactory");
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(new FileInputStream(keyStoreFile), keyStorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyStorePassword.toCharArray());
KeyManager[] oldManagers = kmf.getKeyManagers();
KeyManager[] newManagers = new KeyManager[oldManagers.length];
for (int i = 0; i < oldManagers.length; i++) {
if (oldManagers[i] instanceof X509ExtendedKeyManager) {
newManagers[i] = new CustomKeyManager((X509ExtendedKeyManager) oldManagers[i]);
}else{
newManagers[i] = oldManagers[i];
}
}
SSL_CONTEXT = SSLContext.getInstance("SSL");
SSL_CONTEXT.init(newManagers, null, null);
}
}
public class CustomSecureSocketFactory extends JSSESocketFactory {
public CustomSecureSocketFactory(Hashtable table) {
super(table);
super.sslFactory = CustomKeyManager.SSL_CONTEXT.getSocketFactory();
}
}
Ended up figuring it out. On class load, Axis's SocketFactoryFactory resets the Axis property axis.socketSecureFactory to the default. From a JRE, the class loader loaded SocketFactoryFactory before my code ran; from Tomcat, it wasn't loaded until after my code ran, overwriting my custom settings.
I just added a Class.forName() call before setting axis.socketSecureFactory, and everything worked.

Can't find private key from certificate

We are working on encryption-decryption using applet. We find some unexpected issue with digital certificate. One system has certificate and we can't find the private key from that certificate but by installing the same certificate again works fine.
Java Plug-in 10.25.2.17
Using JRE version 1.7.0_25-b17 Java HotSpot(TM) 64-Bit Server VM
User home directory = C:\Users\admin
To access private key we are using below code.
private PrivateKey getPrivateKeyFromKeyStore(String pubkey, KeyStore browser) {
PrivateKey privateKey = null;
String pubKey1 = "";
if (browser != null) {
try {
Field spiField = KeyStore.class.getDeclaredField("keyStoreSpi");
spiField.setAccessible(true);
KeyStoreSpi spi = (KeyStoreSpi) spiField.get(browser);
Field entriesField = spi.getClass().getSuperclass().getDeclaredField("entries");
entriesField.setAccessible(true);
#SuppressWarnings("rawtypes")
Collection entries = (Collection) entriesField.get(spi);
for (Object entry : entries) {
String alias = (String) invokeGetter(entry, "getAlias");
X509Certificate[] certificateChain = (X509Certificate[]) invokeGetter(entry, "getCertificateChain");
for (X509Certificate current : certificateChain) {
pubKey1 = this.bASE64Encoder.encode(current.getPublicKey().getEncoded());
if (pubkey.equals(pubKey1) && !pubkey.equals("")) {
privateKey = (PrivateKey) invokeGetter(entry, "getPrivateKey");
return privateKey;
}
}
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return privateKey;
}
You won't find private key in certificate because it must be in your keystore, of course, if you generated your cert with its CSR :)
As a tip, I may ask is the cert expired for example?
Anyway, the question is pretty unclear :( If you have cert you must have the keystore which was used to sign your app... It would be better you give more details...

Categories

Resources