How to SHA-256 hmac in Oracle PL/SQL? - oracle

Is there any way to do SHA-256 hmac encryption in Oracle PL/SQL ?
I have Oracle 11.2 and the function is not available in Crypto package.
Thank you in advance.

Function hmac_sha256
(
ptext Varchar2,
pkey Varchar2
) Return Varchar2
Is
-- pad const
c_opad Raw(1) := '5c';
c_ipad Raw(1) := '36';
c_kpad Raw(1) := '00';
--SHA256 block size 512 bit
c_blocksize Integer := 64;
--local var, length equals to blocksize
l_opad Raw(64);
l_ipad Raw(64);
l_key Raw(64);
Begin
l_opad := utl_raw.copies(c_opad, c_blocksize);
l_ipad := utl_raw.copies(c_ipad, c_blocksize);
If utl_raw.length(utl_raw.cast_to_raw(pkey)) > c_blocksize Then
l_key := utl_raw.cast_to_raw(sha256.encrypt(pkey));
Else
l_key := utl_raw.cast_to_raw(pkey);
End If;
l_key := l_key ||
utl_raw.copies(c_kpad, c_blocksize - utl_raw.length(l_key));
Return sha256.encrypt_raw(utl_raw.bit_xor(l_key, l_opad) ||
sha256.encrypt_raw(utl_raw.bit_xor(l_key, l_ipad) || utl_raw.cast_to_raw(ptext))
);
End;

Related

PL/SQL Get Dimensions of JPEG Image

Support for Oracle Multimedia was dropped in Oracle 19c, so my code to extract dimensions from a JPEG image is throwing an error. Is there a workaround to this issue?
For Oracle 12, my code looked like this:
BEGIN
img := ORDSYS.ORDImage.init('FILE', my_dir, my_img_name);
img.setProperties();
w := img.getWidth();
h := img.getHeight();
EXCEPTION
WHEN OTHERS THEN
w := NULL;
h := NULL;
END;
Based on code found in a response to "Getting Image size of JPEG from its binary" (I'm not sure which language), I came up with this procedure:
PROCEDURE p_jpegstats(directory_in IN VARCHAR2,
filename_in IN VARCHAR2,
height_out OUT INTEGER,
width_out OUT INTEGER,
bpc_out OUT INTEGER, -- bits per channel
cps_out OUT INTEGER -- colors per component
) IS
file bfile;
pos INTEGER:=1;
h VARCHAR2(4);
w VARCHAR2(4);
mrkr VARCHAR2(2);
len VARCHAR2(4);
bpc VARCHAR2(2);
cps VARCHAR2(2);
-- Declare a quick helper procedure for readability
PROCEDURE next_byte(buf out varchar2, amt INTEGER:=1) IS
cnt INTEGER;
BEGIN
cnt := amt;
dbms_lob.read(file, cnt, pos, buf);
pos := pos + cnt;
END next_byte;
BEGIN
-- This code is based off of code found here: https://stackoverflow.com/a/48488655/3303651
-- Open the file
file := bfilename(directory_in, filename_in);
dbms_lob.fileopen(file);
-- Init the output variables in case something goes awry.
height_out := NULL;
width_out := NULL;
bpc_out := NULL;
cps_out := NULL;
LOOP
BEGIN
LOOP
next_byte(mrkr);
EXIT WHEN mrkr <> 'FF';
END LOOP;
CONTINUE WHEN mrkr = 'D8'; -- Start of image (SOI)
EXIT WHEN mrkr = 'D9'; -- End of image (EOI)
CONTINUE WHEN mrkr BETWEEN 'D0' AND 'D7';
CONTINUE WHEN mrkr = '01'; -- TEM
next_byte(len, 2);
IF mrkr = 'C0' THEN
next_byte(bpc); -- bits per channel
next_byte(h, 2); -- height
next_byte(w, 2); -- width
next_byte(cps); -- colors per component
EXIT;
END IF;
pos := pos + to_number(len, 'XXXX') - 2;
EXCEPTION WHEN OTHERS THEN EXIT; END;
END LOOP;
-- Write back the values we found
height_out := to_number(h, 'XXXX');
width_out := to_number(w, 'XXXX');
bpc_out := to_number(bpc, 'XX');
cps_out := to_number(cps, 'XX');
-- close out the file
dbms_lob.fileclose(file);
END p_jpegstats;
This will throw an error if the directory is invalid or the file can't be opened. If the outputs are NULL, then there was some other issue.
It's probably not the most efficient or elegant code (I'm not a pro with PL/SQL [yet!]), but it works. Here is an example usage:
DECLARE
h INTEGER;
w INTEGER;
bpc INTEGER;
cps INTEGER;
BEGIN
p_jpegstats('MY_DIR', 'my_image.jpg', h, w, bpc, cps);
DBMS_OUTPUT.PUT_LINE(w || ' x ' || h || ' ' || bpc || ' ' || cps);
END;
/
This ought to return something like
800 x 200 8 3
Edit: Removed unused variable.

