-
-
Save mhofman/f8e1fdd5dce49abacf5fd776fb3727ce to your computer and use it in GitHub Desktop.
#!/bin/sh | |
# Usage: get_lutron_cert.sh [bridge_ip] | tee cert.pem | |
function error() { | |
echo "Error: $1" >&2 | |
exit 1 | |
} | |
login_server="device-login.lutron.com" | |
app_client_id="e001a4471eb6152b7b3f35e549905fd8589dfcf57eb680b6fb37f20878c28e5a" | |
app_client_secret="b07fee362538d6df3b129dc3026a72d27e1005a3d1e5839eed5ed18c63a89b27" | |
app_oauth_redirect_page="lutron_app_oauth_redirect" | |
cert_subject="/C=US/ST=Pennsylvania/L=Coopersburg/O=Lutron Electronics Co., Inc./CN=Lutron Caseta App" | |
base_url="https://${login_server}/" | |
redirect_uri_param="https%3A%2F%2F${login_server}%2F${app_oauth_redirect_page}" | |
authorize_url="${base_url}oauth/authorize?client_id=${app_client_id}&redirect_uri=${redirect_uri_param}&response_type=code" | |
openssl version >/dev/null || error "openssl required" | |
jq --version >/dev/null || error "jq required" | |
echo "Open Browser and login at ${authorize_url}" >&2 | |
echo "Enter the URL (of the \"error\" page you got redirected to (or the code in the URL): " >&2 | |
read -r redirected_url | |
oauth_code=`echo ${redirected_url} | sed -e's/^\(.*\?code=\)\{0,1\}\([0-9a-f]*\)\s*$/\2/;t;d'` | |
[ -n "$oauth_code" ] || error "Invalid code" | |
private_key="`openssl genrsa 2048 2>/dev/null`" | |
escaped_csr="`echo \"$private_key\" | openssl req -new -key /proc/self/fd/0 -subj \"${cert_subject}\" | awk 'NF {sub(/\r/, \"\"); printf \"%s\\\\n\",$0;}'`" | |
[ -n "$escaped_csr" ] || error "Couldn't generate CSR" | |
token="`curl -s -X POST -d \"code=${oauth_code}&client_id=${app_client_id}&client_secret=${app_client_secret}&redirect_uri=${redirect_uri_param}&grant_type=authorization_code\" ${base_url}oauth/token`" | |
[ "bearer" == "`echo \"$token\" | jq -r '.token_type'`" ] || error "Received invalid token $token. Try generating a new code (one time use)." | |
access_token="`echo \"$token\" | jq -r '.access_token'`" | |
pairing_request_content="{\"remote_signs_app_certificate_signing_request\":\"${escaped_csr}\"}" | |
pairing_response="`echo \"$pairing_request_content\" | curl -s -X POST -H \"X-DeviceType: Caseta,RA2Select\" -H \"Content-Type: application/json\" -H \"Authorization: Bearer ${access_token}\" -d \"@-\" ${base_url}api/v1/remotepairing/application/user`" | |
#echo "$pairing_response" | |
app_cert="`echo \"$pairing_response\" | jq -r '.remote_signs_app_certificate'`" | |
remote_cert="`echo \"$pairing_response\" | jq -r '.local_signs_remote_certificate'`" | |
echo "$app_cert" | openssl x509 -noout || error "Received invalid app cert in pairing response $pairing_response" | |
echo "$remote_cert" | openssl x509 -noout || error "Received invalid remote cert in pairing response $pairing_response" | |
echo -e "$private_key\n$app_cert\n$remote_cert" | |
server_addr=$1 | |
[ -n "$server_addr" ] || exit 0 | |
echo "$app_cert" | { echo "$private_key" | { echo "$remote_cert" | { | |
leap_response=`(echo '{"CommuniqueType":"ReadRequest","Header":{"Url":"/server/1/status/ping"}}'; sleep 3) 3<&- 4<&- 5<&- | openssl s_client -connect "$server_addr:8081" -cert /proc/self/fd/3 -key /proc/self/fd/4 -CAfile /proc/self/fd/5 -quiet -no_ign_eof 2>/dev/null` | |
[ "$?" -eq "0" ] || error "Could not connect to bridge" | |
echo "Successfully connected to bridge, running LEAP Server version `echo \"$leap_response\" | jq -r '.Body.PingResponse.LEAPVersion'`" >&2 | |
} 5<&0; } 4<&0; } 3<&0 |
tried to minimize dependencies and I thought it only required openssl
, curl
, sed
, awk
and jq
. Tried to make it as Posix compliant as possible and tested both in Bash and BusyBox's sh, but I don't have a Mac so I couldn't test it there.
The problem is most likely in the use of input redirection to pass around data, and the fact there's no standard way to access a process file descriptors (/proc/self on some systems, /dev on others). I wanted to avoid creating temporary files (which requires cleanup). I didn't want to use Bash's process substitution as most embedded systems don't have bash.
I'm open to any improvements that don't require Bash, correctly handle temporary files and work on embedded systems running Busybox (try your router for example). I'm also open to getting rid of jq
as it's the least standard package used here. It could probably be done with some creative regular expressions.
I tried this and pasted in my code, but it gave me an error:
sed: 2: "s/^\(.*\?code=\)\{0,1\} ...": undefined label ';d'
Error: Invalid code
My code is (couple of # in middle for privacy if it matters, I'm not sure)
4cfea4fdca36a489fc7095ab99b96bd7079671#####27a0dd96e384a9df8f6d
Edit: Nriley's code worked.
I'll second i was getting invalid code, but @nriley's mods worked
@nriley Were you able to ever determine with you were getting an empty pairing_response?
FYI, it looks like the error page (line 24-27) no longer works -- Lutron redirects you to their login page now. Stinks because it looks like Lutron refreshed their certs so the ones I pulled in November no longer work!
Confirmed, also getting invalid config with the certs generated.
I also cannot get the certificate.
I modified the script to just run curl in verbose mode, and that's what I'm getting (replaced long keys with (...) ):
Open Browser and login at https://device-login.lutron.com/oauth/authorize?client_id=e0(...)5a&redirect_uri=https%3A%2F%2Fdevice-login.lutron.com%2Flutron_app_oauth_redirect&response_type=code
Enter the URL (of the "error" page you got redirected to (or the code in the URL):
67(...)8a4
Request: '{"remote_signs_app_certificate_signing_request":"-----BEGIN CERTIFICATE REQUEST-----\nMIICwjCCAaoCAQAwfTELMAkGA1UEBhMCVVMxFTATBgNVBAgMDFBlbm5zeWx2YW5p\n(...)\nXgTCob8JM8NCuzKWkeIFpnLWXVoIIv2qEk2MVrFUg8dX8+B/cMU=\n-----END CERTIFICATE REQUEST-----\n"}'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 54.243.113.58...
* TCP_NODELAY set
* Connected to device-login.lutron.com (54.243.113.58) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
* subject: C=US; ST=Pennsylvania; L=Coopersburg; O=Lutron Electronics Co., Inc.; OU=IT; CN=device-login.lutron.com
* start date: Feb 13 00:00:00 2018 GMT
* expire date: Feb 12 12:00:00 2021 GMT
* subjectAltName: host "device-login.lutron.com" matched cert's "device-login.lutron.com"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=Thawte RSA CA 2018
* SSL certificate verify ok.
> POST /api/v1/remotepairing/application/user HTTP/1.1
> Host: device-login.lutron.com
> User-Agent: curl/7.59.0
> Accept: */*
> X-DeviceType: Caseta,RA2Select
> Content-Type: application/json
> Authorization: Bearer 3859(...)df6c
> Content-Length: 1103
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 400 Bad Request
< Server: Cowboy
< Date: Wed, 09 May 2018 01:14:12 GMT
< Connection: keep-alive
< Content-Type: text/html; charset=utf-8
< X-Request-Id: ec2fb587-d1ab-4aa9-ac13-4b82c5d10e33
< X-Runtime: 0.004386
< Content-Length: 0
< Via: 1.1 vegur
<
* Connection #0 to host device-login.lutron.com left intact
This script has quite a few OS dependencies. I fixed some of them at https://gist.github.com/nriley/e0b7ddeb3f3dfd48bb77bfc91ff6d96f but it's still failing on macOS getting
{}
back as apairing_response
and I don't have further time to troubleshoot.Thanks for working on this.