Swift Packages are JSON files that describe a collection of packages. This post will explain how to sign these packages with a trusted certificate entirely from the terminal. These methods should work on Linux and macOS alike. At the end I describe how to have Swift on Linux implicitly trust these packages.
Using this technique I have published my own package collection.
If you're targeting macOS only and find GUIs more intuitive I recommend following the “Swift Package Collection” blog post from Shaps, which is the post that finally made this “click” for me.
The first choice to make is what kind of key to generate. Packages support certificates using either 256-bit EC or 2048-bit RSA keys. Check with your certificate provider which they support.
Apple's Code Signing certificates only support 2048-bit RSA keys. If you want to use a 256-bit EC key you'll need to use an alternative certificate provider.
A 2048-bit RSA key can be generated using:
openssl genrsa -out private.pem 2048
Here's what this command is doing:
genrsa
tellsopenssl
to generate an RSA key-out private.pem
tellsopenssl
to output the generated file toprivate.pem
2048
tellsopenssl
to generate a key with 2048 bits
If your certificate provider supports 256-bit EC keys openssl
can also be used to generate that key.
openssl ecparam -name prime256v1 -genkey -noout -out private.pem
Here's what this command is doing:
ecparam
tellsopenssl
to work with an EC (elliptical curve) key-name prime256v1
tellsopenssl
to use the curve namedprime256v1
(which is 256-bit)-genkey
tellsopenssl
to generate a key-noout
prevents including the public key in the output (it's not necessary here)-out private.pem
tellsopenssl
to output the generated file toprivate.pem
To have a trusted certificate authority to sign our key we need to generate a Certificate Signing Request (CSR):
openssl req -new -key private.pem -out req.csr
You must provide a value for at least 1 field.
This command is doing:
req
specifies we are making a request-new
tellsopenssl
we want to generate a new request-key private.pem
specifies that we want to request thatprivate.pem
be signed-out req.csr
tellsopenssl
tou output the request toreq.csr
The produced req.csr
file can be uploaded to your chosen certificate authority. If you're using Apple you can do this through their developer portal, specifically https://developer.apple.com/account/resources/certificates/add and choose “Swift Package Collection Certificate.”
Apple will only provide 1 code signing certificate per paid developer account.
Download the .cer
file from your certificate provider and keep it safe. Here I'm going to assume you've named it swift_package.cer
.
With your new certificate you can now sign your package:
This is using the swift-package-collection-generator package, which you will need to install separately.
swift run package-collection-sign collection.json signed-collection.json private.pem swift_package.cer
This file can now be uploaded to a server and others can add it.
Need to quickly test hosting the file? ngrok http "file://$(pwd)"
can be used to serve the files over HTTPS and provide external routing.
Supporting Linux
At this point your collection will be trusted when added via Xcode, but not when downloaded on Linux. To work on Linux the consumer needs to add the root certificate that signed your certificate to their trust store. This is stored in ~/.swiftpm/config/trust-root-certs/
. To test this we can start a Docker container that contains Swift and try adding the package.
docker run --rm -it --entrypoint bash swift
This is asking Docker to:
run
a container--rm
: remove it if it exists--it
: Makes the container--interactive
and attaches a--tty
--entrypoint bash
: Enters the container usingbash
swift
: Use the latest Swift image (at the time of writing this is 5.5.2)
We can try adding the package without making any changes:
swift package-collection add https://example.com/swift-package-collection.json
This will display the following error:
Error: The collection's signature is invalid. If you would still like to add it please rerun 'add' with '--skip-signature-check'.
To be able to download the certificate you'll need a tool such as wget
:
apt update apt install wget
The target directory also needs to exist:
mkdir -p ~/.swiftpm/config/trust-root-certs/
You can get the root certificate from your certificate provider. To see the name of the issuer run openssl x509 -noout -inform DER -in swift_package.cer -issuer
.
With my Apple-provided cert this outputs:
issuer= /CN=Apple Worldwide Developer Relations Certification Authority/OU=G3/O=Apple Inc./C=US
This certificate can be found on Apple's PKI. Copy the URL and download it in the container:
wget https://www.apple.com/certificateauthority/AppleWWDRCAG3.cer -O ~/.swiftpm/config/trust-root-certs/AppleWWDRCAG3.cer
If we rerun the command we should now see:
Added "Joseph Duffy's Collection" to your package collections.
🎉 Our package is now signed and can be trusted by by both macOS and Linux users!
Now all you need to do is upload the signed-collection.json
file somewhere and link people to it.