I am trying to add a "secret" via the API.
I am using the sodium R package which provides support for libsodium.
Below I outline my approach where "pub_key_gh" is the character value representing the public key mentioned in the API docs.
1. Serialize the private key which should be encrypted
2. Decode the public key from Github
3. Encrypt the serialized private key using the public key
4. Encode the encrypted private key again because only base64 encoded strings can be pushed via the API
private_key = sodium::hash(charToRaw(private_key)) pub_key_gh_dec = base64enc::base64decode(pub_key_gh) private_key_encr = sodium::simple_encrypt(private_key, pub_key_gh_dec) private_key_encr = base64enc::base64encode(private_key_encr)
However, during the Action run the private key is marked as "invalid format". Hence, most likely the pushed secret was not decoded correctly behind the scenes when being inserted in the run / was uploaded in the wrong format.
Any help is appreciated - I am newbie when it comes to encryption so I can very well be that one of the steps outlined above does not make sense / is not needed.
Manually adding the "private key" via the Github web interface works without problems so its really about how to encrypt the key correctly / upload it in the right format.
Solved! Solved! Go to Solution.
Although I am also an encryption-noob (who isn't?), I just recently created a CLI that pushes secrets to GitHub using the API, so the information is fresh in my mind.
I didn't see nor use any private key in the process.
The same documentation page you referenced has examples in several languages (not R unfortunately) further down the page.
In general, the process as described is this (pseudocode):
# API call to get the public key for the repo
base64_encoded_public_key, key_id = get_public_key_from_github(repo_name)
# Base64 decode the key
public_key = base64_decode(base64_encoded_public_key)
# Encrypt the secret with sodium and the public key plain_secret = "secret" encrypted_secret = sodium_encrypt(public_key, plain_secret)
# Base64 encode the encrypted secret base64_encoded_secret = base64_encode(encrypted_secret)
# Send it to GitHub using the API, and including the key_id as received in step 1 push_secret_to_github(key_id, base64_encoded_secret)
And this is the example in Ruby:
key = Base64.decode64("+ZYvJDZMHUfBkJdyq5Zm9SKqeuBQ4sj+6sfjlH4CgG0=") public_key = RbNaCl::PublicKey.new(key) box = RbNaCl::Boxes::Sealed.from_public_key(public_key) encrypted_secret = box.encrypt("my_secret") # Print the base64 encoded secret puts Base64.strict_encode64(encrypted_secret)
In my development, I also tested that I can decrypt it back. For this you will need to use sodium to generate a public/private key pair (instead of the one you receive from GitHub), then follow the encryption steps as above, using the public key, and then decrypt it with the same sodium method, only this time using the private key - to verify the output is decrypting properly.
If it will help, or if you are in need of a way to do it and are not married to doing it in R, I can share a link to my CLI (in Ruby).
Your post helped, thanks! I got it working now and I think I was already very close yesterday but did some mistakes on the public key side - but I can't really recall.
In the end, my solution looks like this. Here, we assume that the Github public key is already available via some external code.
library("sodium") library("base64enc") # convert to raw for sodium msg = "message" msg_raw <- charToRaw(msg) # decode public key pub_key_gh_dec <- base64enc::base64decode(pub_key_gh) # encrypt using the pub key msg_raw_encr <- sodium::simple_encrypt(msg_raw, pub_key_gh_dec) # base64 encode secret msg_raw_encr_enc <- base64enc::base64encode(msg_raw_encr)