NewRelic java agent unable to identify .jks trust store - java

We are using a NewRelic java agent to monitor java application. The application uses a custom trust store with .jks extension. However the agent by default or by explicitly specifying the path to the trust store does not identify the trust store and throws an error.
How can we use this trust store without changing the extension as we need to use as it is.
INFO: Using ca_bundle_path:
D:\Java\jdk1.8.0_311\jre\lib\security\cacerts
2022-01-24T16:55:40,590+0530 [7048 1] com.newrelic ERROR: Unable to
generate ca_bundle_path certificate. Verify the certificate format.
Will not process further certs.
java.security.cert.CertificateException: Could not parse certificate:
java.io.IOException: Empty input

The Java agent relies on the default X.509 CertificateFactory that only accepts .pem files.
Relevant lines:
try (InputStream is = new BufferedInputStream(new FileInputStream(caBundlePath))) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// ...
caCerts.add((X509Certificate) cf.generateCertificate(is));
// ...
}
https://github.com/newrelic/newrelic-java-agent/blob/f18215d145bd6992c0fe74a8c503459799e108ca/newrelic-agent/src/main/java/com/newrelic/agent/transport/apache/ApacheSSLManager.java#L54-L58
If you can override the SPI for the X.509 CertificateFactory for one that accepts .pks files you might be able to use your file.

Related

PKIX path building failed in Java application, for self-signed SSL certificate

I created a pair of SSL certificates using OpenSSL, self-signed, to secure the HTTPS connection for a JBoss application.
When I ran the application and tried to get access by HTTPS, it did not show the site and threw an error in my console:
PKIX path building failed in Java application
I know that I should import some certs into Java, like what they said:
The long story short here is to run java InstallCert server:1234 to
generate a file called jssecacerts. Then, drop this file in
${JAVA_HOME}/lib/security directory.
What I do not understand:
1.in fact I try to get access to host B from host A, using HTTPS, why should I add the certificate of my own host to Java, not the client one?
2.using this method, there is nothing to do with my actual certificates (I mean .key and .crt), is that normal?
you need to enter your certificate inside java cacert file
This link demonstrate it the best.
Step 1 : Download certificate
Step 2 : store inside cacert
That is it! your problem resolved.

WsImport unable to find imported certificate

Apologies for yet another "unable to find certificate" question.
I'm developing on a Windows 7 machine. I am using multiple Java versions and because of that am explicit about paths to the used java version (here Java6). I achieve this by the following two lines:
set path=c:\Program Files\Java\jdk1.6.0_45\bin;%path%
set java_home=c:\Program Files\Java\jdk1.6.0_45
I need to use a 3rd party web service https://service.gov/Service.svc?wsdl that provides a certificate.PFX certificate (both service URI and certificate file are renamed as a way to protect the 3rd party's interests). I have made sure that after importing the certificate file in Windows I can open the WSDL file in my browser.
I first import the certificate in my keystore (using Administrator Command Prompt to get access to write in the system folder):
keytool -importkeystore -srckeystore certificate.pfx -srcstoretype pkcs12 -keystore "c:\Program Files\Java\jdk1.6.0_45\jre\lib\security\cacerts"
I get a success notification. Still, I make sure that the new certificate is present in the output of:
keytool -list -keystore "c:\Program Files\Java\jdk1.6.0_45\jre\lib\security\cacerts"
Then I create a new folder containing blank subfolders called src and classes. Once this is done, I run wsimport from that new folder (using Java class instead of binary to make sure I am explicit about the truststore being used):
java -classpath "c:\Program Files\Java\jdk1.6.0_45\lib\tools.jar" -Djavax.net.ssl.trustStore="c:\Program Files\Java\jdk1.6.0_45\jre\lib\security\cacerts" -Djavax.net.ssl.trustStorePassword=changeit com.sun.tools.internal.ws.WsImport https://service.gov/Service.svc?wsdl -s src -d classes
The output is the following:
parsing WSDL...
[ERROR] sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Failed to read the WSDL document: https://service.gov/Service.svc?wsdl, because 1) could not find the document; /2) the document could not be read; 3) the root element of the document is not <wsdl:definitions>.
[ERROR] failed.noservice=Could not find wsdl:service in the provided WSDL(s):
At least one WSDL with at least one service definition needs to be provided.
Failed to parse the WSDL.
The WSDL file contains and is used by other organisations, so the problem is pretty certainly not on the 3rd party's side.
Am I missing something? To me it all seems obvious by now, but it still doesn't work. I have also tried this with Java8, and the result is pretty much the same. The only difference is that in Java8, the WsImport class no longer exists, so I am using the wsimport.exe binary.
Thanks in advance for any ideas or hints.
The pfx file (which contains a certificate and also a private key) is for client authentication, while a truststore is for validating the server certificate. It is important to understand the difference between a keystore and a truststore.
You have imported the client certificate (and key) into the default truststore (cacerts). What you should have done instead is:
Import the issuer (CA) of the SSL certificate of the server into cacerts. You can skip this step if the CA certificate is already in cacerts, which is probably the case here.
Use the pfx file as your keystore for client authentication. The easiest way is to convert it to jks: https://stackoverflow.com/a/3054034/2672392 The properties to pass to wsimport are "javax.net.ssl.keyStore" and "javax.net.ssl.keyStorePassword".
See this answer for a list of important SSL properties: https://stackoverflow.com/a/5871352/2672392

