Code signing is the process of applying a digital signature to a piece of code so that it can later be verified that the code was written by a particular author and has not been modified. In Niagara, this allows us to verify that modules that are installed were written by a trusted author and helps to prevent malicious code from getting installed. Signing is currently optional for third party modules, but will be required in a future release. We recommend getting started with code signing as soon as possible to be prepared when we start to require it.
Code signing is enabled by default in the Niagara build environment starting in 4.6. Any module you build will automatically be signed with a generic, auto-generated, self-signed certificate. After building a module, you will notice some files in USER_HOME/.tridium/security
. The niagara.signing.jks
file is the default signing profile keystore, and the niagara.signing.xml
file stores settings for the signing profile. This is a good starting point and useful for development, but you will probably want to sign your modules with a different key when releasing, and you may want to use a CA signed certificate in development to make testing easier. To make changes to the signing profile, we can either modify the default signing profile, or create a new one.
To create a signing profile, we first need a keystore that contains our signing keys. To create a new certificate and import it into a new keystore, execute the following command.
keytool -genkey -alias my-cert -keyalg RSA -ext EKU="codeSigning" -validity 365 -keypass K3yP@ss -storepass St0reP@ss -keystore my_signing_profile.jks
Keytool is a standard Java utility. These options can be altered as necessary. Run keytool -help
for more information.
Next, we need to create the xml file to store our signing profile settings. This must have the same name as the keystore file, and must be a peer to it, so create a file named my_signing_profile.xml with the following contents:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Code Signing Properties</comment>
<entry key="niagara.signing.keypass.my-cert">K3yP@ss</entry>
<entry key="niagara.signing.storepass">St0reP@ss</entry>
<entry key="niagara.signing.profileType">com.tridium.signing.RestrictedSigningProfile</entry>
</properties>
This file stores the key password for the signing key we just generated, and the keystore password for the keystore we created. It also sets the signing profile to the RestrictedSigningProfile
. The default signing profile is the LocalSigningProfile
, which will automatically regenerate a generic certificate when the existing one gets close to expiration. This is useful if you are using generic self-signed certificates, but since we are using custom certificates, we do not want it to be automatically regenerated.
For this new signing profile to take effect, we need to set the niagara.signing.profile
system property to the path of the profile’s xml file. We can do this either by passing a system property argument to gradle commands from the command line
gradlew jar -Pniagara.signing.profile=/path/to/my_signing_profile.xml
Or you can add it to your gradle.properties file at USER_HOME/.gradle/gradle.properties
niagara.signing.profile=/path/to/my_signing_profile.xml
By default, modules will be signed using the certificate in your signing profile with the alias Niagara4Modules
. Since we used a different alias for our certificate, we will have to specify that alias in our build environment. The alias can be specified globally by adding the following block to your NIAGARA_USER_HOME/build.gradle
file.
...
subprojects { Project p ->
p.pluginManager.withPlugin('com.tridium.niagara-module') {
p.niagaraModule {
certAlias = "my-cert"
}
}
}
...
This should be sufficient in most cases, but if you discover you need to sign certain modules with a different certificate, you can specify the alias on a module specific basis by adding the following to your module’s gradle file.
...
niagaraModule {
moduleName = "module"
preferredSymbol = "mod"
runtimeProfile = "rt"
certAlias = "my-cert"
...
}
...
If you find you need to sign any modules with multiple certificates, you can specify the certAlias property in either of these locations with a comma separated list.
certAlias = "my-cert,my-other-cert"
Timestamping provides proof that a signature was created at a particular time. This allows us to be sure that a module was signed while the signing certificate was within its validity period. Without timestamping, a signed module cannot be successfully validated after the signing certificate has expired, so we recommend timestamping all modules that will be released for use in production environments.
To enable timestamping, add the following to your signing profile
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Code Signing Properties</comment>
<entry key="niagara.signing.keypass.my-cert">K3yP@ss</entry>
<entry key="niagara.signing.storepass">St0reP@ss</entry>
<entry key="niagara.signing.profileType">com.tridium.signing.RestrictedSigningProfile</entry>
<!-- The lines below will enable timestamping -->
<entry key="niagara.signing.standardtsa">
http://sha256timestamp.ws.symantec.com/sha256/timestamp
</entry>
</properties>
The niagara.signing.standardtsa
property can be set to any publicly available RFC 3161 compliant SHA-256 time stamp authority server. The Symantec TSA has worked well for us, so there should be no reason to change this unless the server goes down or there is a connectivity problem from your network.
Enabling timestamping will require an internet connection when building modules, but the signature and timestamp can be verified in Niagara even when running offline.
When we eventually require code signing for all third party modules, the certificate that a module is signed with must be trusted for it to run in a Niagara installation. There are a few ways this can be accomplished. The first is to purchase a signed certificate from a trusted certificate authority. This is the easiest method because the certificate will automatically be trusted by all Niagara installations, so there is no need to install additional certificates when installing the signed module. This is also the most expensive method since a new certificate must be purchased each time it expires. If this cost is prohibitive, one of the following alternatives can be used.
The second option is to use a certificate that is signed by an internal certificate authority, then install the CA certificate into the user trust store of all Niagara installations where the module will be installed. Multiple certificates can be signed by the same internal CA, and any Niagara installation with the CA certificate installed will trust any module signed with any certificate issued by the CA. To import the internal CA certificate into your Niagara user trust store, you must first obtain the CA certificate in PEM format. If it is not available in PEM format, it can be converted using OpenSSL or a similar tool. You can then import the certificate using Niagara’s Certificate Management.
The third option is to sign modules with a self-signed certificate and install that certificate into the user trust store of any Niagara installation where the module will be installed. This is the least scalable approach, but can be useful for small scale operations or during development. To import the certificate into your Niagara user trust store, you will have to first export it from your keystore.
keytool -exportcert -alias my-cert -keypass K3yP@ss -storepass St0reP@ss -keystore my_signing_profile.jks -rfc -file my-cert.pem
You can then import the certificate using Niagara’s Certificate Management. There is also a provisioning job available to install a certificate into the user trust store of multiple hosts at once to make things easier for larger installations.
The first two approaches described above will require you to get your certificate signed by a CA and import it back into your keystore. To do this, you first need to generate a certificate signing request with the following command
keytool -certreq -alias my-cert -keypass K3yP@ss -storepass St0reP@ss -keystore my_signing_profile.jks -file my-cert.csr
Then you should take the my-cert.csr
file and send it to your CA to be signed, and they will send you back the signed certificate, which you will import back into your keystore with the command
keytool -importcert -file my-cert.cer -alias my-cert -keypass K3yP@ss -storepass St0reP@ss -keystore my_signing_profile.jks
It is important to properly manage your code signing keys and protect the keys you use for release because if your keys are exposed, someone could impersonate you by signing code with your signing key, defeating the purpose of code signing.
We suggest having a separate signing certificate for signing release modules and storing that on a separate machine where all release builds will be done. Access to this machine and the signing certificate should be limited to a few trusted developers, and only modules that are intended to be released to customers or used in a production environment should be signed with this certificate.
During development and testing, individual developers can use the default generic self-signed certificate and install it in the user trust store of Niagara test installations to establish a chain of trust as described above. Another option for development certificates is to have all developer certificates signed by an internal development CA, then that CA certificate can be installed in the user trust store of Niagara test installations organization wide. This allows developers to share modules for testing without having to install every developers certificate in their test environment.
There are currently two Niagara permission groups Niagara permission groups that require signed modules due to their sensitivity. These are REFLECTION, and HSM_SIGNING. Any module requesting either of these permissions must be validly signed, and must be trusted by the Niagara installation in which it is installed as described in the Establishing Trust
section above. Any module requesting either of these permissions that is not signed and trusted will cause the Station or Workbench to halt when the module is loaded.
Copyright © 2000-2019 Tridium Inc. All rights reserved.