how can I encode string in HMAC-SHA256 using pl/sql? - oracle

I'm working on REST API authorization in PL/SQL using Oracle database 11g Express Edition. I have api_key, nonce and signature as IN variables for every procedure.
Signature is a HMAC-SHA256 encoded string containing api_secret stored in my database. I want check if signature matching my api_secret in database.
My question is how can I encode string in HMAC-SHA256 using pl/sql?

There is SHA256 PL/SQL Implementation for Oracle 10g,11g by CruiserX.
Download the package from here.
Compile the package and package body in sqlplus.
Call the functions like this:
SQL> select sha256.encrypt('test message') from dual;
-- output: 3f0a377ba0a4a460ecb616f6507ce0d8cfa3e704025d4fda3ed0c5ca05468728
SQL> select sha256.encrypt_raw('74657374206D657373616765') from dual;
-- output: 3f0a377ba0a4a460ecb616f6507ce0d8cfa3e704025d4fda3ed0c5ca05468728

sha256.encrypt for Oracle 10g,11g by CruiserX was huge help for my HMAC-SHA256 API authorization in oracle XE.
Complete source code for my version with hmac-sha256 API authorization in oracle 11g XE you can find in my plsql_hmac-sha256 git project. But to perform HMAC keying we basically need store some basic information like nonce of last user request. And of course some function for hashing in my case (oracle 11gXE) sha256.encrypt . but in newer version oracle did provide better buil-in options for that like dbms_crypto.hash function.
PROCEDURE HMAC_AUTHORIZATION (i_api_key IN VARCHAR2, i_api_nonce IN NUMBER, i_api_sign IN VARCHAR, R OUT NUMBER)
IS
p_auth_id NUMBER(10);
p_api_sign_msg VARCHAR2(500);
p_api_nonce NUMBER(35);
p_api_sign VARCHAR2(500);
system_sign VARCHAR2(500);
BEGIN
SELECT AUTH_ID, API_NONCE INTO p_auth_id, p_api_nonce FROM USER_AUTH WHERE API_KEY = i_api_key AND AUTH_STATUS = 1 AND API_NONCE < i_api_nonce;
/* User signature */
p_api_sign_msg := p_auth_id || i_api_key || i_api_nonce;
p_api_sign := sha256.encrypt(p_api_sign_msg);
/* system signature */
system_sign := sha256.encrypt(p_auth_id || i_api_key || i_api_nonce);
IF p_api_sign = system_sign THEN
UPDATE USER_AUTH SET REQUESTS_COUNT = REQUESTS_COUNT+1, API_NONCE = i_api_nonce, LAST_REQUEST = SYSDATE WHERE API_KEY = i_api_key AND AUTH_STATUS = 1 AND AUTH_ID = p_auth_id;
commit;
R := 1;
ELSE
R := 0;
END IF;
EXCEPTION WHEN NO_DATA_FOUND THEN
R := 0;
WHEN OTHERS THEN
R := 0;
END HMAC_AUTHORIZATION;
Thanks for help. Maybe someone will find this useful :)

Related

Oracle Context equivalent for DB2

I am currently migrating from oracle to DB2. I have actively used sys_context in most of the applications to get the userID of logged in user through sessions(IIS and .net framework).
I am looking to convert the following set of scripts from oracle to DB2. So far not able to find any equivalent for oracle context in DB2.
SQL> create context my_ctx
2 using pkg_ctx;
Context created.
SQL> create package pkg_ctx
2 as
3 procedure set_context;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_ctx
2 as
3 procedure set_context
4 as
5 begin
6 dbms_session.set_context( 'MY_CTX', 'USERNAME', 'test' );
7 end;
8 end;
9 /
Package body created.
SQL> exec pkg_ctx.set_context;
PL/SQL procedure successfully completed.
SQL> select sys_context( 'MY_CTX', 'USERNAME' )
2 from dual;
SYS_CONTEXT('MY_CTX','USERNAME')
-------------------------------------------------------------------------------
test
Assuming you work with Db2 for LUW, you can use one of the CURRENT CLIENT_* registry variables to pass session context information from the client application to the server. For example, during the session initialization on the client side you would issue SET CURRENT CLIENT_USERID='stacky', then on the server that variable can be read by a routine or a trigger.
It worked like this, haven't tried it at application level. But from the database tried to register 2 users from 2 different instances of the same database on DB2.
call sysproc.wlm_set_client_info('stacky',null,null,null,null);
select wlm_set_client_info from sysibm.sysdummy1;
returns stacky
from another instance of the same database
call sysproc.wlm_set_client_info('test',null,null,null,null);
select wlm_set_client_info from sysibm.sysdummy1;
returns test.
Will try this from the application level through creating sessions of the users, will monitor how exactly this works with the special registers.
Not sure if this is the exact way to do this, will explore more and keep updated.
For replicating a similar situation in our environment, while doing the migration from oracle to DB2 LUW.
The below procedure sets up the all the context values in user_id (limited to varchar 255) field which we need as as 'key1:value1|key2:value2|'
Procedure to setup the context values
CREATE OR REPLACE PROCEDURE setup_context(pValues IN VARCHAR)
IS
lValues VARCHAR2(255) := pValues;
BEGIN
--save the as is
wlm_set_client_info(lValues, NULL, NULL, NULL, NULL);
END;
Procedure to set the value of the desired key
CREATE OR REPLACE FUNCTION sys_context(prefix IN VARCHAR) RETURN VARCHAR IS
lValues VARCHAR2(255);
lParam VARCHAR2(255);
lKey VARCHAR2(255);
lValue VARCHAR2(255);
lIndex PLS_INTEGER;
BEGIN
--get the data from the current session
SELECT CURRENT CLIENT_USERID INTO lValues FROM dual;
LOOP
EXIT WHEN lValues IS NULL OR LENGTH(lValues) = 0;
lIndex := instr(lValues, '|');
IF lIndex > 0 THEN
lParam := substr(lValues, 1, lIndex-1);
lValues := substr(lValues, lIndex+1);
ELSE
lParam := lValues;
lValues := NULL;
END IF;
lIndex := instr(lParam, ':');
lKey := substr(lParam, 1, lIndex-1);
lValue := substr(lParam, lIndex+1);
--get the matching value
IF(lKey = prefix ) Then
RETURN lValue;
END IF;
END LOOP;
RETURN '';
END;
reference : https://www.ibm.com/support/knowledgecenter/en/SSEPEK_11.0.0/sqlref/src/tpc/db2z_sp_wlmsetclientinfo.html

