gpg — decrypt only content directly to stout - bash

I have the file secret.txt.gpg which I would like to decrypt, such that the content is stored within a variable like that:
TXT=$(gpg --decrypt secret.txt.gpg)
But this way a lot of extra gpg: … lines are added, containing information about the key etc. like that:
gpg: encrypted with 4096-bit RSA key, ID xxxx, created xxxx
"xx xx (xx) <xx#xx.xx>"
gpg: Signature made xxx
gpg: using RSA key xxx
…
Secret Message
By the way:
gpg -d secret.txt.gpg > out.txt
is just writing the content into the file.
How can I capture the content only, without writing it to a file?
update
Even though #Roger Answer is better and explains the why, I could make it using this:
TXT=$(gpg --decrypt secret.txt.gpg > /dev/stdout)

The GPG messages are written to STDERR, which is why piping STDOUT to a file omits those messages.
If the encrypted file is not signed you usually suppress the messages about encryption by providing the --quiet switch. If the file is signed you will still get messages about the signature. Even if you provide the --skip-verify you will still get a message gently informing you that signature verification was suppressed.
In order to suppress all those message I suggest you pipe STDERR to /dev/null, e.g.:
TXT=$(gpg --decrypt secret.txt.gpg 2>/dev/null)

Related

How do you verify an encrypted and signed file with gpg?

I am trying to get a better understanding of what is going on with gpg.
If you have a file and sign it: gpg --sign file.txt
you can verify it with: gpg --verify file.txt.gpg
when you get a successful output: gpg: Signature made...
But when you sign AND encrypt a file: gpg --encrypt --sign -r test#email.com file.txt
and then run --verify on the encrypted file I get: gpg: verify signatures failed: Unexpected error
I know that I can just call --decrypt on the file and it will verify and decrypt it, but what if I want to verify only?
I figured out the answer to this and then some. So I am going to add some additional information for clarity.
First of all, I realize based on the last line to this answer that gpg uses SIGN THEN ENCRYPT. Which means calling --verify or any variation to verify on an encrypted file will just output gpg: verify signatures failed: Unexpected error. This happens because the signature is "hidden" in encryption, so when you try to call --verify on the file, it will not see a signature.
Secondly, the --decrypt flag will both decrypt the file AND if the file is signed, verify it too.
Here is what --decrypt is doing. It looks at your default secret keyring secring.kbx in ~/.gnupg to use a secret key for decrypting the file. Then after it is decrypted, it looks at your default public keyring pubring.kbx in the folder ~/.gnupg and tries to verify the signature on the file, if it has one.
If it has no signature, it will just decrypt the file.
If it has a signature, but you don't have the public key, it will decrypt the file but it will fail to verify the signature.
If it has a signature and you have the public key, it will decrypt and verify.
With that said, there is no reason to verify a signed file BEFORE decrypting it.
Thirdly, as an added bonus, you can also specify a keyring you want to use for decrypting and verification. Say you want to use a temporary keyring to verify signatures or for what ever reason you want a temporary keyring to decrypt the message too.
You can specify the keyrings for --decrypt to use with the following command:
gpg --secret-keyring path/to/temp/secring.kbx --keyring path/to/temp/pubring.kbx --decrypt file.txt.gpg
This command will look for the secret ring and public ring at the specified paths in order to use those rings for decryption and verification instead of the default rings found in ~/.gnupg. Want to use a default ring with a temp ring? Just omit the flag and path to the ring you want defaulted.
All in all, for encrypted and signed files, if you want to decrypt and verify that file, you need to make sure that the private key for decryption is in your secret keyring and the public key for verification is in your public keyring.
One thing to understand about GPG encrypt & sign, which isn't very well explained, is that the signature can only be verified by the recipient.
Suppose Alice encrypts a file to send to Bob. She will encrypt with Bob's public key, and sign with her private key.
gpg --output encrypted.gpg --recipient B0B0000000000000000000000000000000000000 --armor --sign --default-key A11CE00000000000000000000000000000000000 --encrypt file-to-encrypt.txt
There's no way now for Alice, or anyone who does not have Bob's private key, to verify the signature.
Now Bob will decrypt the file. If it is signed, he'll see information about the signature in the output:
$ gpg --decrypt encrypted.gpg > decrypted.txt
gpg: encrypted with 2048-bit RSA key, ID D83A4C12B3840EBA, created 2020-09-24
"Alice <alice#example.com>"
gpg: Signature made 09/28/20 13:16:47 Eastern Daylight Time
gpg: using RSA key A11CE00000000000000000000000000000000000
gpg: Good signature from "Alice <alice#example.com>" [ultimate]
Note the Signature made and Good signature lines in the output.
$ gpg --encrypt --sign -r test#email.com file.txt
After file.txt.gpg generated, try the command below:
$ gpg -d file.txt.gpg
or just execute:
$ gpg file.txt.gpg

