If you use a tool that downloads artifacts from the Central repository, you need to make sure that you are making an effort to validate that these artifacts have a valid PGP signature that can be verified against a public key server. If you don't validate signatures, then you have no guarantee that what you are downloading is the original artifact. One way to to verify signatures on artifacts is to use a repository manager like Sonatype Nexus Repository. In Sonatype Nexus Repository, you can configure the procurement suite to check every downloaded artifact for a valid PGP signature and validate the signature against a public keyserver.
If you are developing software using Maven, you should generate a PGP signature for your releases. Releasing software with valid signatures means that your customers can verify that a software artifact was generated by the original author and that it hasn't been modified by anyone in transit. Most large OSS forges like the Apache Software Foundation require all projects to be released by a release manager whose key has been signed by other members of the organization, and if you want to synchronize your software artifacts to Maven central you are required to provide PGP signatures.
In this post, I show you how to configure your Maven project to generate a valid signature using GPG. GnuPG (aka, GPG) is a freely available implementation of the OpenPGP standard. It's available for both Windows and Unix-like computers. GPG provides you with the capability to generate a signature, manage keys, and verify signatures. In the following sections, I will introduce GPG as well as maven-gpg-plugin which provides Maven goals to generate signatures for a release.
Download GPG from http://www.gnupg.org/download/, follow the instructions and install it to your system. Verify your gpg installation by running gpg with the version flag:
juven@juven-ubuntu:~$ gpg --version gpg (GnuPG) 1.4.9Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later ...
Before you do anything with GPG, you will need to generate a key pair for yourself. Once you have you own key pair, you can use your private key to sign artifacts, and distribute your public key to public key servers and end-users so that they can validate artifacts signed with your private key.
Generate a key pair like this:
juven@juven-ubuntu:~$ gpg --gen-key
You'll be asked for the type, the size, and the time of validity for the key, just use the default value if you don't have any special requirements. You will be asked to input your name, email, and comment for the key. These identifiers are essential as they will be seen by anyone downloading a software artifact and validating a signature. Finally, you can provide a passphase to protect your secret key, this is not mandatory, but I highly recommend you to do this. It is essential that you choose a secure passphrase and that you do not divulge it to any one. This passphrase and your private key are all that is needed to sign artifacts with your signature.
Once key pair is generated, we can list them to console (along with any other keys imported to local machine) List public keys:
juven@juven-ubuntu:~$ gpg --list-keys /home/juven/.gnupg/pubring.gpg ------------------------------ pub 1024D/C6EED57A 2010-01-13 uid Juven Xu (Juven Xu works at Sonatype) <juven@sonatype.com> sub 2048g/D704745C 2010-01-13
Here /home/juven/.gnupg/pubring.gpg is where my public key are stored. The line starting with pub shows the length (1204D), the keyid (C6EED57A), and the creation date (2010-01-13) of the public key. The next line shows the UID of the key, which is composed of a name, a comment, and an email. The last line shows sub-keys, we don't need to worried about this for now.
List private keys:
juven@juven-ubuntu:~$ gpg --list-secret-keys /home/juven/.gnupg/secring.gpg ------------------------------ sec 1024D/C6EED57A 2010-01-13 uid Juven Xu (Juven Xu works at Sonatype) ssb 2048g/D704745C 2010-01-13
Similar to public keys, here we can see the storage location of the private key, as well key length, keyid and UID.
To create an ASCII formatted signature for any file, run the following gpg command:
juven@juven-ubuntu:~$ gpg -ab temp.java
The -a option tells gpg to create ASCII armored output, the -b option tells gpg to make a detached signature. If your private key has a passphrase, you will be asked for it. Without a passphrase, all someone needs to forge an artifact signature is your private key. The passphrase is an extra level of protection.
GPG will create a file like temp.java.asc, which is the signature of temp.java. You will want to distribute it along with the main file so the other can verify the main file using your public key:
gpg --verify temp.java.asc
Since other people need your public key to verify your files, you have to distribute your public key to a key server:
gpg --keyserver hkp://pgp.mit.edu --send-keys C6EED57A
Here, I distributed my public key to hkp://pgp.mit.edu, use --keyserver along with a key server address, and use --send-keys along with a keyid. You can get your keyid by listing the public keys. Note that public keys are synced among key servers.
Now other people can import your public key from the key server to their local machines:
gpg --keyserver hkp://pgp.mit.edu --recv-keys C6EED57A
When you release a Maven project, you have two options: You can use the manual signing technique shown above, or you can use an automated approach. Manually signing and deploying artifacts to a repository is very time-consuming. The maven-gpg-plugin is very handy in this case. With a few lines of configuration, it can help you automatically sign the maven artifacts.
Before using maven-gpg-plugin, make sure gpg is available on the search path. Configure maven-gpg-plugin in your project's POM like this:
<project> ... <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> <executions> <execution> <id>sign-artifacts</id> <phase>verify</phase> <goals> <goal>sign</goal> </goals> </execution> </executions> </plugin> </plugins> </build> ... </project>
Then simply deploy your artifacts use normal Maven command:
mvn clean deploy -Dgpg.passphrase=yourpassphrase
If you don't specify the passphrase, you will be prompted for one.
What if you have some artifacts which have already been released but not signed? You want to sign and deploy them into repository as well. Fortunately maven-gpg-plugin has another goal for this use case:
$ mvn gpg:sign-and-deploy-file > -DpomFile=target/myapp-1.0.pom > -Dfile=target/myapp-1.0.jar > -Durl=http://oss.sonatype.org/service/local/staging/deploy/maven2/ > -DrepositoryId=sonatype_oss
The last thing I want to mention about maven-gpg-plugin is wrapping the plugin configuration with a profile. Usually when you build SNAPSHOT version, you don't want to sign the artifacts, because it's unnecessary and time-consuming. Artifact signing is only necessary when it's time to release project. So, we can wrap the plugin configuration with a profile, which will only be activated on project releasing:
<profiles> <profile> <id>release-sign-artifacts</id> <activation> <property> <name>performRelease</name> <value>true</value> </property> </activation> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> <executions> <execution> <id>sign-artifacts</id> <phase>verify</phase> <goals> <goal>sign</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> </profiles>
This profile will be activated when the value of maven property performRelease is true. When you use maven-release-plugin and run mvn release:perform, the property value will be set to true.
That's it! With the help of GnuPG and maven-gpg-plugin, you can have more confidence of the security of your Maven artifacts.