Skip to content
Matt Magoffin edited this page Dec 11, 2012 · 14 revisions

SolarNetwork Developer PKI Guide

This page describes how to set up PKI support when developing SolarNode <-> SolarNet secure communication. This guide assumes you have your Eclipse development environment set up as well as the SolarNode and SolarNet environments.

Configure a development DNS name

For TLS to work you'll need to use a DNS name, which you can make up and assign in your system's host file. For example on Unix systems, you can usually add a line to /etc/hosts like this:

127.0.0.1	solarnetworkdev.net

Whatever name you choose, it must match what we configure in the server's certificate subject later on in this guide.

Set up Certification Authority (CA)

You must create a Certification Authority that will act as the trusted root for the SolarNetwork platform. There are several ways to accomplish this, but this guide will show how using OpenSSL. There are lots of references such as this or that to help you create the CA self-signed certificate and then sign other certificates with that CA certificate. We'll describe one method here, creating a CA root certificate CN=Developer CA, O=SolarDev on a Unix platform.

OpenSSL expects a certain directory layout for its ca tool to function. Create that structure with the following command:

mkdir -m 0700 CA CA/certs CA/crl CA/newcerts CA/private

Now we'll create our CA private key and then the CA root certificate:

# Create a private key of 4096 bits
openssl genrsa -des3 -out CA/private/ca.key 4096

# Create a self-signed root "CA" certificate using our private key, that expires in 10 years
openssl req -new -x509 -extensions v3_ca -key CA/private/ca.key -out CA/certs/ca.crt \
	-days 3650 -subj '/CN=Developer CA/O=SolarDev/'
	
# Create some database files the 'ca' tool needs
touch CA/index.txt
echo '01' >CA/serial

Now create a policy file for the CA, named policy.cfg with the following contents:

[ ca ]
default_ca      = CA_solardev

[ CA_solardev ]

dir            = ./CA
database       = $dir/index.txt
new_certs_dir  = $dir/newcerts

certificate    = $dir/certs/ca.crt
serial         = $dir/serial
private_key    = $dir/private/ca.key
RANDFILE       = $dir/private/.rand

default_days   = 3650
default_crl_days= 30
default_md     = md5

policy         = policy_solardevweb
email_in_dn    = no                    # Don't add the email into cert DN

name_opt       = ca_default            # Subject name display option
cert_opt       = ca_default            # Certificate display option
copy_extensions = none                 # Don't copy extensions from request

[ policy_solardevweb ]
organizationName       = supplied
commonName             = supplied

[ policy_solardevnode ]
organizationName       = supplied
userId            	   = supplied

Notice we've declared two policy sections, one named solardevweb and the other solardevnode. The web policy is for our webserver certificate, whose subject will take the form CN=solarnetworkdev.net,O=SolarDev. The CN in the subject must match the DNS name you created earlier. The node policy is for our node certificate(s), whose subject will take the form UID=1,O=SolarDev. The UID in the subject will contain the node's unique ID.

Generate Development webserver certificate

The SolarNet server will need a server TLS certificate now to complete the secure connection to the SolarNode. First you must generate a new private key and CSR for the webserver, which must use the same DNS name you created earlier, solarnetworkdev.net:

# Create a 2048-bit webserver certificate signing request (CSR) for the domain 'solarnetworkdev.net'
openssl req -new -nodes -newkey rsa:2048 -keyout solarnetworkdev.key \
	-out solarnetworkdev.csr -days 3650 -subj '/CN=solarnetworkdev.net/O=SolarDev/'

Now sign the webserver certificate with our CA's private key, using the web policy:

openssl ca -in solarnetworkdev.csr -out CA/certs/solarnetworkdev.crt \
	-config policy.cfg -policy policy_solardevweb 

Use OpenSSL to export the certificates as a PKCS#12 file, with the webserver certificate named central and the CA certificate ca:

openssl pkcs12 -export -in CA/certs/solarnetworkdev.crt -inkey solarnetworkdev.key \
	-out solarnetworkdev.net.p12 -name central -CAfile CA/certs/ca.crt -caname ca -chain

It will ask you to assign a password. Use the same password you configured in the PKIService.manualKeyStorePassword setting, for example dev123.

Now, Java client certificates won't work correctly unless the key store format is JKS. We'll create a JKS store from the PKCS#12 file you just created, but we must do so in two steps (you'll need to assign a password to the new central.jks key store, so continue using the same password as you used in the PKCS#12 store, for example dev123):

# First import the CA certificate
keytool -import -keystore central.jks -file CA/certs/ca.crt -alias ca

# Now import the web server certificate + private key
keytool -importkeystore -srckeystore solarnetworkdev.net.p12 -srcstoretype PKCS12 \
	-destkeystore central.jks

Now move the central.jks file to /solarnetwork-osgi-target/conf/tls/central.jks.

Configure Tomcat with TLS support