GPG --gen -key with password in a separate file

I want to encrypt a file using a passphrase, which I did using gpg --gen-key to create a key (I used the default options) in the command line, and I also go this to work in an "automated" way without user interaction.
But, I'd like to specify the passphrase stored in a separate file. This is what I'm running now:
gpg --batch --gen-key "D:\Staging\FileContainingKeySettings.txt"
And what's in the file called "FileContainingKeySettings.txt" is this:
%echo Generating a default key
Key-Type: default
Subkey-Type: default
Name-Real: PBJ
Name-Comment: test
Name-Email: PBJ#pbj.com
Expire-Date: 0
Passphrase:blah
%commit
%echo done
So that works and generates the key.
Then I call this:
gpg --batch --yes -r PBJ --output D:\Staging\newEncryptedFile.txt --encrypt
D:\Staging\textfiletobeencrypted.csv
And this works too.
So now I have 1 problem and 1 question. The problem is that I need to specify the passphrase to reference a file that looks like this (but much longer):
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: BCPG v1.47
[encoding]
-----END PGP PUBLIC KEY BLOCK-----
instead of using the password where I wrote "blah"^, and I haven't found a way to do this online or from any other team members around me. How can I do that?
Question: I also notice now that the file I was sent containing the encryption key reads at the top
" -----BEGIN PGP PUBLIC KEY BLOCK-----"
...which says it uses PGP (but I am using gpg). I know that gpg and pgp are different and read about them, but can I even do what I'm trying to do using gpg to encrypt a file with a key that was apparently generated with pgp?
(in Windows)
UPDATE: Now I may have gotten this working, although I still have to validate by having the other person decrypt the message (it works fine when I decrypt it). Silly I didn't think of this code change earlier when I've actually tried this before...I simply changed my cmd call from this part:
gpg --batch --gen-key "D:\Staging\FileContainingKeySettings.txt"
to this instead, specifying the passphrase file location here instead:
gpg --batch --passphrase "D:\Staging\FileWithActualKey.txt --gen-key
"D:\Staging\FileContainingKeySettings.txt"
and I took the password line out of the file "FileContainingKeySettings" altogether. I haven't tested this by having the other person decrypt it yet on their end...I hope it worked. At least I didn't get error. But now when I run this line again to actually do encryption, it works:
gpg --batch --yes -r PBJ --output D:\Staging\newEncryptedFile.txt --encrypt
D:\Staging\textfiletobeencrypted.csv

Message Authentication Code for gpg

The gpg software supports symmetric encryption out of the box. That means, it works with a password. But apart from protecting the content it is also important to ensure the Authentication of a message. The idea is to create a hashsum of the file itself together with the password used for encryption. According to [1] a popular “Message Authentication Code” is HMAC. After entering:
gpg --hmac --armor --symmetric --passphrase pwd1 file.txt
gpg: Invalid option "--hmac"
an error message occurs that the switch is not known by the software. How can i use the MAC authentication the right way?
You can't. The reason for the error message is that type of signature is not available with GPG. You'd be better off simply signing and encrypting the file with the standard GPG method. Even if you wanted to use symmetric encryption only, then the recipient would still need to use GPG to decrypt the file. The correct command would be:
gpg -o filename.txt.asc -sear $recipient_key filename.txt
This assumes you also always encrypt to your own key, otherwise the command would be:
gpg -o filename.txt.asc -sear $recipient_key -r $your_key filename.txt
If they don't have a key, you could still sign and encrypt to your own key only and then extract the session key so you could provide that for them to decrypt the file with it:
gpg -o filename.txt --show-session-key -d filename.txt.asc
Then the recipient would be able to decrypt with:
gpg -o filename.txt --override-session-key $session_key -d filename.txt.asc
If you really must use symmetric encryption only, however, you can do it in two setps.
First sign the file:
gpg -o filename.txt.asc -sa filename.txt
Then symmetrically encrypt that file:
gpg -o newfilename.asc -a -c filename.txt.asc
The recipient would then need to run the decryption command twice; first on the symmetrically encrypted file and then a second time on the file it decrypts.
The normal --verify option is only used for checking clearsigned files or files with detached signatures.