How to read browsers certificates uisng java on linux? by using keyStore.getInstance("xyz")

Am trying to read the installed certificates by using code
KeyStore ks = KeyStore.getInstance("Windows-MY")
ks.load(null, null)
Enumeration<String> enumeration = ks.aliases()
while (enumeration.hasMoreElements()) {
String string = (String) enumeration.nextElement()
System.out.println(string)
}
this code list out the installed certificates on windows but on linux doesn't? tried by changing the keystore providers also.
I'm not sure what you mean with "read browsers certificates".
Are you trying to read certificates from the default Java keystore? What's your goal?
KeyStore.getInstance(..) instantiates a keystore with a specific type (JKS, for example). When you want to read from a specific keystore, you need to specify the path to the keystore and make the KeyStore instance load that file.
See http://www.java2s.com/Code/Java/Security/RetrievingaKeyPairfromaKeyStore.htm for an example and https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html for more details.
Edited: updated answer after clarified question.
You can find more info on reading browser keystores in Linux on:
http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/security/keystores.html
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/JSS
applet with SunMSCapi not working in linux
http://forums.mozillazine.org/viewtopic.php?p=12037571
Try with libsoftokn3.so of NSS.
See my answer here, "Approach 1".
The key is to find where libsoftokn3.so is, and use it as the libfile to construct a config file, and then a KeyStore.
You can get the Default Type.
Try the below code
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

Copying java keystore

I have a valid keystore on my local development machine, which contains a certificate for accessing a webservice via HTTPS.
I access this keystore in my project via:
System.setProperty("javax.net.ssl.truststore", "C:\\<workspaceprojectPath>\\SIPkeystore\\truststore.jks"); // sollte wohl auch ohne Keystore klappen
System.setProperty("javax.net.ssl.keyStore", "C:\\<workspaceprojectPath>\\SIPkeystore\\SIPkeystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "SECRET");
Now I want to access that certificate from another project. There I provide the same (full) path to the keystore in the other (local) project directory.
Apparently the path is still found by the jvm and the property is being set, but on accessing the web service I get the same error message as if I hadn't added the keystore at all.
When trying run the application I get a
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid
certification path to requested target
Is there a possibility to copy an existing keystore? This will be important also, when we deploy the application to our server. The application will run as stand-alone jar. (no web server)
EDIT:
Ok, copying the keystore does not seem to be the problem, as I am able to run the original application while using a local copy of the original keystore files. It seems like it is a configuration issue.
Is there a possibility to copy an existing keystore?
Yes. From the file system perspective it is just a file.
I imagine that the problem is one of the following:
The relevant properties are not correctly set in the other project.
The other JVM cannot find the keystore file (e.g. because of chrooting or some such)
The other JVM doesn't have the required permissions to access/read the keystore file.
The read access is being blocked by (for instance) SELinux.
First of all, if Web service doesn't require client certificate (i.e. that you introduce yourself) there's no need for keystore - you only need truststore.
It could be that other project cannot access truststore, so just for exercise, try to open C:\<workspaceprojectPath>\SIPkeystore\truststore.jks there and read first few bytes. If you succeed, it means that the other project has some other truststore set as default, so in that case try:
System.out.println(System.getProperty("javax.net.ssl.trustStore"));
Also, bear in mind that Java is case-sensitive, and you specified javax.net.ssl.truststore property instead of javax.net.ssl.trustStore. Try fixing this also.
The valicert class 3 CA certificate is not in your default truststore (which is probably the cacerts file in your JRE lib/security directory, but see the JSSE documentation for the full story).
I think you should create your own truststore file (which can be a copy of the cacerts file) and add the valicert root ca to this. Then point to this file with the javax.net.ssl.trustStore system property.

Reading an X.509 certificate with Java

I am trying to use Java to read a certificate that I received from an external party. The code is throwing the following error:
java.lang.RuntimeException: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: extra data given to DerValue constructor
The code:
FileInputStream ksfis = new FileInputStream(this.getCertificateFile());
ksbufin = new BufferedInputStream(ksfis);
certificate = (X509Certificate)
CertificateFactory.getInstance("X.509").generateCertificate(ksbufin);
To make sure the problem was not in the code, I created a self-signed certificate and used it with the code, and it worked fine. I have installed both certificates in the system key chain, and they both are valid. I am using a Mac and Java 1.6.
Any idea why I get the above exception when I load the external Party certificate? Do you think it got corrupted during transfer? If it did, it should not show up as valid on the local system, right?
Try to type this using openssl, and then import the result:
openssl x509 -outform der -in certificate.pem -out certificate.der
or use the Java Bouncy Castle functionality in the lightweight API:
http://www.bouncycastle.org/docs/pkixdocs1.5on/org/bouncycastle/openssl/PEMReader.html
You may encode the result again and then use the "X509" CertificateBuilder in Java to get a JCE defined certificate, e.g.
ByteArrayInputStream certStream = new ByteArrayInputStream(binaryCert);
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(certStream);

Categories