copy var partial bytes in shell - shell

The following contents in my shell file :
#!/usr/bin/env bash
api_params_with_timestamp="somethingXXXX"
SECRET_KEY="XXXXXXXXXXXXXX"
signature=$(echo -n "$api_params_with_timestamp" \
| openssl dgst -sha256 -hmac "$SECRET_KEY")
echo "signature"=$signature
execute this shell I got
signature=(stdin)= 3eee8d204c83381dddffgggg
I just need "3eee8d204c83381dddffgggg" part , what can I do
so that (stdin)= part would be skip ?!
Edit:
signature=$(echo -n "$api_params_with_timestamp" \
| openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -c10-1000)
not perfect but works !

The openssl-dgst page doesn't provide a direct option to drop the "(stdin)= " portion of the output, but that can be done trivially with cut, grep or sed. For example:
with cut
signature=$(echo -n "$api_params_with_timestamp" \
| openssl dgst -sha256 -hmac "$SECRET_KEY" \
| cut -d ' ' -f2)
with grep
signature=$(echo -n "$api_params_with_timestamp" \
| openssl dgst -sha256 -hmac "$SECRET_KEY" \
| grep -E -o '[^ ]+$')
with sed
signature=$(echo -n "$api_params_with_timestamp" \
| openssl dgst -sha256 -hmac "$SECRET_KEY" \
| sed 's/^.*[[:space:]]//')
Note: you can also remove the line separator characters "\" and use the pipe for that purpose instead, e.g.
signature=$(echo -n "$api_params_with_timestamp" |
openssl dgst -sha256 -hmac "$SECRET_KEY" |
sed 's/^.*[[:space:]]//')
Either way is fine -- up to you.
Example Use/Output
$ api_params_with_timestamp="somethingXXXX"
> SECRET_KEY="XXXXXXXXXXXXXX"
> signature=$(echo -n "$api_params_with_timestamp" |
> openssl dgst -sha256 -hmac "$SECRET_KEY" |
> sed 's/^.*[[:space:]]//')
> echo $signature
46c86ea27df85b8ab9a8c565a344cadd1230f32ff1d773ba0fa5aa1076116d0b

Related

Why is my key not found when sending a JWT assertion to Azure AD?

