javamavensecuritydigital-signaturehardware-security-module

How to publish Jar to Maven Central using HSM-produced digital signatures?


My objective is to publish a library on Maven Central, while using a Hardware Security Module (HSM, explanation below) to sign the binary. The documentation I found explains how to use maven-gpg-plugin, but this requires the plugin to handle the key and its passphrase, whereas in my case the key is inside the HSM, and cannot leave it.

To illustrate what I mean, here is a simplified draft of my Github CI pipeline:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'
          cache: maven

      - name: Build the jar
        run: mvn package -DskipTests

      - name: Upload jar for subsequent reuse by signer
        uses: actions/upload-artifact@v3
        with:
          name: compiled-jar
          path: target/library.jar

  sign:
    needs: build
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v3
      - uses: actions/download-artifact@v3
        with:
          name: compiled-jar
          path: target

      - name: Process retrieved files
        run: scripts/sign-gui.ps1 -input target/library.jar -output target/signature.asc

In the end I have a library.jar and signature.asc (the detached armored GPG signature). The next step is to feed them into Maven Central, but the available tooling seems to be shaped around the "maven-gpg-plugin signs it and takes care of everything" paradigm. I was unable to find a way to tell Maven that it should take the signature produced by an external program, and then go on with the rest of its publishing logic. Is this possible with maven-gpg-plugin, in principle? If not, what alternatives are there?

Having explored various open source projects, I found many CI pipelines that have the signing keys and the passphrases to them - thus the key isn't really private. If anyone could point me to repositories that take a different approach, I would greatly appreciate it.

Note: an HSM is a physical device designed to securely store keys and perform operations (e.g., sign, encrypt) with them, without exposing the keys themselves. The keys are generated by the device and held internally in a secure way, to prevent one from making copies of them. The reason for this is to avoid incidents in which someone can steal keys and potentially release their own binaries with your signature on them.


Solution

  • The signature process you are talking about is done when calling scripts/sign-gui.ps1 -input target/library.jar -output target/signature.asc. So this is this powershell script that you need to update.

    Signing an artifact on Maven Central means using an asymmetric key among those supported by GPG (RSA, DSA, ...) that is a PGP primary key. For the GPG software to sign files using an asymmetric key that is provided by a HSM, you need to use the PKCS#11 library that is provided with you HSM: most HSM implement the PKCS#11 API by delivering a PKCS#11 library that offers remote cryptographic services from the HSM.

    When GPG does not know about the private or secret keys, it is using a daemon to perform cryptographic operations: scd (smart card daemon). This has been initially introduced in GPG to use locally connected smart cards readers, not for network HSM. But in your case, the key is stored inside a HSM and must not leave it, not inside a smart card, so you need to have a specially designed GPG daemon that is acting as a gateway between GPG and the HSM, using the PKCS#11 library that comes with your HSM: gnupg-pkcs11-scd (https://github.com/alonbl/gnupg-pkcs11-scd) does the job. The network protocol between the library and the HSM is proprietary, but the library offers a fully specified standard cryptographic API with standard bindings in C and Java.

    Therefore you need to install the PKCS#11 library and configure it to talk to your HSM (you will have to create a X.509 certificate for mutual authentication, or a secret for login/password authentication, depending on your HSM). After that, you need to install the gnupg-pkcs11-scd daemon to offer cryptographic services to GPG using the PKCS#11 library. Finally, you need to configure GPG to talk the the daemon.

    Once all of those steps are done, you need to use GPG to create a GPG key that is able to be used with Maven Central. This is a special GPG key set that contains only a primary key to sign the binaries. This is very specific to Maven Central, see why and how to do that here: https://central.sonatype.org/publish/requirements/gpg/

    Finally, integrate into your powershell script the call to GPG to sign the binary with the primary asymmetric key.

    But since you are using a powershell script, I'm considering you want to do this on Windows. Note that you may encounter difficulties with this environment to install the aforementioned daemon that acts as a gateway between GPG and the PKCS#11 library. In this case, you may try to do all of that on a Linux host, this may be easier. When done, consider a migration back to Windows using a WSL2 environment: everything including the daemon are doing userland operations without requiring external devices, all of this may work correctly on a WSL2 environment.