How to extract X509 Certificate fields in Java - java

I am currently working on an application that will process certain fields of a X509 Certificate, and I cannot seem to figure out how to extract certain parts of the certificate for debugging purposes. So far I have only been able to figure out how to read a certificate from a file based on the Javadoc for java.security.cert.Certificate, using this code:
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
while (bis.available() > 0) {
Certificate cert = cf.generateCertificate(bis);
System.out.println(cert.toString());
}
Assuming that no exceptions are thrown, and that cert is a valid certificate, how would I do this?
Sidenote I am using Bouncy Castle in this project

Cast it to an X509Certificate:
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
System.out.println(cert.getSubjectDN());

Related

Certificate chain validation using java, checking revokation and OCSP status

I am new to the world of PKI , certificates in general. I am writing a service which needs to validate a chain of certiticates.
The general approach taken is as follows
a) Generate a List of certificates from the data sent
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
CertPathValidatorResult certPathValidatorResult = null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> x509Certificates =
(List<X509Certificate>) certificateFactory.generateCertificates(byteArrayInputStream);
CertPath certPath = certificateFactory.generateCertPath(x509Certificates);
Load the JDK keystore, with something like this
//Load the JDK's cacerts keystore file
String filename =
System.getProperty("java.home")
+ "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
String password = "changeit";
keystore.load(is, password.toCharArray());
CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX");
PKIXParameters pkixParameters = new PKIXParameters(keystore);
//pkixParameters.setRevocationEnabled(false);
PKIXParameters certPathValidatorResult = certPathValidator.validate(certPath, pkixParameters);
I am assuming if this is not a valid chain it would throw an exception. Would this validation check expired Certificates, Valid Public Key ?
also I need to be able to find the the OCSP staus of a certificate or check if it is revoked>? How can this be done using the Cryptography API
Is the use fo bouncy castle recommended over the API ? Does Bouncy castle have a way to check CRL and OCSP status of a certificate?
Thanks for all the pointers and help in advance. Appreciate it.
Best Regards
It's correct, you can use CertificateFactory to load certificates chain.
If you want validate a chain of certiticates, you don't need a KeyStore. The certificates are validated with the certificate of autority who emit that certificate.
For example:
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(chain.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
List<X509Certificate> x509Certificates = (List<X509Certificate>) certificateFactory.generateCertificates(byteArrayInputStream);
x509Certificates.get(1).verify(x509Certificates.get(0).getPublicKey());
In this case you can use it to validate a certificate if you don't know the root ca.
You can check the period with
x509Certificates.get(1).getNotBefore()
and
x509Certificates.get(1).getNotAfter()
Is important validate the status of certificate.
Yes BouncyCastle is great library for it.

Standard for X509 certificate sharing (java servlet)

I'm trying to develop a simple web service using servlets and Apache Tomcat, in which a client and a server exchange their public keys; this to send some signed data in a further stage of communication.
I've read over the internet that the best way to share public keys is through X509 certificates to prove their reliability / integrity, is that correct?
I have used Bouncy Castle and JCE to generate the keys and a self-signed X509 certificate, which has to be exchanged between the two parties. Here I got lost between different formats like PEM, DER and recommendations on sharing the bytes of the certificate (i.e. transferring the certificate's object via a ByteArrayOutputStream encapsulated in a ObjectOutputStream). Which is the correct way of doing this? How should I code it?
Side note: this software is intended for study purposes and not for real-world use, so it is okay to make big assumptions as "the connection is secure". This is why I preferred to keep it simple and not to implement SSL. However, if it proved to be really necessary I could try to use it.
Thanks.
EDIT: this is how I solved the problem, thanks to #Lothar
X509Certificate taCert = Certificates.getCertificate();
StringWriter sw = new StringWriter();
JcaPEMWriter pw = new JcaPEMWriter(sw);
pw.writeObject(taCert);
pw.flush();
String pemData = sw.toString();
pw.close();
out.println(pemCert);
The function getCertificate() generates a X509Certificate using BouncyCastle and the resulting pemData is something like this:
-----BEGIN CERTIFICATE-----
MIIBwDCCASmgAwIBAgIGAWIe66NnMA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNVBAMT
DlRydXN0QXV0aG9yaXR5MB4XDTE4MDMxMjEwMzMwMloXDTE4MDMzMDExMTMzMFow
GTEXMBUGA1UEAxMOVHJ1c3RBdXRob3JpdHkwgZ8wDQYJKoZIhvcNAQEBBQADgY0A
MIGJAoGBAMT2Y7iU8dWrqDzciR64HPuXOTEsf/90cLF0hickYvJULHFJ90Z1PXxA
Um/WJ5vX4m6+ESmurrFmClyeLMfThgHBlYfBfKSNYzIB1M0NXoe8znaCP9U+WKy7
HdkRvvenJPRx8mqWCcWH1nBPI5SehppgWxWZzYD4BTIQ+ILwdm8fAgMBAAGjEzAR
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADgYEATUmc3BfItRc3UObi
qxdzeN32ZEiLsoywan8qDZyxLFJWf8HhLFq2KOFht6+PatAh1SmcZ67Iw+2thb/0
ptuRE1MetpZnY3M++afv5HRrWm1k52sZYKkYtlfAXlzZuNDPm7lDQzSyS/0IhvjC
32bwhNiajLcc0mLqPQCEMd5oi5U=
-----END CERTIFICATE-----
which is sent with a Content-Type text/plain; charset=utf-8 through the PrintWriter provided by response.getWriter(). Client-side, the response is received through HttpURLConnection urlConnection, and read via InputStream in = urlConnection.getInputStream();. More specifically,
InputStream in = urlConnection.getInputStream();
Scanner scanner = new Scanner(in);
scanner.useDelimiter("\\A");
boolean hasInput = scanner.hasNext();
if (hasInput)
return scanner.next();
else
return null;
To create a X509Certificate, I use a CertificateFactory, considering contactServerResult as the string received from the server:
CertificateFactory fact = CertificateFactory.getInstance("X.509");
certificate = (X509Certificate) fact.generateCertificate(new ByteArrayInputStream(contactServerResult.getBytes(Charset.forName("UTF-8"))));
At the end I did not use a PEMParser when reconstructing the received certificate, because it returned a X509CertificateHolder rather than a X509Certificate, resulting in a ClassCastException.
The format most if not all cryptography libraries support is DER. If you have an X.509-instance you simply call getEncoded and you have it. PEM is also widely supported but you need a parser (PEMParser in BouncyCastle for example) to create the certificates from it again.
I can't recommend serializing the objects using DataOutputStream. The object you serialize is dependent on the cryptographic provider that was involved when the certificate was created. If the other side doesn't have this provider available the deserialization will fail. If the other side isn't Java-based the data stream is completely useless, because it's java specific.
To answer your question, how to code it if we're talking about DER as format:
Sending the certificate (assuming within a servlet):
response.getOutputStream().write(certificate.getEncoded());
Creating a certificate from received data (using BouncyCastle as provider):
byte[] data = getDataFromSomewhere();
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
X509Certificate x509 = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
And for the fun of it, doing the same for PEM:
Sending:
StringWriter sw = new StringWriter();
JcaPEMWriter pw = new JcaPEMWriter(sw);
pw.writeObject(cert);
pw.flush();
String pemData = sw.toString();
Receiving:
String data = getDataFromSomewhere();
PEMParser pr = new PEMParser(new StringReader(data));
X509Certificate x509 = (X509Certificate) pr.readObject();
All examples are simplyfied, e.g. assuming that there is data to read and that the data actually represent a X.509 certificate. There are other things that can be DER- or PEM-encoded so you might get an exception while parsing or when doing the cast because the returned object isn't a certificate but for example a CSR.

How to load public certificate from pem file?

I was trying to extract RES public key from the file below
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
Here is the code i did..
public static PublicKey loadPublicKeyFromFile(File publicKeyFile) throws Exception {
FileReader file = new FileReader(publicKeyFile);
PemReader reader = new PemReader(file);
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey caKey = kf.generatePublic(caKeySpec);
return caKey;
}
But It throws out
java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID
What's the appropriate way to extract RES Public key from a file..
An X.509 certificate and an X509EncodedKeySpec are quite different structures, and trying to parse a cert as a key won't work.
Java's X509EncodedKeySpec is actually SubjectPublicKeyInfo from X.509 or equivalent and more convenient PKIX also linked from Key, which is only a small part of a certificate.
What you need to do is read and parse the cert and then extract the pubkey from the cert.
Standard SunJCE CertificateFactory can do it
(and can read either PEM or DER to boot) like this:
CertificateFactory fact = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream (args[0]);
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
PublicKey key = cer.getPublicKey();
is.close();
// add error handling as appropriate, try-with-resources is often good
If you have BouncyCastle you can use its provider the same way (just add a second argument to .getInstance or set the default provider list order), or you can use PEMParser with JcaX509CertificateConverter -- which effectively does the same thing, internally running the data through a CertificateFactory.
With Sun JVM it is also possible with
import java.security.cert.X509Certificate;
import sun.security.x509.X509CertImpl;
InputStream is = ...
X509Certificate crt = new X509CertImpl(is);
, although I'd prefer the accepted answer as JVM implementation-independent one.
Under the hood, in Sun JVM, CertificateFactory(more precisely, X509Factory) does instantiate X509CertImpl , but there is very subtle difference between the two approaches: CertificateFactory caches X509 Certificate instances by binary content of the input stream, the one that can be retrieved by cer.getEncoded().
The cache can be cleared by
fact.generateCertificate(null);

How does a Java client automatically accept a self-signed certificate from the server

I am implementing a server that uses self-signed certificates. What is the best way to distribute the certificates to the clients? I could import the certificate into the java keystore and setup the client. But is there any way to avoid every client from importing the certificate manually. Can this be done automatically by the java client? I went through the JSSE reference but could not figure out how to do this. Would appreciate any help.
Regards,
Sampath.
Check out the KeyStore class. It allows you to manipulate Java keystores.
Code example:
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(null, null); // Creates a new keystore
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("cert.cer")); // Or read from URL
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = null;
if (bis.available() > 0) {
cert = cf.generateCertificate( bis );
ks.setCertificateEntry( "SGCert", cert );
}
ks.setCertificateEntry("SGCert", cert);
ks.store(new FileOutputStream("out.keystore"), "secret".toCharArray() );

loading a certificate from keystore

Load a certificate and keys from keystore which is password protected and then use it for cert verification and digital signing
To read the certificate is really trivial.
CertificateFactory factory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) factory.generateCertificate(new FileInputStream("file.pem"));
This is with standard APIs (in try/catch) etc and you have loaded your certificate.
Now the toString method of certificate is not suitable for you since it just captures the "user's" view of the certificate e.g. you would use it for println for instance
Can't you send the certificate object itself?
Not sure what your server expects so you can look into the various methods of certificate
X509Certificate
I use this code
PEMReader pr=new PEMReader(new StringReader(trust_certs));
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
Object o;
int i=0;
while((o=pr.readObject())!=null){
if(o instanceof X509Certificate){
i++;
X509Certificate c=(X509Certificate)o;
trustStore.setCertificateEntry(Integer.toString(i), c);
}
}
http://www.bouncycastle.org/docs/docs1.6/org/bouncycastle/openssl/PEMReader.html
A pem file is read as any other text file. Read the Java tutorial about IO (and concentrate on character streams, since a pem file contains text, and on File IO, since this is what you want to do)

Categories