If you use a tool that downloads artifacts from the Central Maven 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 Nexus Professional. In Nexus Professional 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:
|For Windows Users|
|For Mac Users|
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:
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:
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:
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:
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:
|Use Primary Key|
Some PGP tool generates sub key and use it for signing by default, but to make Maven tools recognize the signature, you must use the primary key to sign your artifacts. So you will need to delete the sub signing key, see Delete a Sub Key for the details.
Since other people need your public key to verify your files, you have to distribute your public key to a key server:
Here I distributed my public key to hkp://pool.sks-keyservers.net, 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.
Public keys are synced among key servers, but it may take a while.
Now other people can import your public key from the key server to their local machines:
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:
Then simply deploy your artifacts use normal maven command:
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:$
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:
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.
You will want to configure <mavenExecutorId>forked-path</mavenExecutorId> for maven-release-plugin to get around a GPG plugin hanging issue
Because maven-release-plugin will start a new Maven instance, -Dgpg.passphrase=PASSPHRASE won't work in this case, instead, you should use
mvn release:perform -Darguments=-Dgpg.passphrase=PASSPHRASE
If you are releasing your artifacts on OSSRH, you need to make one more configuration change. Because of the way the maven-release-plugin is configured in the OSS parent POM, you will have to edit the <pluginManagement> block of the <build> section of your POM so that it overrides the parent POM and honors the arguments you are passing in:
That’s it! With the help of GnuPG and maven-gpg-plugin, you can have more confidence of the security of your Maven artifacts.
When you generate your PGP key, you need to specify how long the key should be valid. Let's say 1 year, now the question is, "should I create another key after 1 year?" The answer is no, you can edit your existing key to extend it's valid time.
For example, I have a key pair which expires on 2012-02-27:
$ gpg --list-keys
Now edit this key with the following command: (note that you need to specify the key id as a parameter)
$ gpg --edit-key A6BAB25C
Here is only one key to edit, so I choose 1:
You will see the * after (1), which mean you've selected this key to edit.
To edit the key's expire time, enter the following command: (for a full list of editing command, see this Document)
Enter what you need, for example 10m (10 months), and confirm it.
The last step about editing is saving it what you've done:
Now you can see your key's expires time is updated:
$ gpg --list-keys
Finally, distribute your public key again:
$ gpg --keyserver hkp://pool.sks-keyservers.net --send-keys A6BAB25C
Some PGP tools by default generate a sub signing key and use it for signing instead of using the primary key. This is a problem if you use it to sign artifacts and deploy artifacts to the Central Repository, because Nexus could not get the primary key ID from a signature produced by sub key, thus it could not import the public key and will fail to verify the artifact. The fix is to delete the sub signing key so PGP will use the primary key for signing.
To get an idea weather you have a sub signing key, run command below with your own key ID:
$ gpg --edit-key A6BAB25C
As you can see from above example, this key has 2 sub keys with ID DD289F64 and 8738EC86. The output also shows the creation time and expiration time. What's important here is usage: E stands for Encryption so sub key DD289F64 is used for encryption only, S stands for Signing so sub key 8738EC86 is used for Signing only. If a primary key has a S sub key, it will use it for signing, otherwise itself will do signing job. So we want to delete the sub key 8738EC86.
First select the sub key we want to delete, since its index is 2 (indices starts with 0), we run command:
gpg> key 2
As you can see from the output, the sub key 8738EC86 is marked with *. Now delete it:
If you've already distributed your public key, it's better to revoke the sub signing key instead of deleting it, although either way you can make your primary key as the signing key. See the The GNU Privacy Handbook for the difference between deleting and revoking. To revoke a sub key, use gpg> revkey instead of gpg> delkey.
Ya! 8738EC86 is not listed any more, the final step is saving our change:
That's it! Now you can test the change by signing a file, and then verify it. The output should contain something like: