Skip to content
1 change: 1 addition & 0 deletions examples/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ Conan extensions examples
extensions/commands/custom_commands
extensions/deployers/builtin_deployers
extensions/deployers/custom_deployers
extensions/package_signing/package_signing
12 changes: 12 additions & 0 deletions examples/extensions/package_signing/package_signing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.. _examples_extensions_package_signing:


Package Signing Plugin
======================


.. toctree::
:maxdepth: 2


package_signing_with_openssl
145 changes: 145 additions & 0 deletions examples/extensions/package_signing/package_signing_with_openssl.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
.. _examples_extensions_package_signing_openssl:

Signing packages with OpenSSL
=============================

This is an example of a Package Signing Plugin implementation using the `OpensSSL digest tool <https://docs.openssl.org/3.1/man1/openssl-dgst/>`_.
You will need to have ``openssl`` installed and available on your path.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You will need to have ``openssl`` installed and available on your path.
You will need to have ``openssl`` installed at the system level and available in your ``PATH``.


.. include:: ../../../common/experimental_warning.inc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at some point, I think it would be good to clarify:

.. note::

   OpenSSL is used here for demonstration purposes only. The Package Signing
   plugin mechanism is backend-agnostic, and you could implement a similar
   plugin using other tools available in your system (for example, ``gpg``),
   with minimal changes to the signing and verification commands.

This example is available in the examples2 repository: https://github.com/conan-io/examples2/tree/main/examples/extensions/plugins/openssl_sign
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be rendered as a link to the url?

Suggested change
This example is available in the examples2 repository: https://github.com/conan-io/examples2/tree/main/examples/extensions/plugins/openssl_sign
This example is available in the examples2 repository: `examples/extensions/plugins/openssl_sign <https://github.com/conan-io/examples2/tree/main/examples/extensions/plugins/openssl_sign>`_.


Generating the signing keys
+++++++++++++++++++++++++++

To sign and verify the packages using the plugin, first, we will need a public and private key.

To generate the keys using the ``openssl`` executable, we can run:

.. code-block:: bash
$ openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
This will generate the private key used to sign the packages.

Now, we can get the public key from it with this command:

.. code-block:: bash
$ openssl pkey -in private_key.pem -pubout -out public_key.pem
The plugin will use this public key to verify the packages.


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm missing a brief code tour, just the relevant parts of the code

Configuring the plugin
++++++++++++++++++++++

.. caution::

This example stores a private key next to the plugin for simplicity. **Do not do this in production**.
Instead, load the signing key from environment variables or a secret manager, or delegate signing to a remote signing service.
**Always keep the private key out of the Conan cache and out of source control**.

1. Copy the ``examples/extensions/plugins/openssl_sign/sign.py`` file to your Conan home at ``CONAN_HOME/extensions/plugins/sign/sign.py``.

2. Now, place the private key ``private_key.pem`` and the public key ``public_key.pem`` inside a folder ``my-organization`` next to the ``sign.py``
file (``CONAN_HOME/extensions/plugins/sign/my-organization/<copy_keys_here>``).
Comment on lines +44 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be a bit confusing, maybe use less text to explain and put a tree-like structure in the end?

1. Copy the ``sign.py`` file to your Conan home:

   ``CONAN_HOME/extensions/plugins/sign/sign.py``

2. Place the generated keys in a folder named after your provider
   (``my-organization`` in this example), next to ``sign.py``:

Your final folder structure should look like this:

.. code-block:: text

   CONAN_HOME/
   └── extensions/
       └── plugins/
           └── sign/
               ├── sign.py
               └── my-organization/
                   ├── private_key.pem
                   └── public_key.pem


The ``my-organization`` folder serves as the **provider** in this example, and it is used by the plugin to identify the organization that owns the keys.
This will be used later by the ``verify()`` function to **verify the package with the matching public key**.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from this suggestion:

Suggested change
This will be used later by the ``verify()`` function to **verify the package with the matching public key**.
The ``verify()`` function will use it later to **verify the package with the matching public key**.

Maybe we could be a bit more explicit with the This will be used later sentence. What do we mean by this or it?


.. tip::

The Package Signing plugin is installed in the Conan configuration folder, so they can be easily distributed as part of the client
configuration using the :ref:`conan config install<reference_commands_conan_config_install>` command.


Signing packages
++++++++++++++++

Now that the plugin is configured, we can create a package and sign it afterwards:

.. code-block:: bash
$ conan new cmake_lib -d name=hello -d version=1.0
$ conan create
For signing the recipe and package, use the dedicated command:

.. code-block:: bash
$ conan cache sign hello/1.0
hello/1.0: Compressing conan_sources.tgz
hello/1.0:dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b: Compressing conan_package.tgz
Running command: openssl dgst -sha256 -sign C:\Users\user\.conan2\extensions\plugins\sign\my-organization\private_key.pem -out C:\Users\user\.conan2\p\hello092ffa809a9a1\d\metadata\sign\pkgsign-manifest.json.sig C:\Users\user\.conan2\p\hello092ffa809a9a1\d\metadata\sign\pkgsign-manifest.json
Package signed for reference hello/1.0
Running command: openssl dgst -sha256 -sign C:\Users\user\.conan2\extensions\plugins\sign\my-organization\private_key.pem -out C:\Users\user\.conan2\p\b\hello5b13c694fef4a\d\metadata\sign\pkgsign-manifest.json.sig C:\Users\user\.conan2\p\b\hello5b13c694fef4a\d\metadata\sign\pkgsign-manifest.json
Package signed for reference hello/1.0:dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b
[Package sign] Results:
hello/1.0
revisions
53321bba8793db6fea5ea1a98dd6f3d6
packages
dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b
revisions
4b1eaf2e27996cb39cb3774f185fcd8e
[Package sign] Summary: OK=2, FAILED=0
As you see, the command is executing the ``sign()`` function of the plugin that uses the ``openssl`` executable to sign the recipe and the package with a command similar to:

.. code-block:: bash
$ openssl dgst -sha256 -sign private_key.pem -out pkgsign-manifest.json.sig pkgsign-manifest.json
````
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that correct?

Suggested change
````


And it is also using the conan-generated ``pkgsign-manifest.json`` file to create the signature.
You can read more about this manifest file at :ref:`reference_extensions_package_signing`.


Verifying packages
++++++++++++++++++

For verifying the recipe and package, use the dedicated command:

.. code-block:: bash
$ conan cache verify hello/1.0
[Package sign] Checksum verified for file conan_sources.tgz (4ce077cbea9ce87a481b5d6dbb50bd791f4e37e931754cdeb40aeb017baed66c).
[Package sign] Checksum verified for file conanfile.py (0ec44c268f0f255ab59a246c3d13ae6dbd487dea7635b584236b701047f92ba0).
[Package sign] Checksum verified for file conanmanifest.txt (f7f00bb74ed8469a367ed02faded3c763130da9b63dae23916b2a4f099625b15).
Running command: openssl dgst -sha256 -verify C:\Users\user\.conan2\extensions\plugins\sign\my-organization\public_key.pem -signature C:\Users\user\.conan2\p\hello092ffa809a9a1\d\metadata\sign\pkgsign-manifest.json.sig C:\Users\user\.conan2\p\hello092ffa809a9a1\d\metadata\sign\pkgsign-manifest.json
Package verified for reference hello/1.0
[Package sign] Checksum verified for file conan_package.tgz (5cc1b9e330fe5bb6ad5904db45d78ecd6bdc71bcc18eff8d19a1ed126ba5a5aa).
[Package sign] Checksum verified for file conaninfo.txt (f80367b17176346e10640ed813d6d2f1c45ed526822ff71066696179d16e2f2f).
[Package sign] Checksum verified for file conanmanifest.txt (91429ce32c2d0a99de6459a589ac9c35933ed65165ee5c564b6534da57fdfa65).
Running command: openssl dgst -sha256 -verify C:\Users\user\.conan2\extensions\plugins\sign\my-organization\public_key.pem -signature C:\Users\user\.conan2\p\b\hello5b13c694fef4a\d\metadata\sign\pkgsign-manifest.json.sig C:\Users\user\.conan2\p\b\hello5b13c694fef4a\d\metadata\sign\pkgsign-manifest.json
Package verified for reference hello/1.0:dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b
[Package sign] Results:
hello/1.0
revisions
53321bba8793db6fea5ea1a98dd6f3d6
packages
dee9f7f985eb1c20e3c41afaa8c35e2a34b5ae0b
revisions
4b1eaf2e27996cb39cb3774f185fcd8e
[Package sign] Summary: OK=2, FAILED=0
As you see, Conan is performing an internal checksum verification for the files and calling the ``verify()`` function of the plugin that uses
the ``openssl`` executable to verify the recipe and the package with a command similar to:

.. code-block:: bash
$ openssl dgst -sha256 -verify public_key.pem -signature pkgsign-manifest.json.sig pkgsign-manifest.json
.. seealso::

If you want to create your own package signing plugin, check the reference documentation at
:ref:`reference_extensions_package_signing`.
7 changes: 7 additions & 0 deletions reference/extensions/package_signing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,10 @@ Here is a usual flow for signing and verifying packages:
The :command:`conan upload` command **will not automatically sign** the packages since Conan 2.26.0.
Please make sure to use the :command:`conan cache sign` command to **sign the packages before uploading them**,
and **update your plugin** to conform to the new implementation.

Plugin implementation examples
==============================

Here you can find some implementation examples of the plugin so they can serve as guidance to develop your own:

- :ref:`examples_extensions_package_signing_openssl`.