Saturday, February 3, 2018

Making your own CA work that works with Google Chrome



Introduction

This post is about one single aspect of running your own CA (usually for internal systems): Making it actually work with Google Chrome.

The reason I writing this down is because I found all the other sources of information around the world very confusing.

The main problem is twofold:
  1. Newer browsers (like Google Chrome) require that the "Subject Alternative Name" has been defined as part of the certificate.
  2. When simply signing a certificate request (the includes the "Subject Alternative Name") with openssl this property is lost and the resulting certificate doesn't work. 

Create your own CA.

On CentOS 7 (which I use right now) the openssl package contains the /etc/pki/tls/misc/CA script that makes doing this very easy.

Generate key and create a certificate request.

Now the confusing part here is that it doesn't matter if the "Subject Alternative Name" is or isn't present in the certificate request. If the "Subject Alternative Name" is present in the certificate request it will be ignored anyway.

So you can create the key and certificate request with any existing system and manual.

Three basic ways for doing this:
  • Some systems already have one and/or is capable of generating one as part of the solution (like I have seen in VMware ESXi). So this part is done for you.
  • Simply using the script provided with openssl for this purpose
/etc/pki/tls/misc/CA -newreq
This will create  newkey.pem and newreq.pem
  • Manually via a config file.
    So if you create a config file with something like this:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[ dn ]
C=NL
ST=Noord Holland
L=Amstelveen
O=Basjes
OU=At home
emailAddress=certificates@example.com
CN = server.example.com
then with this config file (let's call it server.example.com.config) you can do 
openssl req -new -sha256 -nodes \
        -out server.example.com.csr \
        -newkey rsa:2048 \
        -keyout server.example.com.key \
        -config server.example.com.config

Now sign the request and add the desired "Subject Alternative Name" values

To do this we create this config file where we include all the hostnames and IP addresses we want to allow (let's call it server.example.com.sanconfig).

[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = server.example.com
DNS.2 = www.server.example.com
IP = 10.11.12.13

Now we sign this where we add the config file (-extfile) and instruct to use the extra settings present under v3_req (-extensions). The actual name of the "v3_req" section doesn't really matter.

openssl ca -policy policy_anything \
       -out server.example.com.crt \
       -in server.example.com.csr \
       -extfile server.example.com.sanconfig \
                 -extensions v3_req

If during the signing you see something like this then everything went right.

Certificate Details:
        ...
        X509v3 extensions:
            ...
            X509v3 Subject Alternative Name: 
                DNS:server.example.com, DNS:www.server.example.com, IP Address:10.11.12.13

As you see the signing command is pretty standard and really only adds the config file with the SAN extension.

Last remark

And to actually make your own CA work with chrome you have to import the certificate of your own authority /etc/pki/CA/cacert.pem file into chrome as a new authority.


Monday, December 14, 2015

Writing Avro records to Parquet

Today I tried to write records created using Apache Avro to disk using Apache Parquet in Java.
Although this seems like a trivial task I ran into a big obstacle which was primarily caused by a lack in my knowledge of how generics in Java work.

Because all constructors are either deprecated or package private the builder pattern is the way to go.
So I simply tried this (assisted by the fact that IntelliJ 'suggested' this):

Path outputPath = new Path("/home/nbasjes/tmp/measurement.parquet");

ParquetWriter<Measurement> parquetWriter =
        AvroParquetWriter<Measurement>.builder(outputPath)
            .withSchema(Measurement.getClassSchema())
            .withCompressionCodec(CompressionCodecName.GZIP)
            .build();

This fails to compile with terrible errors and does not yield an understandable reason why it does so.
None of the unit tests in the Parquet code base use the builder pattern yet; they are still on the methods that have been deprecated.

Looking at the source of the class that needs to do the work you'll see this:

public class AvroParquetWriter<T> extends ParquetWriter<T> {
  public static <T> Builder<T> builder(Path file) {
    return new Builder<T>(file);
  }



After talking to a colleague of mine he pointed out that calling a static method in generic has a slightly different syntax. Apparently calling such a method requires the following syntax (which works like a charm):

Path outputPath = new Path("/home/nbasjes/tmp/measurement.parquet");

ParquetWriter<Measurement> parquetWriter =
        AvroParquetWriter.<Measurement>builder(outputPath)
            .withSchema(Measurement.getClassSchema())
            .withCompressionCodec(CompressionCodecName.GZIP)
            .build();

Let me guess; You don't see the difference?
It is the '.' before the call to the static builder method.

So instead of this
        AvroParquetWriter<Measurement>.builder(outputPath)
you must do this
        AvroParquetWriter.<Measurement>builder(outputPath)