If you haven't already created the /solarnetwork-osgi-target/config/tomcat-server.xml file, copy the example file from /solarnetwork-osgi-target/example/config. Then add (or uncomment the example) TLS Connector element, which should look like this:

	<Connector port="8683" protocol="HTTP/1.1" SSLEnabled="true"
		maxThreads="25" scheme="https" secure="true" sslProtocol="TLS"
		keyAlias="central" keystoreType="jks"
		keystoreFile="conf/tls/central.jks" keystorePass="dev123"
		truststoreFile="conf/tls/central.jks" truststorePass="dev123"
		clientAuth="want"/>

Note the port setting, which you'll need to also configure the same value for later on. Also note the keystorePass and truststorePass values. We're using the same file as the key store and trust store, so the passwords will be the same value you used when you created the PKCS#12 file with OpenSSL.

Also in the tomcat-server.xml file, you must modify the Engine and Host elements to use the DNS name you created earlier and is the CN portion of your webserver certificate subject:

<Engine name="Catalina" defaultHost="solarnetworkdev.net">
	<Host name="solarnetworkdev.net" unpackWARs="false" autoDeploy="false"
		liveDeploy="false" deployOnStartup="false" xmlValidation="false"
		xmlNamespaceAware="false"/>
</Engine>

Notice the Engine/@defaultHost attribute and the Host/@name attribute both use our DNS name now. Most likely they would have had localhost used before you change them.

Create initial node key store

Since our Tomcat certificate is from our own CA, our node won't be able to communicate with Tomcat unless we add the CA's certificate to the node's trust store. That just means we need to create the node's key store with the CA certificate already in it. The node expects a key store in the Java JKS format.

keytool -import -keystore node.jks -file CA/certs/ca.crt -alias ca

Now move the node.jks file to /solarnetwork-osgi-target/conf/tls/node.jks.

Configure development settings

You need to configure some development settings in the /solarnetwork-osgi-target/configurations/services directory.

File Description
net.solarnetwork.central.in.cfg

Configure the host as the DNS name you assigned earlier, and the same port that you configured the Tomcat server to use. For example:

			<p><code>SimpleNetworkIdentityBiz.host = solarnetworkdev.net<br/>
			SimpleNetworkIdentityBiz.port = 8683<br/>
			SimpleNetworkIdentityBiz.forceTLS = true</code></p>
			<br/>
		</td>
	</tr>
	<tr>
		<td><b>net.solarnetwork.central.user.biz.cfg</b></td>
		<td>
			<p>Configure a development certificate subject DN template that matches the PKI infrastructure you want to set up. For example:</p>
			
			<p><code>RegistrationBiz.networkCertificateSubjectDNFormat = UID=%s,O=SolarDev</code></p>
			<br/>
		</td>
	</tr>
	<tr>
		<td><b>net.solarnetwork.node.setup.cfg</b></td>
		<td>
			<p>Configure a simple manual password for the node's key store. This will simplify configuring TLS in Tomcat. For example:</p>
			
			<p><code>PKIService.manualKeyStorePassword = dev123</code></p>
			<br/>
		</td>
	</tr>
</tbody>

Next you need to update the OSGi runtime in Eclipse so the node uses the node.jks file as its trust store, enabling it to "trust" your CA root certificate. Go to **Run > Run Configurations... > OSGi Framework > SolarNetwork ** and select the Arguments tab. Add the following to the VM arguments field:

-Djavax.net.ssl.trustStore=conf/tls/node.jks -Djavax.net.ssl.trustStorePassword=dev123

Associate a new node

Start up the SolarNetwork OSGi platform in Eclipse, and then use the SolarNet registration app (/solarreg) to register yourself as a new SolarNet user (if you haven't already). You can use the net.solarnetwork.central.common.mail.mock bundle to have the user registration code logged to the console rather than relying on an actual email to be sent. Once registered, invite a new SolarNode (under the My Nodes section). Copy the invitation code, then go to the SolarNode setup app (/setup). Paste in the invitation and complete the association process.

At this point, you'll have assigned the node an ID and generated a CSR for the node's certificate. Save this to a file, for example 1013.csr. Here I'm using the assigned node ID 1013 but your node ID will probably be different.

Sign the node's CSR

openssl ca -in 1013.csr -out CA/certs/1013.crt -config policy.cfg -policy policy_solardevnode

Then create a PKCS#7 chain file to import into the SolarNode app:

openssl crl2pkcs7 -nocrl -certfile CA/certs/ca.crt -certfile CA/certs/1013.crt -out 1013-chain.p7p

Now use the Certificates section on the SolarNode app to import the certificate chain you created. When all is finally complete, you should end up with a screen similar to the following:

Holy Moly, does it even work?

To test if everything is working as expected, enable some mock data to be collected on the node and then have it uploaded to SolarIn to see that everything works smoothly. For example, deploy these node projects in Eclipse to collect and post some fake PowerDatum data:

  • net.solarnetwork.node.power
  • net.solarnetwork.node.power.mock
  • net.solarnetwork.node.upload.bulkxmlwebpost

Watch the console for messages like

INFO  Got Datum to persist: PowerDatum{pvAmps=5.0,pvVolts=18.0,batVolts=12.0,ampHoursToday=12.0,kwHoursToday=0.144}

followed later on by

INFO  Bulk uploaded 1 objects to [BulkXmlWebPostUploadService:solarnetworkdev.net]

If you see messages like that, you're good to go!

Clone this wiki locally