According to the documentation at 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 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 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 "[email protected]" \ 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
wiwo The latest Google JSON credentials provide the key in PEM format. ::pki::parse_key expects the key in RSA format. Openssl to the rescue:
openssl rsa -in pk.key -out pk-rsa.key