Hi All,
Do we have Encryption function which supports SHA256 in SAS9.3? I saw this function exists in SAS 9.4 but
per the new features SAS9.3 in bleow link
it says that SHA256 encrpytion does exist in SAS 9.3.
Please suggest.
Thanks,
Deen
I found the right sasjar version and executed the code below is log file. though it shows couple of warnings but it produced the desired result.
NOTE: PROCEDURE HTTP used (Total process time):
real time 9.42 seconds
cpu time 0.21 seconds
NOTE: PROCEDURE HTTP used (Total process time):
real time 0.20 seconds
cpu time 0.01 seconds
NOTE: The ADD CLASSPATH command completed.
NOTE: The ADD CLASSPATH command completed.
NOTE: The ADD CLASSPATH command completed.
WARNING: SAS jars do not have a source compatibility guarantee across versions of SAS. Future
versions of these jars can change without notice.
NOTE: The ADD SASJAR command completed.
NOTE: The SUBMIT command completed.
Single Round: Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo=
Final Round: aJDhkskZ9OwP1n1akIoOgReHjm+iFJ0ofPt3CNhvFy8=
NOTE: The SUBMIT command completed.
NOTE: PROCEDURE GROOVY used (Total process time):
real time 0.64 seconds
cpu time 0.00 seconds
NOTE: The data set WORK.FOO has 1 observations and 1 variables.
NOTE: DATA statement used (Total process time):
real time 0.13 seconds
cpu time 0.00 seconds
WARNING: Could not initialize classpath. Classpath variable is not set.
one=Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo=
thousand=aJDhkskZ9OwP1n1akIoOgReHjm+iFJ0ofPt3CNhvFy8=
NOTE: There were 1 observations read from the data set WORK.FOO.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
If any quick suggestions to resolve the warning it would be really helpful for me ..also meanwhile, I need to extend this functionality to apply this hashing on a big dataset column for final requirements.
The SHA256 function was added in SAS 9.4m1. See this post: A fresh helping of hash: the SHA256 function in SAS 9.4m1.
I'm pretty sure that it wasn't present before then, even as an undocumented function. I remember researching that before I wrote the blog post...
My colleague Rick Langston will have a Hashing Technique paper published for SAS Global Forum (April) -- might be of interest!
I see that SHA-256 encryption is availbale in SASv9.4 but also I see that its available in SAS9.3 in below link (under Encrpyption section)
but I think it requires SAS/Secure license..is it correct?
You can use the method I shared at the following link to create a hashing function for use in SAS prior to v9.4 where many new functions for this were added
https://gist.github.com/FriedEgg/79ad315afa1b315e8ac3
PROC GROOVY was introduced in SAS 9.3 but this method will work similarly for version prior using Java Component Objects and compiling the Java classes outside of SAS. I do not have a example specific for plain SHA-256, however, the SHA-1 examples should be very easily adaptable.
I have a write-up for this as well:
I think this would work and looks like simple..
looks like it requires java..
but it seems I need to get java on my machine..
do you have similar code which uses Groovy...I read something on Proc Groovy..something there should be in proc groovy to achieve this..
meanwhile I do further research.. could you please post some commands in proc groovy to achieve sha256 in sas9.3
Thanks a lot!!
also
i tried to make use of the proc groovy from one of your sugested programs in below link
https://gist.github.com/FriedEgg/79ad315afa1b315e8ac3
%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.;
it produces below result
8d3d5f3c32ca98e8d9c4125509b3d7a00466d296
I think above code performs SHA256 hashing ...
and when I test above code for 'abc123' text as below its not producing the desired result as one of our requirements.
%let toHash=abc123;
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.;
am getting below result for the above code.
6367c48dd193d56ea7b0baad25b19455e529f5ee
but whereas expected result for hashing 'abc123' text using SHA256 algorithm is below.
Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo=
the requirement is below from our end user.
Hashing Requirements
1.Concatenate last name and date of birth to create Salt input
–Last Name is uppercase
–Date is in string format YYYY-MM-DD
–Output as string (varchar), e.g. "JONES1983-12-30’
2.Hash result from Step 1 one time using SHA 256 to create the Salt
3.Concatenate the results from Steps 1 and 2
4.Convert result from Step 3 to lower case and remove binary "0x"
5.Hash result from Step 4 one time using SHA 256
6.Hash result of Step 5 for 1000 iterations
7.Base 64 hashed result from step 6
8.Output value in ME998
Hashing Audit
Hashed value for ‘abc123’ based on single round, should be:
Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo=
if I have a proc groovy code or any suggestions to achieve above requirement it would be really helpful.
Thanks..
You are using SHA-1 encrypting in the code you posted. Which is what my example you used does... not SHA-256. The correct value for SHA-256 encryption is
6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
for an input value of:
abc123
Your requirements go far beyond just a regular SHA-256 hash and several components of those instructions make little to no sense to me. They appear to be written for some specific platform rather than a generalization...
Step 4. The notion of removing a binary '0x' doesn't make sense, but lets assume that this just means that we want to convert the byte array produced in step2 to a string of hex values in the form of
6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
rather than
0x6ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
No matter what, this isn't 'binary'
Next, we want to convert that value to lower case, so step4 is ultimately equal to
abc1236ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
or are we expected to somewhere convert the result of step 1 to a hex string as well?
616263313233366ca13d52ca70c883e0f0bb101e425a89e8624de51db2d2392593af6a84118090
Is this then meant to get hashed with SHA-256 as a string or as a byte array of itself? Very unclear instructions...
Finally in Step 8, I've never heard of anything called ME998 before, so, I am left to assume this is some internal system or table to your environment.
Regardless of the method I tried as outlined above, I cannot produce the 'expected' output you provided.
import java.security.MessageDigest import java.security.NoSuchAlgorithmException import javax.xml.bind.DatatypeConverter def toHexString(byte[] bytes) { return DatatypeConverter.printHexBinary(bytes) } def toByteArray(String s) { return DatatypeConverter.parseHexBinary(s) } def Sha256(byte[] bytes) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256") md.update(bytes) return md.digest() } def Base64(byte[] bytes) { return new String(Base64.getEncoder().encodeToString(bytes)) } def merryGoRound(byte[] bytes) { step6 = bytes for (i=0; i<1000; i++) { step6 = Sha256(step6) } return step6 } def DeenEncrypt1(String input) { step2 = Sha256(input.getBytes()) step3 = input + toHexString(step2) step4 = step3.toLowerCase() step5 = Sha256(step4.getBytes()) step6 = merryGoRound(step5) step7 = Base64(step6) return step7 } def DeenEncrypt2(String input) { step2 = Sha256(input.getBytes()) step3 = toHexString(input.getBytes()) + toHexString(step2) step4 = step3.toLowerCase() step5 = Sha256(step4.getBytes()) step6 = merryGoRound(step5) step7 = Base64(step6) return step7 } def DeenEncrypt3(String input) { step2 = Sha256(input.getBytes()) step3 = toHexString(input.getBytes()) + toHexString(step2) step4 = step3.toLowerCase() step5 = Sha256(toByteArray(step4)) step6 = merryGoRound(step5) step7 = Base64(step6) return step7 }
def to0xString(byte[] bytes) {
return "0x" + toHexString(bytes)
}
def DeenEncrypt4(String input) {
step2 = Sha256(input.getBytes())
step3 = input + toHexString(step2)
step4 = step3.toLowerCase()
step5 = Sha256(step4.getBytes())
step6 = to0xString(step5)
for (i=0; i<999; i++) { step6 = to0xString(Sha256(step6.getBytes()))}
step7 = Base64(Sha256(step6.getBytes()))
return step7
}
myString="abc123"
println(DeenEncrypt1(myString))
//Ir4+t3lTvPeE0s+daBIZY3btG0u8ist8znq6FnhIJNc=
println(DeenEncrypt2(myString))
//UqPhYbaI/bZ5s7jO3jnVIfPP4mT+BvPDYLWgInRSHB4=
println(DeenEncrypt3(myString))
//pobTckp4TAuDudylcugnzJGBc9YM4poSUPRk2zTgHrY=
println(DeenEncrypt4(myString))
//2wQPtEPxQJLCkmpH1Bo4t1OFqdcnGR8bzA7/uUrDiTY=
I would recommend you get some clearer hashing requirements
Matt,
Thanks for your prompt response with more detailed explanation . I will test your code once again and test our requirements.
You got the correct understanding about our requirement.however, after my testing and would check with the requirements once again..
Thanks a ton!!!
Matt,
I tried to test your code by keeping it between proc groovy and quit but somehow i was getting bleow error.
81 import java.security.MessageDigest
------
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
100 i<1000;
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
100 i++) { step6 = Sha256(step6) }
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
139 i<999;
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
139 i++) { step6 = to0xString(Sha256(step6.getBytes()))}
-
180
ERROR 180-322: Statement is not valid or it is used out of proper order.
Here are the clear requirements recieved from end user.
Step 1: hashing the input ('abc123' in our example) using SHA 256 algorithm
Step 2: input + lower (convert to hexadecimal(step 1 result))
Step 3: hashing the step 2 result using SHA 256 algorithm
Step 4: hashing the step 3 result for 1000 times using SHA 256 algorithm
Hashed value for ‘abc123’ based on single round, should be:
Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo=
Hashed value on above result after 1000 rounds, should be:
aJDhkskZ9OwP1n1akIoOgReHjm+iFJ0ofPt3CNhvFy8=
yes for display purpose only we need Base64 encoding.
filename groovy temp;
proc http method='get'
url="http://central.maven.org/maven2/org/codehaus/groovy/groovy-all/2.4.6/groovy-all-2.4.6.jar"
out=groovy;
run;
filename xmlbind temp;
proc http method='get'
url="http://central.maven.org/maven2/javax/xml/bind/jaxb-api/2.2.12/jaxb-api-2.2.12.jar"
out=xmlbind;
run;
filename cp temp;
options set=classpath= "%sysfunc(pathname(cp,f))";
proc groovy classpath=cp;
add classpath=groovy;
add classpath=xmlbind;
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 javax.xml.bind.DatatypeConverter
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.ArrayList
import org.apache.commons.codec.binary.Base64
/**
* DeenHash Requirements
*
*
* 1. Hash an input String (ex. "abc123") using SHA-256 algorithm
* 2. Concatenate input string + hexDump of step1 result
* 3. Hash step2 result using SHA-256 algorithm
* a. this marks a single-round
* b. print raw step3 result in Base64 encoding
* 4. Hash step3 result 999 additional times using SHA-256 algorithm
*/
public class DeenHash {
private String input = "abc123";
private String algorithm = "SHA-256";
protected ArrayList<byte[]> results=new ArrayList();
public DeenHash() {}
public DeenHash(String input) {
setInput(input);
}
public DeenHash(String algorithm, String input) throws NoSuchAlgorithmException{
setAlgorithm(algorithm);
}
public String getInput() {
return this.input;
}
public void setInput(String input) {
this.input = input;
}
public String getAlgorithm() {
return this.algorithm;
}
public void setAlgorithm(String algorithm) throws NoSuchAlgorithmException {
MessageDigest.getInstance(algorithm);
this.algorithm = algorithm;
}
private String toHexString(byte[] bytes) {
return DatatypeConverter.printHexBinary(bytes);
}
private String toBase64(byte[] bytes) {
Base64 base64 = new Base64();
return new String(base64.encode(bytes));
}
private byte[] merryGoRound(byte[] bytes) throws NoSuchAlgorithmException {
byte[] step4 = bytes;
for (int i=0; i<999; i++) { step4 = doSingleHash(step4); }
return step4;
}
public byte[] doSingleHash(byte[] bytes) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance(this.algorithm);
md.update(bytes);
return md.digest();
}
public void doDeenHash() throws NoSuchAlgorithmException {
byte[] step1 = doSingleHash(this.input.getBytes());
String step2 = this.input + toHexString(step1).toLowerCase();
byte[] step3 = doSingleHash(step2.getBytes());
this.results.add(step3);
byte[] step4 = merryGoRound(step3);
this.results.add(step4);
}
public String getFinalHash() {
return toBase64(this.results.get(1));
}
public String getInitialHash() {
return toBase64(this.results.get(0));
}
public void main(String[] args) {
if (args.length == 1) {
setInput(args[0]);
} else if (args.length == 2) {
try {
setAlgorithm(args[0]);
setInput(args[1]);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
try {
doDeenHash();
System.out.println("Single Round: " + toBase64(this.results.get(0)));
System.out.println("Final Round: " + toBase64(this.results.get(1)));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
endsubmit;
submit;
foo = new DeenHash()
foo.main()
endsubmit;
quit;
data foo;
input foo $;
cards;
abc123
;
run;
data _null_;
dcl javaobj hash('DeenHash');
do until(done);
set foo end=done;
length one thousand $ 64;
hash.callVoidMethod('setInput',strip(foo)); *strip is important;
hash.callVoidMethod('doDeenHash');
hash.callStringMethod('getInitialHash',one);
hash.callStringMethod('getFinalHash',thousand);
put (one thousand) (=/);
end;
stop;
run;
190 endsubmit; NOTE: The SUBMIT command completed. 191 192 submit; 193 foo = new DeenHash() 194 foo.main() 195 endsubmit; Single Round: Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo= Final Round: aJDhkskZ9OwP1n1akIoOgReHjm+iFJ0ofPt3CNhvFy8= NOTE: The SUBMIT command completed. 196 197 quit; NOTE: PROCEDURE GROOVY used (Total process time): real time 0.14 seconds cpu time 0.01 seconds
215 put (one thousand) (=/); 216 end; 217 stop; 218 run; one=Pbb2/dJkywFxhuW2O33twGm+Gu67UfoEFupDMUeBnuo= thousand=aJDhkskZ9OwP1n1akIoOgReHjm+iFJ0ofPt3CNhvFy8= NOTE: There were 1 observations read from the data set WORK.FOO. NOTE: DATA statement used (Total process time): real time 0.02 seconds cpu time 0.00 seconds
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.