Last active
July 11, 2022 05:28
-
-
Save FriedEgg/79ad315afa1b315e8ac3 to your computer and use it in GitHub Desktop.
Using PROC GROOVY in SAS to Calculate Hash Digests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%let key = %nrstr(kd94hf93k423kf44&pfkkdhi9sl3r4s00); | |
%let message = %nrstr(GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal); | |
proc groovy; | |
add sasjar="commons_codec" version="1.7.0.0_SAS_20121211183158"; *version is specific to SAS Installation and may differ from this; | |
submit "&key." "&message."; | |
import javax.crypto.Mac | |
import javax.crypto.spec.SecretKeySpec | |
import org.apache.commons.codec.binary.Base64 | |
def base64hmacsha1(key, message) { | |
mac = Mac.getInstance("HmacSHA1") | |
mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA1")) | |
sha1_bytes = mac.doFinal(message.getBytes()) | |
base64 = new Base64() | |
return new String(base64.encode(sha1_bytes)) | |
} | |
exports.base64hmacsha1 = base64hmacsha1(args[0], args[1]) | |
endsubmit; | |
quit; | |
%put &base64hmacsha1.; | |
/*tR3+Ty81lMeYAr/Fid0kMTYa/WM=*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%let key = mykey; | |
%let message = helloworld; | |
proc groovy; | |
submit "&key." "&message."; | |
import javax.crypto.Mac | |
import javax.crypto.spec.SecretKeySpec | |
def sha1(key, message) { | |
mac = Mac.getInstance("HmacSHA1") | |
mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA1")) | |
sha1_bytes = mac.doFinal(message.getBytes()) | |
return new BigInteger(1, sha1_bytes).toString(16) | |
} | |
exports.hmacsha1 = sha1(args[0], args[1]) | |
endsubmit; | |
quit; | |
%put &hmacsha1.; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
filename inc temp; | |
data _null_; | |
file inc; | |
input @; | |
put _infile_; | |
cards4; | |
filename cp temp; | |
proc groovy classpath=cp; | |
add sasjar="commons_codec" version="1.7.0.0_SAS_20121211183158"; *version is specific to SAS Installation and may differ from this; | |
submit parseonly; | |
import java.security.MessageDigest | |
import javax.crypto.Mac | |
import javax.crypto.spec.SecretKeySpec | |
import org.apache.commons.codec.binary.Base64 | |
class SASHash { | |
private def mac(algorithm, key) { | |
def mac = Mac.getInstance(algorithm) | |
mac.init(new SecretKeySpec(key.getBytes(), algorithm)) | |
return mac | |
} | |
String base64hmacsha1(String key, String message) { | |
def mac = this.mac("HmacSHA1", key) | |
return Base64.encodeBase64String(mac.doFinal(message.getBytes())) | |
} | |
String base64hmacsha256(String key, String message) { | |
def mac = this.mac("HmacSHA256", key) | |
return Base64.encodeBase64String(mac.doFinal(message.getBytes())) | |
} | |
private def sha(algorithm, message) { | |
return MessageDigest.getInstance(algorithm).digest(message.getBytes()) | |
} | |
String sha1(String message) { | |
return new BigInteger(1, this.sha("SHA1",message)).toString(16) | |
} | |
String base64sha1(String message) { | |
return Base64.encodeBase64String(this.sha("SHA1",message)) | |
} | |
String sha256(String message) { | |
return new BigInteger(1, this.sha("SHA-256",message)).toString(16) | |
} | |
String base64sha256(String message) { | |
return Base64.encodeBase64String(this.sha("SHA-256",message)) | |
} | |
} | |
endsubmit; | |
quit; | |
options set=classpath "%sysfunc(pathname(cp,f))"; | |
;;;; | |
run; | |
%macro SASHash(action,key=key,message=message,return=hash,include=inc); | |
%goto &action; | |
%compile: | |
%include &include; | |
%return; | |
%declare: | |
if _n_=1 then declare javaobj SASHash('SASHash'); | |
%return; | |
%hash: | |
SASHash.callStringMethod("&action",strip(&message),&return); | |
%return; | |
%hmachash: | |
SASHash.callStringMethod("&action",strip(&key),strip(&message),&return); | |
%return; | |
%base64hmacsha256: | |
%goto hmachash; | |
%base64hmacsha1: | |
%goto hmachash; | |
%sha1: | |
%goto hash; | |
%base64sha1: | |
%goto hash; | |
%sha256: | |
%goto hash; | |
%base64sha256: | |
%goto hash; | |
%mend; | |
%let key = sdfoij3242039sdflkj3r23; | |
%SASHash(compile,include=inc) | |
data class; | |
%SASHash(declare) | |
length hashname $ 128; | |
set sashelp.class; | |
%SASHash(sha256,message=name,return=hashname) | |
run; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*This is to encode items in a data set instead of a macro variable, using javaobj*/ | |
filename cp temp; | |
proc groovy classpath=cp; | |
submit parseonly; | |
import java.security.MessageDigest | |
class Sha1 { | |
public String encode(String message) { | |
return new BigInteger(1, MessageDigest.getInstance("SHA1").digest(message.getBytes())).toString(16); | |
} | |
} | |
endsubmit; | |
quit; | |
options set=classpath "%sysfunc(pathname(cp,f))"; | |
data class; | |
dcl javaobj sha1('Sha1'); | |
do until (done); | |
set sashelp.class end=done; | |
length encoded_name $ 40; | |
sha1.callStringMethod('encode', strip(name), encoded_name); | |
output; | |
end; | |
run; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%let toHash=something to hashify; | |
proc groovy; | |
submit "&toHash."; | |
import java.security.MessageDigest | |
exports.sha1 = new BigInteger(1, MessageDigest.getInstance("SHA1").digest(args[0].getBytes())).toString(16) | |
endsubmit; | |
quit; | |
%put &sha1.; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
*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*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
How to get the checksum for one file using SAS