Write a shell script to send S/MIME signed emails via Mutt - shell

I'm making an app (tiny PHP website) that sends my AirBnB guest info to the police (which I'm required to do - it's what they do in a hotel, when they take your passport at check-in).
The app needs to generate a file with guest's data and then send it to the Czech police department via a digitally signed email. The first part is done but I've got stuck with Mutt for yet another day.
My goal is to send a S/mime signed email with an attachment with a shell script without any user interaction. So something like this:
mutt -s 'My mail ' abc#gmail.com -a report1.txt < report2.txt
The code above sends an email with an attachment but without a digital signature although enabled by default in muttrc.
I've managed to send signed emails with mutt but only via it's interactive shell plus I need to manually type in the password for my private key every time, the code above doesn't ask for the password just sends it without a signature straight away.
I tried generating the key with an empty PEM pass but then OpenSSL didn't like it at all and aborted immediately.

I would use sendmail command instead of mutt.
openssl smime -sign -in report.txt -text -signer cert.pem \
-from you#domain.com -to abc#gmail.com \
-subject "My mail" | sendmail abc#gmail.com
In this example, cert.pem is created from a PKCS12 file:
openssl pkcs12 -in yourcert.p12 -out cert.pem -clcerts -nodes
report.txt is the plain text file/report to be signed and sent.
The OpenSSL smime(1) man page has a few more good examples.

Desperate people do desperate things. I edited the openssl source code to generate Content-Type: text/plain;\r\n instead of Content-Type: text/plain\r\n\r\n so that I could follow up with:
Content-Disposition: attachment;
filename="test.txt"

Related

download cert with curl, echo cert value into openssl

