- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
I am using SAS 9.4. I want to write a JSON Web Signature ("JWS") to complete my JSON Web Token ("JWT") - (I already have the header and claims encoded and tested). I would like to make API calls to Google (Server to Server API). My problem is that I am not certain how to create the JWS. Per all the docs and tutorial web sites I have researched the syntax to create the signature is (pseudo-code):
encodedContent = base64UrlEncode(header) + "." + base64UrlEncode(payload);signature = hashHmacSHA256($encodedContent);
and/or
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload);HMACSHA256(encodedString, 'secret');
etc.
I have been attempting to create the JWS using SAS PROC GROOVY code found here: https://gist.github.com/FriedEgg/79ad315afa1b315e8ac3 ...and other places.
Google's doc (https://developers.google.com/identity/protocols/OAuth2ServiceAccount) states: "Sign the UTF-8 representation of the input ('{Base64url encoded header}.{Base64url encoded claim set}') using SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function) with the private key obtained from the Google Developers Console. The output will be a byte array."
However, I am confused about what "key" to use (or whether I should be using a key) as some sites (example above) just suggest hashing the encoded header+claims while others suggest hashing using a "secret key" while others say "private key" - and the github page (URL above) just states "key".
I am also confused about sha1 vs. sha256. The Google docs state sha256 but some have suggested sha1 in this Stack Overflow thread: http://stackoverflow.com/questions/18362327/creating-digital-signature-usining-sas-for-google-api-ge... . I do note a related question where FriedEgg suggested writing a perl module to use sha256 ( https://communities.sas.com/t5/Base-SAS-Programming/Connecting-to-amazon-web-service/m-p/35555/highl... ) but we do not currently have perl installed on our SAS server and our admins have the x/execute commands in SAS disabled for security purposes.
Again, I have verified my header and claims (encoded in SAS) are working using: jwt.io and kjur.github.io/jsjws/tool_jwt.html.
Since SAS supports Groovy (PROC GROOVY) I assume that I can write my JWS successfully using java code but I have not yet been able to replicate any JWS in my SAS code using examples found on the sites I have mentioned above and others.
Has anyone ever done this before (using SAS to connect to Google APIs using JWT)?
Any help is appreciated!
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
I updated the gist you mentioned to add support for JWT. I made use of the JAVA library java-jwt and added support for SHA256withRSA.
Example for PROC GROOVY:
https://gist.github.com/FriedEgg/79ad315afa1b315e8ac3#file-sha256withrsa-jwt4sas-sas
Github Repository for my fork of JAVA-JWT:
https://github.com/FriedEgg/java-jwt
and the JAR:
https://github.com/FriedEgg/java-jwt/releases/download/java-jwt-2.1.2/java-jwt-2.1.1-SNAPSHOT.jar
filename jwt '/path/to/java-jwt-2.1.2-SNAPSHOT.jar';
filename cp temp;
proc groovy classpath=cp;
add classpath=jwt;
/* test *//*
submit;
import java.security.KeyFactory
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.RSAPrivateKeySpec;
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.Algorithm;
modulus = new BigInteger("AB34D0D48B16438BBBDFF6DA0CC7D3936DC2CE71E89DEF5B4AA9EA2539EAC17B5765FAAF2C533D176AF95CF16F157FECB977F51DE6E5473808E95A487321A3AB", 16);
privateExponent = new BigInteger("0FEA1CEF64EE70E0F059E54C679BBBA31CB4DB13E397AAC445B07DBF701ECE554DE0266E99B96CE8F3148291551E70357E5AF29FB192FB9E5C2F4AA5C45A0141", 16);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus,privateExponent);
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyFactory.generatePrivate(rsaPrivateKeySpec);
signer = new JWTSigner(rsaPrivateKey);
HashMap<String, Object> claims = new HashMap<String, Object>();
String token = signer.sign(claims, new JWTSigner.Options().setAlgorithm(Algorithm.RS256));
println token
endsubmit;
*/
submit load;
import java.security.KeyFactory
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.RSAPrivateKeySpec;
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.Algorithm;
public class Jwt4SAS {
private JWTSigner signer;
private HashMap<String, Object> claims = new HashMap<String, Object>();
public Jwt4SAS(String modulus, String privateExponent) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(
new BigInteger(modulus, 16),
new BigInteger(privateExponent, 16)
);
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyFactory.generatePrivate(rsaPrivateKeySpec);
signer = new JWTSigner(rsaPrivateKey);
}
public void addClaim(String key, String value) {
this.claims.put(key, value);
}
public String sign() {
return signer.sign(claims, new JWTSigner.Options().setAlgorithm(Algorithm.RS256));
}
}
endsubmit;
quit;
options set=classpath "%sysfunc(pathname(cp,f))";
data x;
dcl javaObj jwt4sas('Jwt4SAS',
'AB34D0D48B16438BBBDFF6DA0CC7D3936DC2CE71E89DEF5B4AA9EA2539EAC17B5765FAAF2C533D176AF95CF16F157FECB977F51DE6E5473808E95A487321A3AB',
'0FEA1CEF64EE70E0F059E54C679BBBA31CB4DB13E397AAC445B07DBF701ECE554DE0266E99B96CE8F3148291551E70357E5AF29FB192FB9E5C2F4AA5C45A0141');
jwt4sas.callVoidMethod('addClaim','sub','123456789');
jwt4sas.callVoidMethod('addClaim','name','John Doe');
length jwt $ 2048;
Jwt4SAS.callStringMethod('sign',jwt);
jwt = strip(jwt);
put jwt=;
array enc[3] $ 1024 encodedHeader encodedPayload signedData;
do _n_=1 to dim(enc);
enc[_n_]=scan(jwt,_n_,'.');
end;
length header payload $ 1024;
header = input(encodedHeader,$base64x1024.);
payload = input(encodedPayload,$base64x1024.);
put (encodedHeader header ) (=/) //
(encodedPayload payload) (=/) //
signedData=;
run;
/*http://jwt.io*/
Instructions to generate a RSA key using OpenSSL and get the modulus and privateExponent
*generate RSA private key openssl genrsa -out foo.pem * show the details of the key as text, help identify modulus and privateExponent openssl rsa -in foo.pem –text * makes the values easier to copy/paste openssl asn1parse -in foo.pem * create you public key openssl rsa -in foo.pem -outform PEM -pubout -out foo_public.pem
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
This is GREAT! Thanks! I was struggling with the same thing and will try this.
Quick question, though... Why not just use the Google API for Java in a proc Groovy?
https://developers.google.com/identity/protocols/OAuth2ServiceAccount?hl=en_US#delegatingauthority (see the "Preparing to make an authorized API call" heading and Java example).
The Java API are at https://github.com/google/google-api-java-client#Service_Accounts
From the sounds of it, the SHA256HMACHEX('key','message',3) function doesn't quite work like this groovy option, is that correct?