Updated 2015-02-12 07:38:38 by plwork

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]\",\
\"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] \
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