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

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.

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).

OpenSSL::PKCS7::PKCS7Error: decrypt error

smime = OpenSSL::PKCS7.read_smime(File.read('encrypted_smime.p7m'))
p_key = OpenSSL::PKey::RSA.new(File.read('pr_key.pem'))
cert = OpenSSL::X509::Certificate.new(File.read('cert.pem'))
smime.decrypt(p_key, cert)
In the decrypt line above, I am getting the error OpenSSL::PKCS7::PKCS7Error: decrypt error.
I am not sure why this error is causing, as I am able to decrypt the smime using openssl command line tool properly. Also I have verified that the cert I am using is signed using the same private key that I am using in p_key.
Following is the command which is getting me the expected output -
openssl smime -decrypt -in encrypted_smime.p7m -inkey pr_key.pem -out decrypt_smime.pem
What is the problem, and how do I fix it?

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

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"

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 respond to prompts in bash

I need to generate a lot of private keys and self signed certs so I'd like to make a bash script to do it for me. The problem I've run into is that when you self sign a cert it asks the same questions as if you were generating a signing request.
Namely:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
I've tried several things that I've seen around the site but none of them seem to work. This is probably because I'm very new to bash.
My script currently looks like this:
#!/bin/bash
openssl genrsa -out privkey.pem 2048
openssl req -new -key privkey.pem -out cert.csr
I've tried using a pipe where echo the things I'd like to enter but it doesn't work.
Any suggestions?
See the examples provided at:
http://www.openssl.org/docs/apps/req.html#EXAMPLES
Or do everything in the command line:
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509\
-subj "/C=US/ST=Denial /L=Springfield/O=Dis/CN=www.example.com" \
-keyout www.example.com.key -out www.example.com.cert

Resources