According to the documentation at https://developers.google.com/accounts/docs/OAuth2ServiceAccount%|%Using OAuth 2.0 for Server to Server Applications%|%
===
The signing algorithm in the JWT header must be used when computing the signature.
The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is
RSA using SHA-256 hashing algorithm. This is expressed as ‘RS256’ in the ‘alg’ field in the JWT header.
Sign the UTF-8 representation of the input using SHA256withRSA
(also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function) with the private key
obtained from the Google Cloud Console. The output will be a byte array.
===
What is there in the tcl space that is equivalent to `SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function)`? I was thinking that the `sha256` package would do the trick, but now I'm not so sure. Reason is that I took the signature block given on the developers.google.com page and did a `::base64::decode`. This gave me a string of assorted low and high ascii, which is not what I'm getting with a `::sha2::hmac`
The goal is to be able to set up an OAuth2 connection to Google Analytics. I've already been to the https://cloud.google.com/console%|%Google Cloud Console%|% to set up the account, and get the various keys. The code I'm using is below. I'm still at the point where you generate the header. I haven't got to the HTTP stuff yet.
======
package require json
package require sha256
package require base64
set h [open "client_secret.json" r]
set d [read $h]
close $h
set mydd [dict create {*}[lindex [::json::json2dict $d] 1]]
set header [string map {\n "" "=" ""} [::base64::encode "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"]]
set claims [string map {\n "" "=" ""} [::base64::encode "{\
\"iss\":\"[dict get $mydd client_email]\",\
\"scope\":\"https://www.googleapis.com/auth/analytics.readonly\",\
\"aud\":\"https://accounts.google.com/o/oauth2/token\",\
\"exp\":[expr [clock seconds] + 3600],\
\"iat\":[clock seconds]\
}"]]
set signature "$header.$claims"
set h [open "privatekey.p12" r]
fconfigure $h -translation binary
set d [read $h]
close $h
set sig [string map {\n "" "=" ""} [::base64::encode [::sha2::hmac $d $signature]]]
set final "$signature.$sig"
puts $final
======
[APN] See if http://core.tcl.tk/tcllib/doc/trunk/embedded/www/tcllib/files/modules/pki/pki.html#3%|%pki::sign%|% from the pki package in [tcllib] does what you want.
[Bovine] We got this working, and the pki module was indeed the key:
======
package require yajltcl
package require sha256
package require base64
package require http
package require tls
package require pki
proc base64_url_encode {input} {
return [string map {\n "" "=" "" + - / _} [::base64::encode $input]]
}
set header {{"alg":"RS256","typ":"JWT"}}
set header [base64_url_encode $header]
set x [yajl create #auto]
$x map_open \
string iss string "this_is_my_identifier_here@developer.gserviceaccount.com" \
string scope string "https://www.googleapis.com/auth/prediction" \
string aud string "https://accounts.google.com/o/oauth2/token" \
string exp number [expr {[clock seconds] + 3600}] \
string iat number [clock seconds] \
map_close
set claims [base64_url_encode [$x get]]
$x delete
set signature "$header.$claims"
set fd [open "privatekey.pem" "r"]
set keydata [read $fd]
close $fd
set key [::pki::pkcs::parse_key $keydata "this_is_my_pem_password"]
set sig [base64_url_encode [::pki::sign $signature $key sha256]]
set final "$signature.$sig"
set postdata [::http::formatQuery grant_type "urn:ietf:params:oauth:grant-type:jwt-bearer" assertion $final]
::http::register https 443 ::tls::socket
set fp [::http::geturl "https://accounts.google.com/o/oauth2/token" -query $postdata]
set status [::http::status $fp]
set ncode [::http::ncode $fp]
set html [::http::data $fp]
::http::cleanup $fp
array set token [::yajl::json2dict $html]
parray token
======
<<categories>> Security