I'm trying to get an intermediate certificate using curl, and process the certificate without writing it to a file.
I've figured out how to process a leaf cert and get the distribution point, but that's where I'm stuck.
For an example, let's take stackoverflow (seems pertinent). The first intermediate cert for SO is from http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt.
In an interactive shell, I can do something like this to store the value of the certificate into a variable:
DigiCertIntermediateCert=$(curl http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt)
Then I can check the value of the cert by doing something like this:
echo "$DigiCertIntermediateCert" | openssl x509 -inform DER -outform PEM
This outputs the nice and friendly PEM-formed cert. It looks like this:
-----BEGIN CERTIFICATE-----
MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs
...
cPUeybQ=
-----END CERTIFICATE-----
(Note: this will change when the intermediate cert changes, if this isn't exactly the value you're seeing, don't freak out)
Neat! Seems like this should work. Let's throw all this logic into a script with some basic case handling (like if the cert is in a different form, then we should interpret it as that form) and we should be done, right?
#! /bin/bash
cert_content=$(curl -s "$1")
echo "$cert_content"
if echo "$cert_content" | openssl x509 -text -noout; then
echo "Not converting"
echo "$cert_content"
else
echo "Converting"
echo "$cert_content" | openssl x509 -inform DER -outform PEM
fi
What does this output when we feed the url?
...
[binary contents of a certificate]
...
unable to load certificate
4667577964:error:09FFF06C:PEM routines:CRYPTO_internal:no start line:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.230.1/libressl-2.6/crypto/pem/pem_lib.c:683:Expecting: TRUSTED CERTIFICATE
Converting
unable to load certificate
4421875308:error:0DFFF08E:asn1 encoding routines:CRYPTO_internal:not enough data:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.230.1/libressl-2.6/crypto/asn1/a_d2i_fp.c:251:
So it's pretty easy to see that the certificate is in DER form (because of the "Converting" line), but it looks like the certificate is not complete - note the error:
unable to load certificate
4421875308:error:0DFFF08E:asn1 encoding routines:CRYPTO_internal:not enough data:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.230.1/libressl-2.6/crypto/asn1/a_d2i_fp.c:251:
says that there is not enough data.
Why is this different between the script and the actions I'm taking in the shell? How can I feed the binary cert through the openssl tool to get the PEM-form cert out?
As the question alludes, I was not strict enough in my shell usage. The "interactive" session was done using zsh, and the script was run under bash. zsh will append an EOF to variables passed through its pipes, while bash (apparently) does not. Now my quest becomes how to append an EOF to a variable in bash (which, apparently, is non-trivial).

Xero Private App to get invoice with cURL- failed to validate signature / error 500?

I am trying to communicate with my Xero account through Xero API, a simple bash script and cURL. Also, I am working with a Xero Private App, this means I have already generated a public/private keypair, the public key uploaded to Xero and the private being used in my procedure.
Keypair generated as suggested here https://developer.xero.com/documentation/api-guides/create-publicprivate-key:
openssl genrsa -out privatekey.pem 1024
openssl req -new -x509 -key privatekey.pem -out publickey.cer -days 1825
openssl pkcs12 -export -out public_privatekey.pfx -inkey privatekey.pem -in publickey.cer
The following is my code and thought process.
First, I create the parameters for the OAuth request. I know Xero Private Apps work with OAuth1.0a and need to be signed with RSA-SHA1.
oauth_consumer_key="my_key"
oauth_nonce="$(($(date +%s) - 10000))"
oauth_signature_method="RSA-SHA1"
oauth_timestamp="$(date +%s)"
oauth_token="my_key"
oauth_version="1.0"
Now, I focus on generating the OAuth Signature as explained clearly in https://oauth1.wp-api.org/docs/basics/Signing.html. I make sure to create a base string using Method (I call it verb), URL and Params. I make sure that Params are sorted by name. Also, I URL_encode these values before concatenating with &.
verb=GET
url=https://api.xero.com/api.xro/2.0/Invoices/243216c5-369e-4056-ac67-05388f86dc81
params=oauth_consumer_key=$oauth_consumer_key\&oauth_nonce=$oauth_nonce\&oauth_signature_method=$oauth_signature_method\&oauth_timestamp=$oauth_timestamp\&oauth_token=$oauth_token\&oauth_version=$oauth_version
baseString=$(urlencode $verb)\&$(urlencode $url)\&$(urlencode $params)
echo $baseString returns
GET&https%3A%2F%2Fapi.xero.com%2Fapi.xro%2F2.0%2FInvoices%2Fe4d08842-29fc-4228-8227-8661e0f93ea3&oauth_consumer_key%*%26oauth_nonce%3D1523125307%26oauth_signature_method%3DRSA-SHA1%26oauth_timestamp%3D1523135308%26oauth_token%*%26oauth_version%3D1.0
urlencode function:
function urlencode() {
echo -n "$1" | perl -MURI::Escape -ne 'print uri_escape($_)'
}
I sign the baseString using OpenSSL as follows.
oauth_signature=$(echo -n "$baseString" | openssl dgst -sha1 -sign "C:\path\to\keys\privatekey.pem" | openssl enc -A -base64)
echo $oauth_signature
Now, I create the Authorization header, with the same parameters but including the signature which has just been generated.
auth_header="Authorization: OAuth oauth_consumer_key=\"$oauth_consumer_key\", oauth_nonce=\"$oauth_nonce\", oauth_signature=\"$oauth_signature\", oauth_signature_method=\"$oauth_signature_method\", oauth_timestamp=\"$oauth_timestamp\", oauth_token=\"$oauth_token\", oauth_version=\"$oauth_version\""
echo $auth_header returns
Authorization: OAuth oauth_consumer_key="*", oauth_nonce="1523124975", oauth_signature="*", oauth_signature_method="RSA-SHA1", oauth_timestamp="1523134975", oauth_token="*", oauth_version="1.0"
Finally, I send a GET request via cURL to get a specific invoice.
curl -G "https://api.xero.com/api.xro/2.0/Invoices/243216c5-369e-4056-ac67-05388f86dc81" -H "$auth_header"
And I receive...
oauth_problem=signature_invalid&oauth_problem_advice=Failed%20to%20validate%20signature
Where I have expected a JSON response with the invoice or something telling me that the invoice does not exist.
I feel that I have followed the steps correctly. Well, I guess there is a problem with the signature. Either the thing it was signing wasn't formed correctly or a problem with RSA-SHA1? I'm at a loss. I would appreciate any feedback.
EDIT: Thanks to #rustyskates, I fixed some mistakes, however, now I get:
<ApiException xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ErrorNumber>500</ErrorNumber>
<Type>UnknownErrorException</Type>
<Message>An error occurred in Xero. Check the API Status page http://status.developer.xero.com for current service status. Contact the API support team at api#xero.com for more assistance.</Message>
</ApiException> which still looks like a problem because Xero doesn't report operational problems and many others seem to have experienced this too.
The last step is to url-encode the oauth_signature value directly before you add it in to the Authorization header. Once you do that, you'll be golden.

Dart HTTP Server and importing a SSL Certificate

I have create a Dart HTTP(s) server for deploying files. I can have it run the https with a self signed cert. But how do you import a .crt properly from someone for example GoDaddy properly?
So after a long struggle I have finally succeeded in importing a certificate from GoDaddy properly into Darts HttpServer bindSecure.
In order to pull this off, first you must merge your key and the certificate from GoDaddy together.
This can be done using a variation of this:
openssl pkcs12 -export -in website_cert.crt -inkey website_key.key -out website.p12 -name Name-Of-Cert -passout pass:SECRET
After...
pk12util -i website.p12 -d 'sql:./' -W SECRET
You should now see the cert with the Name-Of-Cert value, (If you have a password on the db, apply the appropriate flags)
certutil -L -d 'sql:./'
Now, verify
certutil -V -u V -d 'sql:./' -n "Name-Of-Cert"
Credit to: https://stomp.colorado.edu/blog/blog/2010/06/04/on-setting-up-mod_nss/

How to specify passphrases for P12 to PEM file conversion without interaction.

I'm trying to convert a P12 file to a PEM file. When I execute the command, the terminal asks me for three things:
P12 passphrase (I type it in, hit enter)
PEM passphrase (type it in, hit enter)
PEM passphrase confirm (type it in, hit enter)
I know I can execute a sudo command all in one shot by using the following:
echo sudopassword | sudo rm -rf /file.p12;
How can I add all three values in one shot? Thanks
Can you explain what these P12 files are? I found this link which deals with the conversion of pkcs12 Cert/key files to .PEM format using openssl. (http://gridsite.org)
Key to the answer is:
Use -passin file:... and -passout file:... for unattended processing
It's my guess that you will have to specify the -passin file:P12passphrase and -passout file PEMpassphrase options for this case.
This little test confirms how an input passphrase can be specified through a file:<...> parameter. This helps to hide such phrases from any over the shoulder attacks. Don't forget to restrict access to such files. Even though it's a common feature of most openssl commands, it's not explicitly mentioned and it is key to the original question. The full list of options is below.
$ openssl pkcs12 -passin file:P12phrase
Can't open file P12phrase
Error getting passwords
(I leave it to the OP to construct the full command.)
Below are all supported options for the pkcs12 subcommand:
$ openssl pkcs12 help
Usage: pkcs12 [options]
where options are
-export output PKCS12 file
-chain add certificate chain
-inkey file private key if not infile
-certfile f add all certs in f
-CApath arg - PEM format directory of CA's
-CAfile arg - PEM format file of CA's
-name "name" use name as friendly name
-caname "nm" use nm as CA friendly name (can be used more than once).
-in infile input filename
-out outfile output filename
-noout don't output anything, just verify.
-nomacver don't verify MAC.
-nocerts don't output certificates.
-clcerts only output client certificates.
-cacerts only output CA certificates.
-nokeys don't output private keys.
-info give info about PKCS#12 structure.
-des encrypt private keys with DES
-des3 encrypt private keys with triple DES (default)
-aes128, -aes192, -aes256
encrypt PEM output with cbc aes
-nodes don't encrypt private keys
-noiter don't use encryption iteration
-maciter use MAC iteration
-twopass separate MAC, encryption passwords
-descert encrypt PKCS#12 certificates with triple DES (default RC2-40)
-certpbe alg specify certificate PBE algorithm (default RC2-40)
-keypbe alg specify private key PBE algorithm (default 3DES)
-keyex set MS key exchange type
-keysig set MS key signature type
-password p set import/export password source
-passin p input file pass phrase source
-passout p output file pass phrase source
-engine e use engine e, possibly a hardware device.
-rand file:file:...
load the file (or the files in the directory) into
the random number generator
-CSP name Microsoft CSP name
-LMK Add local machine keyset attribute to private key
It's unlikely that these commands are reading from stdin. It's more likely that they're reading directly from the terminal. This allows them to set a mode that doesn't echo the password to the screen. Try echoing your input to /dev/tty.
Beyond that, you'll need to use something like expect / pexect to control these. Those projects were build specifically for this purpose.
Openssl has a -stdin optoin to read its input from stdin. This works:
tmp=`mktemp`
cat > $tmp <<EOF
$1
EOF
cat $tmp | openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key
I've used cat and a here-document to avoid putting the password on the commandline.
I used openssl pkcs12 -in Certificates.p12 -out sampleCore.pem -nodes and it was working for me.
Have you tried just echoing three lines? It would probably work
echo $'P12 passphrase\nPEM passphrase\nPEM passphrase confirm' | cmd
Although I feel I must point out that echoing passwords like this is highly insecure. Not only does the password end up in your bash history file, but it's also visible to anyone else on the system who runs ps.

Digitally sign email in Ruby with S/MIME

Is there a way in Ruby to digitally sign email messages with S/MIME? Our group uses PKI and our users are conditioned to expect digital signatures for important messages.
I know I can invoke the openssl command line tool:
openssl smime -sign -signer $CERT_FILE -passin pass:$CERT_PASS
-in $UNSIGNED_MAIL -out $SIGNED_MAIL -certfile $CERT_CA_FILE
-from 'your ' -to 'recipients <email#address>'
-subject 'The Subject'
but I am hoping to utilize a Ruby solution.
I ended up using the above solution, but for those in a similar situation, you have to convert the PKI key (in .p12 file format) first: openssl pkcs12 -in #{#cert_file} -passin pass:#{#pass_phrase} -passout pass:#{#pass_phrase} -out #{out_file}

Resources