Inedo Community Forums Forums
    • Recent
    • Tags
    • Popular
    • Login

    Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.

    If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!

    How to download private GPG key of an APT repository

    Scheduled Pinned Locked Moved Support
    11 Posts 3 Posters 28 Views 1 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • hwittenbornH Offline
      hwittenborn
      last edited by

      It didn't look like there was anything in the UI to get the key out of my instance, so I started looking around in the database to see if I could find anything:

      I found the Feeds table which looked like it might contain the key in the SecretKeys field, but GPG isn't recognizing that as valid key if I put it into a file.

      Is there something I'm missing? I'm more than glad to do whatever I need to from the database manually if this isn't something that could be added to the UI in the near future.

      atrippA 1 Reply Last reply Reply Quote 0
      • atrippA Offline
        atripp inedo-engineer @hwittenborn
        last edited by

        @hwittenborn there isn't a way to download the keys once created; it's probably not something we'll add to the UI in the near future since it doesn't seem trivial

        The keys are indeed stored in the SecretKeys, but they're encrypted using the EncryptionKey stored in your ProGet configuration file.

        You can try to decrypt them with this method (note: use EncryptionMode.Aes128)

                /// <summary>
                /// Decrypts data using the specified encryption mode.
                /// </summary>
                /// <param name="data">The data to decrypt.</param>
                /// <param name="mode">Method used to decrypt the data.</param>
                /// <returns>Decrypted data.</returns>
                /// <exception cref="ArgumentNullException"><paramref name="data"/> is null.</exception>
                /// <exception cref="ArgumentOutOfRangeException"><paramref name="mode"/> is invalid.</exception>
                public byte[] Decrypt(byte[] data, EncryptionMode mode)
                {
                    if (data == null)
                        throw new ArgumentNullException(nameof(data));
        
                    byte[]? key = mode switch
                    {
                        EncryptionMode.Aes128 => this.Aes128Key ?? throw new InvalidOperationException("Cannot decrypt value; there is no legacy encryption key defined."),
                        EncryptionMode.Aes256 => this.Aes256Key ?? throw new InvalidOperationException("Cannot decrypt value; there is no AES256 encryption key defined."),
                        EncryptionMode.None => null,
                        _ => throw new ArgumentOutOfRangeException(nameof(mode))
                    };
        
                    if (key == null)
                        return data;
        
                    var iv = new byte[16];
                    Buffer.BlockCopy(data, 0, iv, 0, 8);
                    Buffer.BlockCopy(data, data.Length - 8, iv, 8, 8);
        
                    using var buffer = new MemoryStream(data.Length - 16);
                    buffer.Write(data, 8, data.Length - 16);
                    buffer.Position = 0;
        
                    using var aes = Aes.Create();
                    aes.Key = key;
                    aes.IV = iv;
                    aes.Padding = PaddingMode.PKCS7;
                    using var cryptoStream = new CryptoStream(buffer, aes.CreateDecryptor(), CryptoStreamMode.Read);
                    var output = new byte[SlimBinaryFormatter.ReadLength(cryptoStream)];
        
                    int bytesRead = cryptoStream.Read(output, 0, output.Length);
                    while (bytesRead < output.Length)
                    {
                        int n = cryptoStream.Read(output, bytesRead, output.Length - bytesRead);
                        if (n == 0)
                            throw new InvalidDataException("Cannot decrypt value; stream ended prematurely.");
                        bytesRead += n;
                    }
        
                    return output;
                }
        
        1 Reply Last reply Reply Quote 0
        • hwittenbornH Offline
          hwittenborn
          last edited by

          Do you know where I can find the configuration file @atripp?

          You mentioned that code to run too, but where should I put it if I want to run it? Is it like an extension I put in ProGet, or is it something I'd run manually on a command line?

          stevedennisS 1 Reply Last reply Reply Quote 0
          • stevedennisS Offline
            stevedennis inedo-engineer @hwittenborn
            last edited by

            @hwittenborn it's generally in C:\ProgramData\Inedo\SharedConfig\ProGet.config; here's information about where to find the configuration file:
            https://docs.inedo.com/docs/installation-configuration-files

            It's just sample code; you would need to write a C# program (or whatever language you'd like) that follows that same logic to decrypt the content stored in SecretKeys using AES128

            I'm not entirely how SecretKeys are persisted, but I think either base64 or hex literals

            1 Reply Last reply Reply Quote 0
            • hwittenbornH Offline
              hwittenborn
              last edited by hwittenborn

              @stevedennis: I'm running ProGet through the Docker images, do you know where I could fine the config in that? I haven't been able to find it anywhere inside of my container.

              atrippA 1 Reply Last reply Reply Quote 0
              • atrippA Offline
                atripp inedo-engineer @hwittenborn
                last edited by

                Hi @hwittenborn ,

                For Linux/Docker, it's passed-in as an environment variable:
                https://docs.inedo.com/docs/installation-linux-supported-environment-variables

                It's possible you don't have one, and in that case, the data won't be encrypted. Note that SecretKeys is just a byte[] stored as base64.

                Alana

                1 Reply Last reply Reply Quote 0
                • hwittenbornH Offline
                  hwittenborn
                  last edited by

                  I confirmed non of the environment variables were set @atripp, but after getting the GPG key running echo '{gpg_key}' | base64 -d | gpg --show-keys /dev/stdin is complaining about it being an invalid key.

                  Is the GPG key in that part of the database just in some weird format or something?

                  1 Reply Last reply Reply Quote 0
                  • hwittenbornH Offline
                    hwittenborn
                    last edited by

                    I'm not sure how much it'll help, but this is the specific error I get from GPG:

                    gpg: read_block: read error: Invalid packet
                    gpg: import from '/dev/stdin' failed: Invalid keyring
                    

                    After some further looking it appears that the XML entries in FeedConfiguration_Xml just stop without a closing quote/bracket as well, are they supposed to?

                    atrippA 1 Reply Last reply Reply Quote 0
                    • atrippA Offline
                      atripp inedo-engineer @hwittenborn
                      last edited by

                      Unfortunately I have no idea what format gpg is looking for or how these could be used locally by debian. We mostly know the API/repo format, not so much the client tooling.

                      We use the BouncyCastle encryption library. We simply use that byte array like this:

                      var keys = new PgpSecretKeyRingBundle(this.Data.SecretKeys);
                      
                      using (var output = new MemoryStream())
                      {
                          using (var armor = new ArmoredOutputStream(new UndisposableStream(output)))
                          {
                              if (!detached)
                              {
                                  armor.BeginClearText(HashAlgorithmTag.Sha512);
                                  armor.Write(data, 0, data.Length);
                                  armor.EndClearText();
                              }
                      
                              foreach (PgpSecretKeyRing ring in keys.GetKeyRings())
                              {
                                  var key = ring.GetSecretKey();
                                  var signer = new PgpV3SignatureGenerator(key.PublicKey.Algorithm, HashAlgorithmTag.Sha512);
                                  signer.InitSign(PgpSignature.CanonicalTextDocument, key.ExtractPrivateKeyRaw(null));
                                  signer.Update(data, 0, data.Length);
                                  signer.Generate().Encode(armor);
                              }
                          }
                      
                          return output.ToArray().Where(b => b != '\r').ToArray();
                      }
                      

                      Beyond that no idea how they work. Probably not very helpful, but just FYI

                      1 Reply Last reply Reply Quote 0
                      • hwittenbornH Offline
                        hwittenborn
                        last edited by

                        I'm pretty sure it's just due to Microsoft SQL's CLI cutting off the XML output @atripp, as the XML object would just be unfinished when viewing in my terminal.

                        I'm gonna whip together a quick CLI to try to pull it out programmatically later today, and then I can let you know if it all works. I'm quite confident that's what's been causing my issue though.

                        1 Reply Last reply Reply Quote 0

                        Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                        Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                        With your input, this post could be even better 💗

                        Register Login
                        • 1 / 1
                        • First post
                          Last post
                        Inedo Website Home • Support Home • Code of Conduct • Forums Guide • Documentation