Oracle PL/SQL utl_url.escape clob

I have a large BLOB object which I need to convert to CLOB in base64 with encoded characters, I have tried to use utl_encode.base64_encode but it does not escape special characters like ={}+#~ etc. does anyone know how to escape those special characters with utl_url.escape? my code that I have:
PROCEDURE base64encode ( i_blob in blob, io_clob in out nocopy clob) IS
l_step pls_integer := 22500;
l_converted VARCHAR2(32767);
l_buffer_size_approx pls_integer := 1048576;
l_buffer CLOB;
BEGIN
dbms_lob.createtemporary(l_buffer, TRUE, dbms_lob.call);
FOR i IN 0 .. trunc((dbms_lob.getlength(i_blob) - 1 )/l_step)
LOOP
l_converted := utl_raw.cast_to_varchar2(utl_encode.base64_encode(dbms_lob.substr(i_blob, l_step, i * l_step + 1)));
dbms_lob.writeappend(l_buffer, length(l_converted), l_converted);
IF dbms_lob.getlength(l_buffer) >= l_buffer_size_approx THEN
dbms_lob.append(io_clob, l_buffer);
dbms_lob.trim(l_buffer, 0);
END IF;
END LOOP;
dbms_lob.append(io_clob, l_buffer);
dbms_lob.freetemporary(l_buffer);
END base64encode;
and if I try to convert BLOB to base64 it gives me output like:
8J5G7Ty8t3Pn7T+9ce/+w/c//nn/Jd1VDTKhadyLoNKDDx/Cl+4/SHN7jFjSVFrj <- not full base64 just single line,
But I need the output like this, with escaped characters:
8J5G7Ty8t3Pn7T%2B9ce%2F%2Bw%2Fc%2F%2Fnn%2FJd1VDTKhadyLoNKDDx%2FCl%2B4%2FSHN7jFjSVFrj
the result is large CLOB text, it does not fit in varchar2 variable.
APEX_UTIL.URL_ENCODE might do the trick.
SELECT APEX_UTIL.URL_ENCODE('8J5G7Ty8t3Pn7T+9ce/+w/c//nn/Jd1VDTKhadyLoNKDDx/Cl+4/SHN7jFjSVFrj')
FROM dual;
Output:
8J5G7Ty8t3Pn7T%2B9ce%2F%2Bw%2Fc%2F%2Fnn%2FJd1VDTKhadyLoNKDDx%2FCl%2B4%2FSHN7jFjSVFrj
So I have came up with a working solution, this code decodes base64 clob and escapes reserved chars. It is a bit slow, but it does the work. first, you need to call base64encode function from my main question, then pass the clob to this function. I hope it will help others too.
FUNCTION encode64_clob(in_clob CLOB) RETURN CLOB IS
temp_chunk VARCHAR(4000);
l_enter NUMBER := 1;
l_old_ent NUMBER := 1;
l_ent_cnt NUMBER := 1;
l_out CLOB;
BEGIN
LOOP
l_enter := instr(in_clob, chr(10), 1, l_ent_cnt);
IF l_enter = 0 THEN
temp_chunk := regexp_replace(substr(in_clob, l_old_ent, length(in_clob)), '[[:space:]]+', '');
l_out := l_out || temp_chunk;
EXIT;
END IF;
temp_chunk := regexp_replace(substr(in_clob, l_old_ent, l_enter - l_old_ent), '[[:space:]]+', '');
temp_chunk := utl_url.escape(url => temp_chunk, escape_reserved_chars => TRUE, url_charset => 'UTF-8');
l_out := l_out || temp_chunk;
l_ent_cnt := l_ent_cnt + 1;
l_old_ent := l_enter;
END LOOP;
RETURN l_out;
END;