How to use Gnupg's passphrase-fd argument?

I would like to use GnuPG´s decrypt command without any user interation. The script's --passphrase-fd argument seems exactly what I need. But I don't know how it works - haven't found examples.
Could anyone give me an example of such a command, on both Windows and UNIX environments?
(FYI, I'm using GnuPG 2).
Thanks already :)
In order to use the gpg option --passphrase-fd in GnuPG v2, you must specify the --batch parameter. I will first explain how --passphrase-fd works, and then get to the examples.
--passphrase-fd tells GnuPG which file descriptor (-fd) to expect the passphrase to come from. The standard file descriptors are STDIN (0), STDOUT (1) and STDERR (2). For the context of this question, you would normally only be concerned about STDIN (0).
You didn't specify where you want the passphrase to come from, so I will demonstrate the usage of STDIN (standard in) in a variety of ways.
--passphrase-fd 0 tells GnuPG to retrieve the passphrase from input into the current shell; so for example if you want GnuPG to get the passphrase data in the very next line of console input, the command and output would be like so:
gpg2 --batch --passphrase-fd 0 --armor --decrypt /path/to/encrypted_file.pgp
<next line of input is passphrase followed by hitting enter>
gpg: encrypted with 1024-bit RSA key, ID EC18C175, created 2013-10-26
"testkey4321 (4321) <test#4321.com>"
this is a test... this is only a test...
In the above example, the passphrase was provided via file descriptor 0 (STDIN) - which we provided by entering it on the shells current standard input.
In the next example, we will tell GnuPG to retrieve the passphrase from input into the current shell that is actually the output of another command (echo, in this case, which merely "echos" what you tell it to):
echo "mypassphrase" | gpg2 --batch --passphrase-fd 0 --armor --decrypt /path/to/encrypted_file.pgp
gpg: encrypted with 1024-bit RSA key, ID EC18C175, created 2013-10-26
"testkey4321 (4321) <test#4321.com>"
this is a test... this is only a test...
Another example that dumps the contents of a file that contains the passphrase to STDIN -
cat /path/to/file_with_passphrase | gpg2 --batch --passphrase-fd 0 --armor --decrypt /path/to/encrypted_file.pgp
gpg: encrypted with 1024-bit RSA key, ID EC18C175, created 2013-10-26
"testkey4321 (4321) <test#4321.com>"
this is a test... this is only a test...
In summary, --passphrase-fd just tells GnuPG that you want to feed it the requisite passphrase via a standard file descriptor; the difference between GnuPG v2 and GnuPG is merely the --batch parameter.
The above examples should work the same in Windows and *nix environments, with the only difference being that in Windows - depending on your configuration and version - you will have to replace cat with type in order to dump the contents of a file to STDIN.
kylehuff's answer still wouldn't work for me, with gpupg still popping up a password prompt.
According to https://wiki.archlinux.org/index.php/GnuPG#Unattended_passphrase with gnupg version 2.1.0 and higher, you need to do additional steps to support --passphrase-fd
First, edit the gpg-agent configuration to allow loopback pinentry mode:
~/.gnupg/gpg-agent.conf
allow-loopback-pinentry
Restart the gpg-agent process if it is running to let the change take effect.
Second, either the application needs to be updated to include a commandline parameter to use loopback mode like so:
$ gpg --pinentry-mode loopback ...
Using GPG4win/gpg 2.2.3: to use the passphrase-fd 0 and bypass the prompt, I can confirm that the following works:
--pinentry-mode loopback
As I've had to recently figure this out myself I thought it might be worth chiming in.
The answer by kylehuff is very good if you're decryping files, however, if you've need of input/output redirection, such as piping, here's an example of using a non-0 file descriptor to pass the passphrase.
#!/usr/bin/env bash
# Set some variables for easy modding
Var_fd='9'
Var_pass_location="/path/to/passphrase.file"
Var_gpg_decrypt_opts="--passphrase-fd ${Var_fd} --decrypt"
Var_output_location="out.txt"
Arr_string=( "$#" )
# Open file descriptor and shove the passphrase file into it
exec ${Var_fd}<${Var_pass_location}
# Pipe input array though gpg and append to output file
cat <<<"${Arr_string[*]}" | $(which gpg) ${Var_gpg_decrypt_opts} >> ${Var_output_location}
# Do not forget to close the file descriptor
exec ${Var_fd}>&-
Do be warned, outside of special use cases, that saving your private keys passphrase is generally seen as a bad idea or bad security practice. -Also please don't forget to close the descriptor when finished so that your passphrase isn't accessible via that method anymore.- Often I've seen advised in these use cases to use specifically non-passphrase protected keys but that's totally your choose. If you like the above code then you may want to also checkout the script I debugged for key generation either unattended or attended because it covers even less commonly used gpg file descriptor options.
Edits/updates
So I've been debugging the bulk decryption operations and have evidence to show that file descriptors seem to close automatically or perhaps it's auto closed by GnuPG. Check build 152 all the way at the bottom of the raw logs, just before diff checks, you'll find that the first block of encrypted data ate the passphrase leaving the next two blocks of data without a valid passphrase. The related scripts in this operation are ; first the script_decrypt.sh build script sets the test key's passphrase to file descriptor 9 as shown in above examples, then the Helper script is called such that it'll make use of that file descriptor... it's a funky use case but the moral of the story seems to be that what ever bulk decryption operations you plan to implement with GnuPG file descriptors will likely need to follow the steps outlined above as a whole function to properly have the file descriptors reopened. I'll be rewriting the helper script over the next few pushes so check the Travis-CI build logs greater than 152 to find if I've a solution to where file descriptors get closed...
... so that only took two tries to get things working, see the difference in build 154 both the encrypted file and raw input log match. As hypothesised the file descriptors get dumped after first usage by either GnuPG or a sub shell, thus the passphrase needs to be assigned before every decrypt command for bulk decryption to happen.
Hope this was valuable to y'all.

