Standard for X509 certificate sharing (java servlet) - java

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.

Related

How to extract X509 Certificate fields in 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());

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);

Android - converting pkcs12 certificate string to x509 certificate object for bks keystore

I am writing an android app that requires SSL certification for certain web requests. Unlike the options I see online about creating the keystore files using the cert file, I have to make an initial web request that returns the certificate as a string in a json response.
The JSON data is formatted like the following... (note: the following certificate is shortened, the "..." does not exist in an actual response)
"result":{
"pkcs12": "Ulv6GtdFbjzLeqlkelqwewlq822OrEPdH+zxKUkKGX/eN...9801asds3BCfu52dm7JHzPAOqWKaEwIgymlk="
},
I am decoding this value using Base64.deocode to save it as a byte[]
ssl.setPkcs12( Base64.decode( jsonObject.optString( "pkcs12" ) ) );
Then I am trying to create a X509Certificate using the byte[]
CertificateFactory certFactory = CertificateFactory.getInstance( "X.509" );
InputStream in = new ByteArrayInputStream( ssl.getPkcs12() );
X509Certificate cert = (X509Certificate) certFactory.generateCertificate( in );
My code is failing on the generateCertificate() method with
"java.security.cert.CertificateException: org.apache.harmony.security.asn1.ASN1Exception: ASN.1 Sequence: mandatory value is missing at [4]".
I have spent a great deal of time trying to fix what appears to be a simple problem with no luck at all. Any help would be amazing!
In case anyone else needs the answer...
I was able to work around this by completely stripping out the BouncyCastle libraries and using a PKCS12 store instead of a BKS. The following snippet is the solution. I no longer had to generate a certificate object, instead I am saving the json certificate string in the preferences, then grabbing it and using it to generate a keystore on the fly. The base64 decoder is not the BouncyCastle decoder, it is a custom standard decoder.
KeyStore keyStore = KeyStore.getInstance( "PKCS12" );
String pkcs12 = UserSession.getCertificate( context );
InputStream sslInputStream = new ByteArrayInputStream( MyBase64Decoder.decode( pkcs12.getBytes() ) );
keyStore.load( sslInputStream, "password".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)

How to verify PEM format certificate in Java

I have PEM format file, How can verify the signature in Java, as I followed http://download.oracle.com/javase/tutorial/security/apisign/versig.html but found that Java doesnt support PEM
You can read a certificate in a PEM file using BouncyCastle's PEMReader. If the content is an X.509 certificate, you should get an instance of X509Certificate and verify it as you want from there.
EDIT: Here is what the code should look like (not tried):
// The key with which you want to verify the cert.
// This is probably a CA certificate's public key.
PublicKey publicKey = ...;
PEMReader reader = new PEMReader(new FileReader("/path/to/file.pem"));
Object pemObject = reader.readObject();
if (pemObject instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)pemObject;
cert.checkValidity(); // to check it's valid in time
cert.verify(publicKey); // verify the sig. using the issuer's public key
}
(Of course, as with any I/O operations, you'll need to close the reader perhaps with try/finally.)
Note that checkValidity and verify don't return anything: instead, they throw exceptions if when they fail.

Categories