How to call SOAP WS from Oracle DB 11.2 with payload more than 32000 chars?

Colleagues, hello. I need to call SOAP web service from Oracle DB where payload consists of file in base64 encoding. The problem is that i have limitation in payload size - 32 000 chars (varchar type). Examples, here and here. So it means that i can not call web service where payload is quite huge. Have anybody examples where payload increases the limitation of varchar2 type. Than you.
To go over the 32k characters limitation, you can try to use a CLOB with utl_http. Here is a procedure to do it :
PROCEDURE write_clob_to_http_request(pc_clob IN OUT NOCOPY CLOB)
IS
ln_offset NUMBER := 1;
ln_i NUMBER := 1;
ln_amount NUMBER := 32767;
ln_clob_length NUMBER := dbms_lob.getlength(pc_clob);
lv_buffer VARCHAR2(32767);
luh_http_request utl_http.req;
BEGIN
luh_http_request := utl_http.begin_request('http://SoapServiceLocation' , 'POST', 'HTTP/1.1');
utl_http.set_header(luh_http_request, 'Content-Type', lv_http_content_type);
utl_http.set_header(luh_http_request, 'Content-Length', LENGTH(pc_content));
utl_http.set_header(luh_http_request, 'SOAPAction', 'http://SoapServiceLocation');
IF dbms_lob.isopen(pc_clob) != 1 THEN
dbms_lob.open(pc_clob, 0);
END IF;
WHILE ( ln_offset < ln_clob_length )
LOOP
dbms_lob.read(pc_clob, ln_amount, ln_offset, lv_buffer);
UTL_HTTP.write_text(luh_http_request, lv_buffer);
ln_offset := ln_offset + ln_amount;
ln_i := ln_i + 1;
END LOOP;
IF dbms_lob.isopen(pc_clob) = 1 THEN
dbms_lob.close(pc_clob);
END IF;
luh_http_response := utl_http.get_response(luh_http_request);
utl_http.read_text(luh_http_response, pc_content);
utl_http.end_response(luh_http_response);
END write_clob_to_http_request;
You can easily create a CLOB by concatenating VARCHAR2 variables:
pc_content CLOB := TO_CLOB('a string') || TO_CLOB('another string)...;

Error when decoding base 64 to blob

I am using the following function to convert a large base64 encoded file(image or voice) into a blob file and store it in the Oracle database (Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - Production).
I am able to store it and retrieve it but the image is getting corrupted. Only a potion of the image is getting retrieved. I tried using small images(11KB size) and it is working fine. But for larger images(88KB to 700KB) only a portion of the image is retrieved.
The problem is with the base-64 decoding. Earlier I was not able to get even the smaller image due to corruption, but when I increased the buffer size, it came fine. Now the buffer size is at its maximum at 32767 as its the maximum for varchar2 and raw.
Can anyone provide a suitable workaround or solution.
function decode_base64(p_clob_in in clob) return blob is
v_blob blob;
v_result blob;
v_offset integer;
v_buffer_size binary_integer := 32767; -- 24, 48, 3072
v_buffer_varchar varchar2(32767);
v_buffer_raw raw(32767);
begin
if p_clob_in is null then
return null;
end if;
dbms_lob.createtemporary(v_blob, true);
v_offset := 1;
for i in 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size)
loop
dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
v_offset := v_offset + v_buffer_size;
end loop;
v_result := v_blob;
dbms_lob.freetemporary(v_blob);
return v_result;
end decode_base64;
The code that i use to call the function and insert the blob into the table is given below...
PROCEDURE create_notes (
p_task_id IN NUMBER
,p_note_title IN VARCHAR2
,p_note_detail IN VARCHAR2
,p_attach_name IN VARCHAR2
,p_attachment IN CLOB
,p_attach_type IN VARCHAR2
,x_return_code OUT VARCHAR2
,x_return_message OUT VARCHAR2
)
IS
l_blob_data BLOB;
BEGIN
.
.
.
IF p_attachment IS NOT NULL THEN
SELECT incident_id INTO l_pk1_value FROM csf_ct_tasks where task_id = p_task_id;
l_blob_data := xx_utl_base64.decode_base64(p_attachment);
INSERT INTO fnd_lobs
(file_id, file_name, file_content_type, upload_date,
expiration_date, program_name, program_tag, file_data,
LANGUAGE, oracle_charset, file_format
)
VALUES (l_media_id, p_attach_name,p_attach_type, -- 'audio/mpeg','application/pdf','image/jpeg'
SYSDATE,
NULL, 'FNDATTCH', NULL, l_blob_data, --l_blob_data,EMPTY_BLOB ()
'US', 'UTF8', 'binary'
)
RETURNING file_data
INTO x_blob;
COMMIT;
END IF:
Attaching the original picture and its decoded version, below.
I got the below code from net. It worked like a charm. Dont know whats the problem with my old code though.
FUNCTION base64decode(p_clob CLOB)
RETURN BLOB
IS
l_blob BLOB;
l_raw RAW(32767);
l_amt NUMBER := 7700;
l_offset NUMBER := 1;
l_temp VARCHAR2(32767);
BEGIN
BEGIN
DBMS_LOB.createtemporary (l_blob, FALSE, DBMS_LOB.CALL);
LOOP
DBMS_LOB.read(p_clob, l_amt, l_offset, l_temp);
l_offset := l_offset + l_amt;
l_raw := UTL_ENCODE.base64_decode(UTL_RAW.cast_to_raw(l_temp));
DBMS_LOB.append (l_blob, TO_BLOB(l_raw));
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
END;
RETURN l_blob;
END;
I tried your function with a v_buffer_size of 8192 and it worked fine. I've tried several numbers smaller than 32767 and they all worked fine, so try something less than that.
For those who's still looking for a correct solution - you need to decode input data in multiples of 4. In case input contains non-base64 symbols (which are ignored by built-in function utl_encode.base64_decode), it might lead to incorrect results on large files.
I've found a lot of samples on the web which do not correctly decode, posting my code below
FUNCTION base64_decode(p_content CLOB) RETURN BLOB
IS
C_CHUNK_SIZE CONSTANT INTEGER := 12000; -- should be a multiple of 4
C_NON_BASE64_SYM_PATTERN CONSTANT VARCHAR2(20) := '[^A-Za-z0-9+/]';
l_chunk_buf VARCHAR2(12000);
l_chunk_b64_buf RAW(9000);
l_chunk_offset INTEGER := 1;
l_chunk_size INTEGER;
l_res BLOB;
FUNCTION get_next_full_base64_chunk(l_data CLOB, p_cur_pos IN OUT INTEGER, p_desired_size INTEGER, p_cur_size IN OUT INTEGER) RETURN VARCHAR2 IS
l_res VARCHAR2(12000);
l_tail_desired_size INTEGER;
BEGIN
l_res := dbms_lob.substr(l_data, p_desired_size, p_cur_pos);
p_cur_pos := p_cur_pos + p_desired_size;
IF l_res IS NULL THEN
RETURN NULL;
END IF;
l_res := regexp_replace(l_res, C_NON_BASE64_SYM_PATTERN, '');
p_cur_size := p_cur_size + length(l_res);
l_tail_desired_size := 4 - mod(p_cur_size, 4);
IF l_tail_desired_size = 4 THEN
RETURN l_res;
ELSE
RETURN l_res || get_next_full_base64_chunk(l_data, p_cur_pos, l_tail_desired_size, p_cur_size);
END IF;
END;
BEGIN
dbms_lob.createtemporary(l_res, false);
WHILE true
LOOP
l_chunk_size := 0;
l_chunk_buf := get_next_full_base64_chunk(p_content, l_chunk_offset, C_CHUNK_SIZE, l_chunk_size);
EXIT WHEN l_chunk_buf IS NULL;
l_chunk_b64_buf := utl_encode.base64_decode(utl_raw.cast_to_raw(l_chunk_buf));
dbms_lob.writeappend(l_res, utl_raw.length(l_chunk_b64_buf), l_chunk_b64_buf);
END LOOP;
RETURN l_res;
END;

