I'm currently trying to sign an e-mail with openssl cms with the following command:
openssl cms -sign -in unsigned_message -out signed_message -certfile ca_certs.pem -signer client_cert.pem -from xxx#yyy.com -to yyy#zzz.com -subject sample_subject
If the unsigned_message is built up like that (no multipart mail), everything works fine and the signature is correct:
Content-Type: text/html
<div>test</div>
But if the e-mail contains a multipart message like that:
Content-Type: multipart/mixed;boundary="sample_boundary"
--sample_boundary
Content-Type: text/html
<div>test</div>
--sample_boundary
Content-type: text/plain; charset=us-ascii
test
--sample_boundary--
The signature of the e-mail is wrong. But I don't know why?
Related
I would like to know how to sign it with an email digital certificate and then use CURL to send this signed email message.
Below is an example of a simple TEXT/PLAIN email message that I would like to sign.
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit
MIME-Version: 1.0 (Created with SublimeText 3)
Date: Fri, 01 Jan 2021 23:59:59 -0700
Message-ID: <aade2ef2-960f-4e0d-94e4-d100f5270d28#initech.com>
Subject: TPS Report #1001
From: "Michael # Initech" <michael#initech.com>
To: "Peter # Initech" <peter#initech.com>
User-Agent: CURL/7.75.0
Good morning.
This is a message in plain text format.
It contains no inline images or attachments.
Each line is no more than 76 characters in length.
Please reach out if you have any questions.
Thank you,
Michael Bolton
SENIOR DEVELOPER
michael#initech.com
P | 801.555.1234
I place the entire message in a file called singlepart--text-plain.eml and then I use CURL to send it.
curl --verbose -ssl smtps://secure.email.com:465 --login-options AUTH=PLAIN --user michael#initech.com:Letmein --mail-from michael#initech.com --mail-rcpt peter#initech.com --mail-rcpt-allowfails --upload-file singlepart--text-plain.eml
Bam. That is all it takes.
Now the following link describes how to sign mime messages with OPENSSL.
https://www.misterpki.com/openssl-smime/
openssl smime -sign -in singlepart--text-plain.eml -text -out signed-singlepart--text-plain.eml -signer michael-digital-signature.pem
RFC 8551 Section 3.5, 3.5.1 & 3.5.2 shows limited examples on how to structure the message and my question is how to correctly do this.
https://www.rfc-editor.org/rfc/rfc8551#section-3.5
What I don't know is, what am I signing?
The entire content?
Just the literal body of the message?
Is this supposed to be a multipart message containing the body of text unchanged and a digital signature as a base64 attachment?
Or is it supposed to contain modified/signed text within a single part just like text/plain?
As you know, RFC documents are not for the faint of heart.
Only the message body is signed. That is everything after the blank line that comes after the headers.
Headers cannot be included because they can be changed (existing ones modified, or new ones added) by mail servers and mail clients on their way to the recipient.
However, there are approaches to include some important headers (like To and Subject) by copying them to the body, but they're not widely supported (yet?).
openssl smime knows how to handle the S/MIME message correctly, so its input is the complete message (singlepart--text-plain.eml in your example).
Here's a load of useful openssl smime examples which I've copied from the page http://openssl.cs.utah.edu/docs/apps/smime.html which, much to my regret, seems to be gone:
Create a cleartext signed message:
openssl smime -sign -in message.txt -text -out mail.msg \
-signer mycert.pem
Create an opaque signed message:
openssl smime -sign -in message.txt -text -out mail.msg -nodetach \
-signer mycert.pem
Create a signed message, include some additional certificates and read the private key from another file:
openssl smime -sign -in in.txt -text -out mail.msg \
-signer mycert.pem -inkey mykey.pem -certfile mycerts.pem
Create a signed message with two signers:
openssl smime -sign -in message.txt -text -out mail.msg \
-signer mycert.pem -signer othercert.pem
Send a signed message under Unix directly to sendmail, including headers:
openssl smime -sign -in in.txt -text -signer mycert.pem \
-from steve#openssl.org -to someone#somewhere \
-subject "Signed message" | sendmail someone#somewhere
Verify a message and extract the signer's certificate if successful:
openssl smime -verify -in mail.msg -signer user.pem -out signedtext.txt
Send encrypted mail using triple DES:
openssl smime -encrypt -in in.txt -from steve#openssl.org \
-to someone#somewhere -subject "Encrypted message" \
-des3 user.pem -out mail.msg
Sign and encrypt mail:
openssl smime -sign -in ml.txt -signer my.pem -text \
| openssl smime -encrypt -out mail.msg \
-from steve#openssl.org -to someone#somewhere \
-subject "Signed and Encrypted message" -des3 user.pem
Note: the encryption command does not include the -text option because the message being encrypted already has MIME headers.
Decrypt mail:
openssl smime -decrypt -in mail.msg -recip mycert.pem -inkey key.pem
The output from Netscape form signing is a PKCS#7 structure with the detached signature format. You can use this program to verify the signature by line wrapping the base64 encoded structure and surrounding it with:
-----BEGIN PKCS7-----
-----END PKCS7-----
and using the command:
openssl smime -verify -inform PEM -in signature.pem -content content.txt
Alternatively you can base64 decode the signature and use:
openssl smime -verify -inform DER -in signature.der -content content.txt
Create an encrypted message using 128 bit Camellia:
openssl smime -encrypt -in plain.txt -camellia128 -out mail.msg cert.pem
Add a signer to an existing message:
openssl smime -resign -in mail.msg -signer newsign.pem -out mail2.msg
This is a recap starting from the main purpose of the question.
RFC-2311 - Section 3.4.3.3 Sample multipart/signed Message - I simply could not get this implementation to work with openssl. For some reason, it corrupts the message.
Content-Type: multipart/signed;
protocol="application/pkcs7-signature";
micalg=sha1; boundary=boundary42
--boundary42
Content-Type: text/plain
This is a clear-signed message.
--boundary42
Content-Type: application/pkcs7-signature; name=smime.p7s
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7s
ghyHhHUujhJhjH77n8HHGTrfvbnj756tbB9HG4VQpfyF467GhIGfHfYT6
4VQpfyF467GhIGfHfYT6jH77n8HHGghyHhHUujhJh756tbB9HGTrfvbnj
n8HHGTrfvhJhjH776tbB9HG4VQbnj7567GhIGfHfYT6ghyHhHUujpfyF4
7GhIGfHfYT64VQbnj756
--boundary42--
RFC-2311 - Section 3.4.2 Signing Using application/pkcs7-mime and SignedData - This implementation is veritably working in Outlook365 that I have tested.
I believe the reason signed-data works best is, if the entire contents are encoded to base64, it's less likely to be messed with during the email transfer process.
It takes your plain text message and embeds your digital signature into it creating an smime.p7m attachment that contains both. This is the file that contains all of it in base64 format.
S/MIME email clients will automatically verify, decode and display to you as plain text again. It works great!
Content-Type: application/pkcs7-mime; smime-type=signed-data;
name=smime.p7m
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=smime.p7m
567GhIGfHfYT6ghyHhHUujpfyF4f8HHGTrfvhJhjH776tbB9HG4VQbnj7
77n8HHGT9HG4VQpfyF467GhIGfHfYT6rfvbnj756tbBghyHhHUujhJhjH
HUujhJh4VQpfyF467GhIGfHfYGTrfvbnjT6jH7756tbB9H7n8HHGghyHh
6YT64V0GhIGfHfQbnj75
Step 1. Generate a Certificate Signing Request and Private Key for the email address you want with openssl. Copy Private Key to openssl\bin directory.
Step 2. Use Certificate Signing Request to obtain a DigiCert S/MIME Class 1 Certificate (DV) from https://www.sslstore.com
Step 3. Download certificate files to disk and look for My_CA_Bundle.ca-bundle file. Copy this file to openssl\bin directory.
Optional Step. Create PKCS #12 from the 3 files My_Private.key My_Certificate.crt My_CA_Bundle.ca-bundle if you feel like importing this certificate into Windows Certificate Manager.
openssl pkcs12 -export -out My_Digital_Signature_Bundle.p12 -inkey My_Private.key -in My_Certificate.crt -certfile My_CA_Bundle.ca-bundle
Signing the plain text message
Step 1. Create a text file called text-message.eml, add the string This is an opaque-signed message. and copy it to openssl\bin directory. This file is the literal plain text message you want to sign. No headers. Just your message.
Now we are going to use the 3 files My_Private.key My_Certificate.crt My_CA_Bundle.ca-bundle to sign a plain text message which will then be output to signed-message.eml file.
openssl smime -sign -in text-message.eml -inkey My_Private.key -signer My_Certificate.crt -certfile My_CA_Bundle.ca-bundle -nodetach -text -out signed-message.eml -to "<admin#example.com>" -from "<client#example.com>" -subject "A Digitally Signed Message"
In the above openssl command, you see the -nodetach which creates a .p7m base64 fusion of the message and the signature into one blob.
signed-message.eml
What used to be just This is an opaque-signed message. has now transformed into:
To: <client#example.com>
From: <admin#example.com>
Subject: A Digitally Signed Message
MIME-Version: 1.0
Content-Disposition: attachment; filename="smime.p7m"
Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"
Content-Transfer-Encoding: base64
MIISuwYJKoZIhvcNAQcCoIISrDCCEqgCAQExDzANBglghkgBZQMEAgEFADBKBgkq
hkiG9w0BBwGgPQQ7Q29udGVudC1UeXBlOiB0ZXh0L3BsYWluDQoNClRoaXMgaXMg
YSBjbGVhci1zaWduZWQgbWVzc2FnZS6ggg+1MIIGTjCCBTagAwIBAgIQBK55YGZm
kBq5xX+mbFvczTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UE
ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD
VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTMxMTA1MTIwMDAw
WhcNMjg...
From this point forward, the plain text message is embedded in the base64 content together with public certificates. Even the slightest alteration to this base64 message will invalidate the entire message and fail verification. That means nothing; no extra words, no spaces, no dots. At all. Period.
Verify Digitally Signed Message with openssl
The final step is to verify the recently digitally signed message.
openssl smime -verify -in signed-message.eml -CAfile My_CA_Bundle.ca-bundle -binary -signer signer-certificate-extracted.crt -out signed-content-extracted.txt
openssl only needs the public My_CA_Bundle.ca-bundle file to do this verification. If successful, openssl will output the signer-certificate-extracted.crt and lastly the signed-content-extracted.txt that was digitally signed; the message intact, if you would.
The screenshot below illustrates a successful digital signature verification.
Send digitally signed email with CURL
curl --verbose -ssl smtps://secure.email.com:465 --login-options AUTH=PLAIN --user admin#example.com:Letmein --mail-from admin#example.com --mail-rcpt client#example.com --mail-rcpt-allowfails --upload-file signed-message.eml
I am using below command for HTML page -
mailx -m -r $from -s $subject Content-type: text/html $to <$message
I want to send attachment with it.
I'm trying to upload a jpeg image to me/staging_resources by doing something similar to what curl does:
curl -X POST \
https://graph.facebook.com/me/staging_resources \
-F "file=#images/prawn-curry-1.jpg" \
-F "access_token=$USER_ACCESS_TOKEN"
The above is from Facebook doc (https://developers.facebook.com/docs/opengraph/using-object-api/#staging).
My HTTP request looks something like this:
METHOD: POST
HEADERS:
Content-Type multipart/form-data;boundary=Random_Boundary_Chars
BODY:
--Random_Boundary_Chars
Content-Disposition: form-data; name="access_token"
USER_AUTH_TOKEN
--Random_Boundary_Chars
Content-Disposition: form-data; name="file"
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9/KKKKAP/2Q==
--Random_Boundary_Chars--
The problem is I'm always getting this response from the server:
"WWW-Authenticate: OAuth "Facebook Platform" "invalid_request" "(#100) Invalid file. Expected file of one of the following types: image/jpg, image/jpeg, image/gif, image/png" ".
In what format should I attach the image to the HTTP request? (I've tried encoding it base64 and also tried using an URL encoder).
Thanks
Found the solution. It seems the image can be sent as a binary data so no encoding is really needed. The only thing that Facebook is actually checking is the filename attribute that must have a correct extension. So this will translate into:
--Random_Boundary_Chars
Content-Disposition: form-data; name="file"; filename="randomName.jpg"
Content-Type: image/jpeg
JPEG_BINARY_DATA
--Random_Boundary_Chars--
I used this syntax to post a file along with some parameters:
curl -v -include --form "key1=value1" --form upload=localfilename URL
The file is around 500K in size. First of all, I see content length to be 254 on the transmit side. Later the server response's content length is 0.
Where am I going wrong?
Here is the complete trace of the command.
* Couldn't find host xxx.xxx.xxx.xxx in the _netrc file; using defaults
* About to connect() to xxx.xxx.xxx.xxx port yyyy (#0)
* Trying xxx.xxx.xxx.xxx...
* Adding handle: conn: 0x4b96a0
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x4b96a0) send_pipe: 1, recv_pipe: 0
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port yyyy (#0)
* POST /zzzzzz/UploadFile HTTP/1.1
* User-Agent: curl/7.32.0
* Host: xxx.xxx.xxx.xxx:yyyy
* Accept: */*
* Content-Length: 254
* Expect: 100-continue
* Content-Type: multipart/form-data; boundary=------------------------948a6137eef50079
*
* HTTP/1.1 100 Continue
* HTTP/1.1 100 Continue
* HTTP/1.1 200 OK
* HTTP/1.1 200 OK
* Server Apache-Coyote/1.1 is not blacklisted
* Server: Apache-Coyote/1.1
* Server: Apache-Coyote/1.1
* Added cookie JSESSIONID="C1D7DD042E250211D9DEA82688876F88" for domain xxx.xxx.xxx.xxx, path /zzzzz/, expire 0
* Set-Cookie: JSESSIONID=C1D7DD042E250211D9DEA82688876F88; Path=/zzzzzz/;
* HttpOnly
* Set-Cookie: JSESSIONID=C1D7DD042E250211D9DEA82688876F88; Path=/zzzzzz/; HttpOnly
* Content-Type: text/html;charset=ISO-8859-1
Content-Type: text/html;charset=ISO-8859-1
* Content-Length: 0
* Content-Length: 0
* Date: Tue, 01 Oct 2013 11:54:24 GMT
* Date: Tue, 01 Oct 2013 11:54:24 GMT
* Connection #0 to host xxx.xxx.xxx.xxx left intact
The following syntax fixes it for you:
curl -v -F key1=value1 -F upload=#localfilename URL
to upload a file using curl in Windows I found that the path requires escaped double quotes
e.g.
curl -v -F 'upload=#\"C:/myfile.txt\"' URL
This is what worked for me
curl --form file='#filename' URL
It seems when I gave this answer (4+ years ago), I didn't really understand the question, or how form fields worked. I was just answering based on what I had tried in a difference scenario, and it worked for me.
So firstly, the only mistake the OP made was in not using the # symbol before the file name. Secondly, my answer which uses file=... only worked for me because the form field I was trying to do the upload for was called file. If your form field is called something else, use that name instead.
Explanation
From the curl manpages; under the description for the option --form it says:
This enables uploading of binary files etc. To force the 'content'
part to be a file, prefix the file name with an # sign. To just get
the content part from a file, prefix the file name with the symbol <. The difference between # and < is then that # makes a
file get
attached in the post as a file upload, while the < makes a text field and just get the contents for that text field from a
file.
Chances are that if you are trying to do a form upload, you will most likely want to use the # prefix to upload the file rather than < which uploads the contents of the file.
Addendum
Now I must also add that one must be careful with using the < symbol because in most unix shells, < is the input redirection symbol [which coincidentally will also supply the contents of the given file to the command standard input of the program before <]. This means that if you do not properly escape that symbol or wrap it in quotes, you may find that your curl command does not behave the way you expect.
On that same note, I will also recommend quoting the # symbol.
You may also be interested in this other question titled: application/x-www-form-urlencoded or multipart/form-data?
I say this because curl offers other ways of uploading a file, but they differ in the content-type set in the header. For example the --data option offers a similar mechanism for uploading files as data, but uses a different content-type for the upload.
Anyways that's all I wanted to say about this answer since it started to get more upvotes. I hope this helps erase any confusions such as the difference between this answer and the accepted answer. There is really none, except for this explanation.
I had a hard time sending a multipart HTTP PUT request with curl to a Java backend. I simply tried
curl -X PUT URL \
--header 'Content-Type: multipart/form-data; boundary=---------BOUNDARY' \
--data-binary #file
and the content of the file was
-----------BOUNDARY
Content-Disposition: form-data; name="name1"
Content-Type: application/xml;version=1.0;charset=UTF-8
<xml>content</xml>
-----------BOUNDARY
Content-Disposition: form-data; name="name2"
Content-Type: text/plain
content
-----------BOUNDARY--
but I always got an error that the boundary was incorrect. After some Java backend debugging I found out that the Java implementation was adding a \r\n-- as a prefix to the boundary, so after changing my input file to
<-- here's the CRLF
-------------BOUNDARY <-- added '--' at the beginning
...
-------------BOUNDARY <-- added '--' at the beginning
...
-------------BOUNDARY-- <-- added '--' at the beginning
everything works fine!
tl;dr
Add a newline (CRLF \r\n) at the beginning of the multipart boundary content and -- at the beginning of the boundaries and try again.
Maybe you are sending a request to a Java backend that needs this changes in the boundary.
On Windows 10, curl 7.28.1 within powershell, I found the following to work for me:
$filePath = "c:\temp\dir with spaces\myfile.wav"
$curlPath = ("myfilename=#" + $filePath)
curl -v -F $curlPath URL
With Smartbear Zephyr Scale, server version, you would attach a file to a Test Cycle this way :
curl -H "Authorization: Basic YkskfdygyzghhMg==" -X POST -H "Content-Type: multipart/form-data" https://jira/jira/rest/atm/1.0/testrun/TDLT-C32/attachments --form file="#file2.txt"
(returns {"id":7099})
where in file2.txt : Test Cycle has also a link to a test plan, and a link to an issue.
{
"projectKey": "TDD",
"testPlanKey": "TDD-P1",
"name": "Bonjour chez vous, Le Prisonnier",
"issueKey": "TDLT-999"
}
I'm posting this since unable to find that out from official documentation :)
Maybe this form work
curl -X POST -d 'key1=value1&key2=value2' http://URL -H "Content-Type: application/x-www-form-urlencoded"
I need to sent a file via mailx or mail, but I wat to sent it as attachment not in the body message. Is there any way how to do it ?
Eventually is there any other tool in solaris which can be used for such as procedure ?
Thanks
You can attach files to mailx using -a like so
echo "this is the body of the email" | mailx -s"Subject" -a attachment.jpg Someone#Domain.com
so long as your in the same directory as your attachment that should work fine. If not you can just state the directory like `
samachPicsFolder/samachpic.jpg
If your mailx doesn't support the -a option and you don't have access to mutt, and you don't want to turn to uuencode as a fallback from the 1980s, as a last resort you can piece together a small MIME wrapper yourself.
#!/bin/sh
# ... do some option processing here. The rest of the code
# assumes you have subject in $subject, file to be attached
# in $file, recipients in $recipients
boundary="${RANDOM}_${RANDOM}_${RANDOM}"
(
cat <<____HERE
Subject: $subject
To: $recipients
Mime-Version: 1.0
Content-type: multipart/related; boundary="$boundary"
--$boundary
Content-type: text/plain
Content-transfer-encoding: 7bit
____HERE
# Read message body from stdin
# Maybe apply quoted-printable encoding if you anticipate
# overlong lines and/or 8-bit character codes
# - then you should change the last body part header above to
# Content-Transfer-Encoding: quoted-printable
cat
cat <<____HERE
--$boundary
Content-type: application/octet-stream; name="$file"
Content-disposition: attachment; filename="$file"
Content-transfer-encoding: base64
____HERE
# If you don't have base64 you will have to reimplement that, too /-:
base64 "$file"
cat <<____HERE
--$boundary--
____HERE
) | sendmail -oi -t
The path to sendmail is often system-dependent. Try /usr/sbin/sendmail or /usr/lib/sendmail or ... a myriad other weird places if it's not in your PATH.
This is quick and dirty; for proper MIME compliance, you should do RFC2047 encoding of the subject if necessary, etc, and see also the notes in the comments in the code. But for your average US-centric 7-bit English-language cron job, it will do just fine.
Regarding mailx, you can find some inspiration here
http://www.shelldorado.com/articles/mailattachments.html
I would recommend you to have a look at mutt
http://www.mutt.org/
Try using this command in order to send an attachment using Mailx:
uuencode source_file encoded_filename |mailx -m -s "Subject" something#something.com
I'd recommend using mutt for it, which is light-weight enough to quickly install on any system.