I'm trying to write a bash script to get access tokens from Microsoft for my app registered in AD. I can't seem to get past this error though:
"error":"invalid_client","error_description":"AADSTS700027: The certificate with identifier used to sign the client assertion is not registered on application. [Reason - The key was not found., Thumbprint of key used by client: '356134 ...
The full gist can be found here: https://gist.github.com/smaring/3a3a6779a809beecc39624aada6e2b88
Here are some of the juicy bits ...
$ openssl pkcs12 -in <your-app>.pfx -out <your-app>.pem
$ openssl x509 -outform der -in <your-app>.pem -out ${PUBLIC_CERT_FILE}
$ openssl rsa -in <your-app>.pem -out ${PRIVATE_KEY_FILE}
x5t="$(sha1sum ${PUBLIC_CERT_FILE} | awk '{print $1;}' | openssl base64 | sed s/\+/-/g |sed 's/\//_/g' | sed -E s/=+$// )"
read -r -d '' HEADER <<EOF
{
"alg": "RS256",
"typ": "JWT",
"x5t": "${x5t}"
}
EOF
HEADER_NO_WHITESPACE=$(echo "${HEADER}" | sed ':a; N; s/[[:space:]]//g; ta')
BASE64_ENCODED_HEADER=$(echo ${HEADER_NO_WHITESPACE} | openssl base64 | sed s/\+/-/g |sed 's/\//_/g' | sed -E s/=+$// )
read -r -d '' PAYLOAD <<EOF
{
"aud": "https: //login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token",
"exp": ${exp},
"iss": "${CLIENT_ID}",
"jti": "${jti}",
"nbf": ${nbf},
"sub": "${CLIENT_ID}"
}
EOF
PAYLOAD_NO_WHITESPACE=$(echo "${PAYLOAD}" | sed ':a; N; s/[[:space:]]//g; ta')
BASE64_ENCODED_PAYLOAD=$(echo ${PAYLOAD_NO_WHITESPACE} | openssl base64 | sed s/\+/-/g |sed 's/\//_/g' | sed -E s/=+$// )
SIGNATURE=$( echo -n "${BASE64_ENCODED_HEADER}.${BASE64_ENCODED_PAYLOAD}" | \
openssl dgst -sha256 -binary -sign <(cat ${PRIVATE_KEY_FILE}) | \
openssl base64 | sed s/\+/-/g |sed 's/\//_/g' | sed -E s/=+$// )
CLIENT_ASSERTION="${BASE64_ENCODED_HEADER}.${BASE64_ENCODED_PAYLOAD}.${SIGNATURE}"
curl -s -X POST \
--header \"Content-Type: application/x-www-form-urlencoded\" \
-d \"\
scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&\
grant_type=client_credentials&\
client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&\
client_id=${CLIENT_ID}&\
client_assertion=${CLIENT_ASSERTION}\" \
https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token"
After getting a Java version of this working I figured out that I was not generating the thumbprint properly. The fix was this:
thumbprint=$(openssl x509 -in ${publicCertFile} -fingerprint -noout | awk '{split($0,a,"="); print a[2]}' )
thumbprintHex=$(echo -e "\\x${thumbprint}" | sed 's/:/\\x/g' )
thumbprintHexString=$(echo -e "${thumbprint}" | sed 's/://g' )
x5t=$(echo -ne "${thumbprintHex}" | openssl base64 | sed s/\+/-/g | sed 's/\//_/g' | sed -E s/=+$// )
I have updated the gist to the fully working bash script.
Construct a JWT assertion in bash using client-credential with cert to get an access token from Microsoft Azure Directory --> https://gist.github.com/smaring/3a3a6779a809beecc39624aada6e2b88

How to generate an aws4 signed request in bash?

I'm trying to connect to the Scaleway S3 api in bash (it's a subset of aws s3 api). The signature model is aws4_request, and is documented. In the code below I'm uploading a file. I've reviewed every line and although I'm no bash guru, I'm pretty confident I have all the right parts to compute a correct signature, but I still get 403 error as a response. The request itself seems fine, as far as I can see in a wireshark capture. Can anyone find any issue here?
#!/bin/bash
set -e
echo "creds $SCW_ACCESS_KEY/$SCW_SECRET_KEY"
BUCKET="vni-backups"
REGION="fr-par"
HOST="$BUCKET.s3.$REGION.scw.cloud"
filepath="../import.yml"
file_sha256=$(sha256sum -b $filepath | head -c64)
fulldate=$(date +"%Y%m%dT%H%M%SZ" --utc)
shortdate=$(date +"%Y%m%d" --utc)
# Assemble canonical url
canonicalRequest="PUT
/import.yml
content-type:text/plain
host:$HOST
x-amz-content-sha256:$file_sha256
x-amz-date:$fulldate
content-type;host;x-amz-content-sha256;x-amz-date
$file_sha256"
canonReqSha=$(echo -n "$canonicalRequest" | openssl dgst -sha256 | awk '{print $2}')
echo $canonicalRequest
echo "requestHash: $canonReqSha"
echo "
---------------------------------------
"
stringToSign="AWS4-HMAC-SHA256
$fulldate
$shortdate/fr-par/s3/aws4_request
$canonReqSha"
echo $stringToSign
dateKey=$(echo -n "$shortdate" | openssl dgst -sha256 -binary -hmac "AWS4$SCW_SECRET_KEY")
regionKey=$(echo -n "fr-par" | openssl dgst -sha256 -binary -hmac "$dateKey")
serviceKey=$(echo -n "s3" | openssl dgst -sha256 -binary -hmac "$regionKey")
signingKey=$(echo -n "aws4_request" | openssl dgst -sha256 -binary -hmac "$serviceKey")
signature=$(echo -n "$stringToSign" | openssl dgst -sha256 -hmac "$signingKey" | awk '{print $2}')
echo "signature: $signature"
echo "
---------------------------------------
"
# Make request
curl -X PUT \
-H "Content-Type: text/plain" \
-H "x-amz-content-sha256: $file_sha256" \
-H "x-amz-date: $fulldate" \
-H "Authorization: AWS4-HMAC-SHA256" \
-H "Credential: $SCW_ACCESS_KEY/${shortdate}/$REGION/s3/aws4_request" \
-H "SignedHeaders: content-type;host;x-amz-content-sha256;x-amz-date" \
-H "Signature: $signature" \
--data-binary #../import.yml \
"http://$BUCKET.s3.$REGION.scw.cloud/import.yml"
The request is done in clear http just because it's simpler to capture it with network analysis tool (e.g. wireshark) than https.

Bash OpenSSL is not equal to php openssl_encrypt value

I am trying to reach the same value of encryption in bash as it is from php , with no success .
Here is my php script
$message ='{"coupon_key":"011205358365345679","location_id":"288","device_key":"test_1234"}';
$key = "password";
$iv = base64_decode("dU+AyWweQYhAlGKLaxoS9w==");
$base64_iv = base64_encode($iv);
$base64_ev = base64_encode($encrypted_value);
$encrypted_value = openssl_encrypt(
$message,
'AES-256-CBC',
$key,
OPENSSL_RAW_DATA|OPENSSL_NO_PADDING,
$iv
);
$encode = base64_encode($encrypted_value);
//Zi7UcBwqM+gKZC9iZPgF3UHBXLUg1+Age/1+kRIfcstYBgGfYm7N1qCIFnm6jGn4AGQph/Q6hKjR1hYBT0wkJv8c8qFrHHZNUuraMfSRH3s=
$mac = hash_hmac('sha256', $base64_iv . $encode, $key);
echo '{"iv":"'.$base64_iv.'","value":"'.$encode.'","mac":"'.$mac.'"}'."<br>";
//{"iv":"dU+AyWweQYhAlGKLaxoS9w==","value":"Zi7UcBwqM+gKZC9iZPgF3UHBXLUg1+Age/1+kRIfcstYBgGfYm7N1qCIFnm6jGn4AGQph/Q6hKjR1hYBT0wkJv8c8qFrHHZNUuraMfSRH3s=","mac":"97fb6f4226a917322c7361af1d9b2949ad96691c1fc1a7f1c8379c71ae19f356"}
$retString2 = base64_encode('{"iv":"'.$base64_iv.'","value":"'.$encode.'","mac":"'.$mac.'"}');
echo $retString2;
//eyJpdiI6ImRVK0F5V3dlUVloQWxHS0xheG9TOXc9PSIsInZhbHVlIjoiWmk3VWNCd3FNK2dLWkM5aVpQZ0YzVUhCWExVZzErQWdlLzEra1JJZmNzdFlCZ0dmWW03TjFxQ0lGbm02akduNEFHUXBoL1E2aEtqUjFoWUJUMHdrSnY4YzhxRnJISFpOVXVyYU1mU1JIM3M9IiwibWFjIjoiOTdmYjZmNDIyNmE5MTczMjJjNzM2MWFmMWQ5YjI5NDlhZDk2NjkxYzFmYzFhN2YxYzgzNzljNzFhZTE5ZjM1NiJ9
and here is my bash version
password="password";
passwordhex=$(echo "$password" | xxd -c 256 -ps) ;
# iv2=$(hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random) ;
iv=$(printf 'dU+AyWweQYhAlGKLaxoS9w==' | base64 -d )
biv=$(printf "$iv" | base64) ;
hexiv=$(printf "$iv" | xxd -c 256 -ps) ;
eString=$(printf '{"coupon_key":"011205358365345678","location_id":"288","device_key":"test_1234"}');
tttw=$(printf "$eString" | openssl aes-256-cbc -e -nosalt -a -A -K "$passwordhex" -iv "$hexiv") ;
echo "$tttw" ;
printf "${biv}${tttw}" |openssl dgst -sha256 -hmac abc -macopt hexkey:"$passwordhex" | sed 's/^.* //';
macopt2=$(printf "${biv}${tttw}" |openssl dgst -sha256 -hmac abc -macopt hexkey:"$passwordhex" | sed 's/^.* //');
echo "$macopt2"
finalString="{'iv':'$biv','value':'$tttw','mac':'$macopt2'}";
echo "$finalString";
sendHash=$(printf "$finalString" | base64 -w 0)
echo "$sendHash"
Starting from
tttw=$(echo -n $eString | openssl aes-256-cbc -e -nosalt -a -A -K $passwordhex -iv $hexiv) ;
it goes wrong , because $tttw is returning then
bKG5quB9/YQUsmlFvDHq2H+AfNGQuDfVztyi0dd5hCY7hLfaACnjD8SWlwqy0yy4hXUZSA2YcTXej/xtMg9vqEpoO6CDw9hk7+tUcYOOV5aOdVBnSLowmEllHt0JfjdE
instead of Zi7UcBwqM+gKZC9iZPgF3UHBXLUg1+Age/1+kRIfcstYBgGfYm7N1qCIFnm6jGn4AGQph/Q6hKjR1hYBT0wkJv8c8qFrHHZNUuraMfSRH3s=
Can anybody maybe see what option is going wrong here?
Note1: I have updated the bash script to use prinf instead of echo -n
Note 2 : Seems on my newer machine I get "hex string is too short, padding with zero bytes to length" from the line
tttw=$(printf $eString | openssl aes-256-cbc -e -nosalt -a -A -K $passwordhex -iv $hexiv) ;
Looks like the issue is near the very top:
passwordhex=$(echo "$password" | xxd -c 256 -ps) ;
So essentially the output of echo "$password" is password\n.
Using echo like this will append a line feed to the input for xxd which I assume you already know since the original script was packed with echo -n. I mentioned in the comments that printf is more portable. The accepted way to use printf with a variable is like this:
passwordhex=$(printf '%s' "$password" | xxd -c 256 -ps) ;
The reason for this is that if your variable has a format identifier (like %s), it would change the output unexpectedly. This would have also showed up on shellcheck.
With this one change, here is the output:
Zi7UcBwqM+gKZC9iZPgF3UHBXLUg1+Age/1+kRIfcssepjJ8+wUjTDAjPUMkGA+eF9EL284iD5UIzA+REyhMWLWbUJpPltHFk1+lhQyVlUXXVTw0FFV1G+iQfEWhbyg4
484123c33b54e446c61120112955cd15f3592f42e737c9fa24db266cdec954a2
484123c33b54e446c61120112955cd15f3592f42e737c9fa24db266cdec954a2
{'iv':'dU+AyWweQYhAlGKLaxoS9w==','value':'Zi7UcBwqM+gKZC9iZPgF3UHBXLUg1+Age/1+kRIfcssepjJ8+wUjTDAjPUMkGA+eF9EL284iD5UIzA+REyhMWLWbUJpPltHFk1+lhQyVlUXXVTw0FFV1G+iQfEWhbyg4','mac':'484123c33b54e446c61120112955cd15f3592f42e737c9fa24db266cdec954a2'}
eydpdic6J2RVK0F5V3dlUVloQWxHS0xheG9TOXc9PScsJ3ZhbHVlJzonWmk3VWNCd3FNK2dLWkM5aVpQZ0YzVUhCWExVZzErQWdlLzEra1JJZmNzc2Vwako4K3dValREQWpQVU1rR0ErZUY5RUwyODRpRDVVSXpBK1JFeWhNV0xXYlVKcFBsdEhGazErbGhReVZsVVhYVlR3MEZGVjFHK2lRZkVXaGJ5ZzQnLCdtYWMnOic0ODQxMjNjMzNiNTRlNDQ2YzYxMTIwMTEyOTU1Y2QxNWYzNTkyZjQyZTczN2M5ZmEyNGRiMjY2Y2RlYzk1NGEyJ30=

Why do i have an empty file after sending stdout to it

Using bash i am executing following commands to filter information from a certificate
openssl s_client -connect google.com:443 < /dev/null > cert.pem
openssl x509 -in cert.pem -noout -subject > commonName
tr "," "\n" < commonName | sed -nr '/CN/p' | tr -d ' /t' > commonName
The last command leaves the file "commonName" empty, i wonder why this is.
If i instead append the file ">>" the desired filtered output is shown but unfiltered content remains.
Leaves file empty
tr "," "\n" < commonName | sed -nr '/CN/p' | tr -d ' /t' > commonName
Works but with undesired content
tr "," "\n" < commonName | sed -nr '/CN/p' | tr -d ' /t' >> commonName
Edit,
Might add that sending to a file with a new name works as intended. Changing "commonName" to "test" for example.
Thanks in advance!
/R
You can't edit the file with other commands and sed in only one pipe (the way you do it). You need a temporary file:
openssl s_client -connect google.com:443 < /dev/null > cert.pem
openssl x509 -in cert.pem -noout -subject > commonName
tr "," "\n" < commonName | sed -nr '/CN/p' | tr -d ' /t' > /tmp/temp
mv /tmp/temp commonName
And a better way to achieve the whole script, without temporary file:
openssl s_client -connect google.com:443 < /dev/null > cert.pem
openssl x509 -in cert.pem -noout -subject |
tr "," "\n" |
grep -o 'CN .*' > commonName

How to verify ECDSA and RSA certificates

I am going to run acme-tiny on a central webserver in order to get the certificates for my two Nginx reverse proxies issued. The newly created certificates are published over https and available to the reverse proxies via download.
I want the Nginx servers to check the newly created certificates before replacing the old certificates with the new ones. For this purpose, I wrote the following bash script, which is run on each Nginx server. I would like to know whether I missed something or whether you have ideas for improvement. Or is there a better way to realise this?
#!/bin/bash
set -e
# Commands for deriving the public keys:
# openssl ec -in ecdsa.key -pubout > ecdsa_public_key.pem
# openssl rsa -in rsa.key -pubout > rsa_public_key.pem
curl -O https://example.org/ecdsa.pem
curl -O https://example.org/intermediate.pem
curl -O https://example.org/rsa.pem
# Are the certificates not expired?
# Have they (ecdsa.pem, rsa.pem) been recently issued (validity >= 80 days)?
openssl x509 -checkend 6912000 -noout -in intermediate.pem
openssl x509 -checkend 6912000 -noout -in ecdsa.pem
openssl x509 -checkend 6912000 -noout -in rsa.pem
# Do the private keys and certificates belong together?
openssl x509 -in ecdsa.pem -pubkey | \
sed -n '/-----BEGIN PUBLIC KEY-----/,/-----END PUBLIC KEY-----/p' | \
cmp - ecdsa_public_key.pem
openssl x509 -in rsa.pem -pubkey | \
sed -n '/-----BEGIN PUBLIC KEY-----/,/-----END PUBLIC KEY-----/p' | \
cmp - rsa_public_key.pem
# Is the certificate chain valid?
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt intermediate.pem
openssl verify -CAfile intermediate.pem ecdsa.pem
openssl verify -CAfile intermediate.pem rsa.pem
# Are the certificates issued for the correct domain names?
(openssl x509 -noout -subject -in ecdsa.pem | awk -F'CN=' '{print $2}' | \
awk -F'/' '{print $1}'; \
openssl x509 -noout -text -in ecdsa.pem | grep "^[[:space:]]*DNS:" | \
xargs | tr ' ' '\n' | grep ^DNS | sed 's/^DNS://' | sed 's/,$//')| \
sort | uniq | cmp - domains.txt
(openssl x509 -noout -subject -in rsa.pem | awk -F'CN=' '{print $2}' | \
awk -F'/' '{print $1}'; \
openssl x509 -noout -text -in rsa.pem | grep "^[[:space:]]*DNS:" | \
xargs | tr ' ' '\n' | grep ^DNS | sed 's/^DNS://' | sed 's/,$//')| \
sort | uniq | cmp - domains.txt
UPDATE 1:
As I have stated I want to check every aspect of the certificate not just the validity of the certificate chain. It currently checks:
the chains validity,
whether private key and certificate match,
whether the cert has been recently issued making replacement of the older cert necessary
whether the cert has been issued for the desired domain names
Therefore, it isn't a duplicate to the linked questions. And, I would like to know whether I have forgotten some check or whether there is space for improvement. Below is an update to the bash script. Beware I have not tested it thoroughly, yet.
#!/bin/bash
set -e
# Execute the script:
# ( cd /path_to_workdir && \
# su - james -c "./check_cert.sh https://www.example.org intermediate.pem ecdsa.pem ecdsa_pubkey.pem /etc/ssl/certs/ca-certificates.crt domains.txt pass.txt" && \
# su - james -c "./check_cert.sh https://www.example.org intermediate.pem rsa.pem rsa_pubkey.pem /etc/ssl/certs/ca-certificates.crt domains.txt pass.txt" && \
# cat ecdsa.pem intermediate.pem > /etc/nginx/ssl/ecdsa_bundle.pem ) && \
# cat rsa.pem intermediate.pem > /etc/nginx/ssl/rsa_bundle.pem ) && \
# /etc/init.d/nginx reload
# Download URL, e.g. https://example.org
URL=$1
# Intermediate certificate of certificate chain
INTERMEDIATE=$2
# Issued certificate, e.g. ecdsa.pem
CERT=$3
# Public key derived from private key via:
# openssl ec -in ecdsa.key -pubout > ecdsa_pubkey.pem
# openssl rsa -in rsa.key -pubout > rsa_pubkey.pem
PUBKEY=$4
# ca-certificates file, e.g. /etc/ssl/certs/ca-certificates.crt
CACERTS=$5
# Domains that the cert should cover
DOMAINS=$6
# password-file.txt possible content:
# machine example.org login james password H3Llo
PASS=$7
# Download files if newer than local.
if [[ $(curl -s -O --netrc-file "$PASS" -w "%{http_code}" -z "$CERT" "$URL/$CERT") -eq 304 ]]; then
echo "No new certificate issued. Nothing to do!"
exit 1
fi
curl -s -O --netrc-file "$PASS" -z "$INTERMEDIATE" "$URL/$INTERMEDIATE"
# Is the certificate chain valid?
openssl verify -CAfile "$CACERTS" -untrusted "$INTERMEDIATE" "$CERT"
# Do the private keys and certificates belong together?
openssl x509 -in "$CERT" -pubkey | \
sed -n '/-----BEGIN PUBLIC KEY-----/,/-----END PUBLIC KEY-----/p' | \
cmp - "${PUBKEY}"
# Has the Let's Encrypt certificate been recently issued (validity >= 80 days)?
openssl x509 -checkend 6912000 -noout -in "$CERT"
# Are the certificates issued for the correct domain names?
(openssl x509 -noout -subject -in "$CERT" | awk -F'CN=' '{print $2}' | \
awk -F'/' '{print $1}'; \
openssl x509 -noout -text -in "$CERT" | grep "^[[:space:]]*DNS:" | \
xargs | tr ' ' '\n' | grep ^DNS | sed 's/^DNS://' | sed 's/,$//')| \
sort | uniq | cmp - "$DOMAINS"
The code might be run via cronjob:
0 1 * * * /usr/local/bin/update_cert.sh >/dev/null 2>&1
The content of update_cert.sh might be:
#!/bin/bash
( cd /path_to_workdir && \
su - james -c "./check_cert.sh https://www.example.org intermediate.pem ecdsa.pem ecdsa_pubkey.pem /etc/ssl/certs/ca-certificates.crt domains.txt pass.txt" && \
su - james -c "./check_cert.sh https://www.example.org intermediate.pem rsa.pem rsa_pubkey.pem /etc/ssl/certs/ca-certificates.crt domains.txt pass.txt" && \
cat ecdsa.pem intermediate.pem > /etc/nginx/ssl/ecdsa_bundle.pem ) && \
cat rsa.pem intermediate.pem > /etc/nginx/ssl/rsa_bundle.pem ) && \
/etc/init.d/nginx reload

Resources