PL/SQL: Procedure not working correctly in a package

I'm working on a package with some procedures in them and I'm running into a bit of trouble. When I try to test the procedure to convert gallons to liters or the other procedures, it just prints out what was declared in the unnamed block instead of converting the numbers. Any ideas?
CREATE OR REPLACE PACKAGE eng_metric
IS
PROCEDURE convert(degree_fahrenheit IN OUT NUMBER,degree_celsius IN OUT NUMBER,measure IN VARCHAR2);
PROCEDURE convert(liters IN OUT NUMBER,gallons IN OUT NUMBER);
END eng_metric;
/
CREATE OR REPLACE PACKAGE BODY eng_metric
AS
PROCEDURE Convert
(degree_fahrenheit IN OUT NUMBER,
degree_celsius IN OUT NUMBER,
measure IN VARCHAR2)
IS
df NUMBER;
dc NUMBER;
convertf NUMBER;
measurecf VARCHAR2(4);
BEGIN
measurecf := measure;
df := degree_fahrenheit;
dc := degree_celsius;
IF measure = 'TEMP' THEN
IF dc = NULL THEN
convertf := ((df - 32) * .56);
degree_fahrenheit := convertf;
dbms_output.Put_line('The temperature in fahrenheit is '
||To_char(degree_fahrenheit));
ELSIF df = NULL THEN
convertf := (dc + 17.98) * 1.8;
degree_celsius := convertf;
END IF;
ELSE
dbms_output.Put_line('Invalid measure');
END IF;
END convert;
PROCEDURE Convert
(liters IN OUT NUMBER,
gallons IN OUT NUMBER)
IS
lit NUMBER;
gal NUMBER;
convertlg NUMBER;
BEGIN
lit := liters;
gal := gallons;
IF gal = NULL THEN
convertlg := (lit / 3.785);
liters := convertlg;
ELSIF lit = NULL THEN
convertlg := (gal * 3.785);
gallons := convertlg;
END IF;
END convert;
END eng_metric;
/
DECLARE
liters NUMBER := 25;
gallons NUMBER := 41;
nully NUMBER := NULL;
BEGIN
eng_metric.Convert(nully,gallons);
dbms_output.Put_line(To_char(gallons));
END;
/
Instead of
IF gal = NULL THEN
you need
IF gal IS NULL
What you have to remember is that NULL means "no value". It NEVER equals or fails to equal anything, including NULL. So you need to use IS NULL or IS NOT NULL, or use the NVL function to change the null to something that has a value.

Resources