GNUPG - stdin encrypted file and passphrase on windows

How can I pipe the passphrase and encrypted file in gpg.exe?
I have tried a few different combinations but I can't get it to work.
Here is my attempt,
C:\>gpg.exe --output [OUTPUT_FILE] --batch --passphrase-fd 0 --decrypt < [INPUT_FILE] < [PASSPHRASE_FILE]
from this I get error
gpg: decrypt_message failed: eof
Swapping the input and passphrase around I get the error
gpg: no valid OpenPGP data found.
gpg: decrypt_message failed: eof
If I don't try and send the encrypted file via stdin then the decryption works fine.
Ultimately what I want to do is decrypt a file based on a file mask with the date timestamp in the file name. Outlined below,
C:\>gpg.exe --output yyyyMMdd.csv --batch --passphrase-fd 0 --decrypt < *_yyyyMMdd_*.txt < [PASSPHRASE_FILE]
I'm not sure if this will work because I can't get the basic scenario working, I would also appreciate if someone could advise me of the best way to achieve this.
Don't use redirection for the file you want to decrypt, only for the file containing your passphrase:
C:\>gpg.exe --output [OUTPUT_FILE] --batch --passphrase-fd 0 --decrypt [INPUT_FILE] < [PASSPHRASE_FILE]
But honestly, it might be easier for you to remove the passphrase from your key. After all, storing your passphrase in a file is not very secure. Just change your passphrase to a blank and protect your gpg home directory to deny access to any process other than your own.

Resources