translate from oracle to db2

If time permits, would someone please let me know what syntax would need to be changed to accommodate this from oracle to db2? I am hoping to use the db2 command line prompt DB2 client version 10.5.3. Please let me know if you need anymore information. I changed the VARCHAR2 to VARCHAR and the NUMBER(3) to INT as well as NUMBER(10 to INT. Its much appreciated. Jean T.
DECLARE
v_count PLS_INTEGER := 0;
TYPE inv_rt IS RECORD (upc VARCHAR(20),
store INT,
inv_avail INT
);
TYPE inv_tt IS TABLE OF inv_rt;
inv_arr inv_tt;
CURSOR cur_data (in_date DATE)
IS
SELECT i.upc, i.storenumber, i.inv_avail - SUM(t.unitvolume) AS inv_avail
FROM zz_tran t,
zz_start_inv i
WHERE t.weekenddate <= in_date
AND t.upc = i.upc
AND t.storenumber = i.storenumber
GROUP BY i.upc, i.storenumber, i.inv_avail;
BEGIN
inv_arr := inv_tt();
FOR d IN (SELECT DISTINCT weekenddate
FROM zz_tran
ORDER BY weekenddate
)
LOOP
inv_arr := inv_tt();
OPEN cur_data (d.weekenddate);
FETCH cur_data BULK COLLECT INTO inv_arr;
CLOSE cur_data;
FORALL i IN inv_arr.FIRST .. inv_arr.LAST
UPDATE zz_tran
SET inv_avail = inv_arr(i).inv_avail
WHERE weekenddate = d.weekenddate
AND upc = inv_arr(i).upc
AND storenumber = inv_arr(i).store;
COMMIT;
END LOOP;
END;
/
If converting from Oracle to Db2, you can create your Db2 database in Oracle Compatibility mode. That will allow Db2 to recognize e.g. VARCHAR2 natively.
https://www.ibm.com/support/knowledgecenter/en/SSEPGG_11.5.0/com.ibm.db2.luw.apdv.porting.doc/doc/c_compat_oracle.html
You can also use the Database Conversion Workbench to assist in converting from Oracle to Db2
https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=05901c97-75b2-47a1-9c32-25f748855913

Oracle equivalent of stored procedure that returns an inline table?

Example in T-SQL (SQL Server - taken from here):
CREATE PROC proc_authors
#au_lname VARCHAR(40)
AS
SELECT
au_id, au_fname, au_lname, city, state
FROM authors
WHERE au_lname = #au_lname
go
Is it possible in Oracle to create a stored procedure that returns an inline table (without declaring a type - like the above)? If not, what would be the closest alternative? i.e. declare inline type, then use it. The idea is to minimize number of DB permissions that are granted.
Please include sample code as part of your answer.
Reasoning behind using stored procedure vs function - we have legacy software that can only execute stored procedures, or raw queries. It appears that only stored procedures in there have support for parameterized execution, which is what we are after.
try this with ref cursor
PROCEDURE proc_get_tada(ip_user IN VARCHAR2,
op_error_code OUT NUMBER,
op_cursor OUT SYS_REFCURSOR,) AS
BEGIN
OPEN op_cursor FOR
SELECT * FROM your_table yt where yt.user = ip_user;
EXCEPTION
WHEN OTHERS THEN
op_error_code := -1;
END proc_get_tada;
you will get collection of all data from you table you can iterate in java or calling program.
Maybe you are searching for something like this:
create table author
(
au_id number,
au_name varchar2(100)
);
insert into author (au_id, au_name) values(1, 'ME');
create or replace function getAuthor(auName varchar2)
return author%rowtype
is
retval author%rowtype;
begin
select * into retval from author where au_name=auName;
return retval;
end;
declare
auth author%rowtype;
begin
auth := getAuthor('ME');
dbms_output.put_line(auth.au_id);
end;

Oracle data masking

We have one requirement to mask a particular table column using a Oracle function which gives persistent masked output string.
We tried Oracle Hash Function but it does not give String type return value.
We tried Oracle Random function (dbms_random.string) but it does not give Persistent output string.
I read on internet that this is called deterministic masking. But we do not want to use Oracle Enterprise Manager; however we require a direct Oracle function.
Please suggest.
This problem is easily solved in 12c with the function STANDARD_HASH.
The solution in previous versions is only slightly more complicated. Build a simple wrapper around DBMS_CRYPTO that acts just like STANDARD_HASH:
--Imitation of the 12c function with the same name.
--Remember to drop this function when you upgrade!
create or replace function standard_hash(
p_string varchar2,
p_method varchar2 default 'SHA1'
) return varchar2 is
v_method number;
v_invalid_identifier exception;
pragma exception_init(v_invalid_identifier, -904);
begin
--Intentionally case-sensitive, just like the 12c version.
if p_method = 'SHA1' then
v_method := dbms_crypto.hash_sh1;
--These algorithms are only available in 12c and above.
$IF NOT DBMS_DB_VERSION.VER_LE_11 $THEN
elsif p_method = 'SHA256' then
v_method := dbms_crypto.hash_sh256;
elsif p_method = 'SHA384' then
v_method := dbms_crypto.hash_sh384;
elsif p_method = 'SHA512' then
v_method := dbms_crypto.hash_sh512;
$END
elsif p_method = 'MD5' then
v_method := dbms_crypto.hash_md5;
else
raise v_invalid_identifier;
end if;
return rawToHex(dbms_crypto.hash(utl_raw.cast_to_raw(p_string), v_method));
end;
/
You may need to logon with SYS and grant your user access to DBMS_CRYPTO to make the function work:
grant execute on sys.dbms_crypto to <your_schema>;
Create a public synonym, grant it to everyone, and it works exactly the same way.
create public synonym standard_hash for <schema with function>.standard_hash;
grant execute on standard_hash to public;
select standard_hash('Some text', 'MD5') from dual;
9DB5682A4D778CA2CB79580BDB67083F
select standard_hash('Some text', 'md5') from dual;
ORA-00904: : invalid identifier
Here is a simple example of using the function:
update some_table
set column1 = standard_hash(column1),
column2 = standard_hash(column2);
But updating large amounts of data can be slow. It may be faster to create a new table, drop the old one, rename the new one, etc. And the hash value may be larger than the column size, it may be necessary to alter table some_table modify column1 varchar2(40 byte);
It amazes me how many products and tools there are to do such a simple thing.
If you looking something like mask the production data to move it into non-prod for integration testing. Below the "user defined" function would be helpful to you. This function will work only 10G and above.
create or replace function scrubbing(word in varchar2)
return varchar2
as
each_var char(2);
final_val varchar2(100);
complete_data varchar2(4000);
each_word varchar2(1000);
cursor val is select substr(replace(word,' ','#'),-level,1) from dual connect by level<=length(word);
begin
open val;
--final_val:= '';
loop
fetch val into each_var;
exit when val%NOTFOUND;
--dbms_output.put_line(each_var);
final_val := trim(final_val)||trim(each_var);
--dbms_output.put_line(final_val);
select regexp_substr(final_val,'[A-Za-z]+') into each_word from dual;
select replace(translate(final_val,each_word,dbms_random.string('L',length(word))),'#',' ') into complete_data from dual;
end loop;
return complete_data;
end;
In Oracle 12C dbms_redact.add_policy is available. It can be used to get the masked value in the select query itself.
You can use dbms_crpyto package of oracle , first you need to convert varchar2 type to raw then mask the data according to the hash value.

oracle stored procedures encryption key

How to update stored procedures encryption key in oracle 11g.
(val IN varchar) RETURN varchar AS
outstr varchar(10);
descr varchar(255);
BEGIN
-- Encryption Key For Encryption
secret_code := '123456788765432112345678';
--create instance of OLE object on an instance of SQL Server;
success = 0
EXEC rc = sp_OACreate 'CAPICOM.EncryptedData', object OUT
if rc <> 0
begin
exec sp_oageterrorinfo object, src out, descr out end
method_call := 'SetSecret("' + Secret_code + '")'
RETURN (outstr);
END;
I am not sure what are you looking for.
Check this
and also you may review this too
Stored procedures can be WRAPPED which is a form of encryption. But you can't update the key as it is hard-coded into the wrapping algorithm.
This might make you think they are susceptible to be cracked. They are and unwrappers are available.

Resources