Secure Password Hashing in Oracle PL/SQL using HMAC-SHA512


1. Introduction
Password hashing is a critical part of building secure applications. In Oracle environments where advanced hashing functions like PKCS5_PBKDF2
are not readily available, it becomes essential to implement custom cryptographic solutions using native PL/SQL tools.
In this post, we introduce a custom PL/SQL package called irap_crypto_pkg
, designed to provide secure password hashing using HMAC-SHA512
, dynamic salts, and a repeatable hashing method as a practical alternative to PBKDF2.
2. Package Specification: irap_crypto_pkg
Below is the specification of the package, outlining its available functions:
CREATE OR REPLACE PACKAGE irap_crypto_pkg AS
-- Generate a random salt
FUNCTION generate_salt RETURN VARCHAR2;
-- Create a password hash using HMAC-SHA512 and salt
FUNCTION generate_hash(
p_password IN VARCHAR2,
p_salt IN VARCHAR2
) RETURN VARCHAR2;
-- Alternative implementation of PBKDF2 using repeated HMAC
FUNCTION pbkdf2_alternative(
p_password IN VARCHAR2,
p_salt IN VARCHAR2,
p_iterations IN NUMBER DEFAULT 10000,
p_key_length IN NUMBER DEFAULT 32
) RETURN VARCHAR2;
-- Validate hash strength
FUNCTION is_hash_strong_enough(
p_hash IN VARCHAR2
) RETURN BOOLEAN;
END irap_crypto_pkg;
/
3. Package Body Implementation
The body uses built-in Oracle packages such as DBMS_CRYPTO
, UTL_I18N
, and RAWTOHEX
for cryptographic operations.
CREATE OR REPLACE PACKAGE BODY irap_crypto_pkg AS
FUNCTION generate_salt RETURN VARCHAR2 IS
v_salt VARCHAR2(255);
BEGIN
v_salt := RAWTOHEX(SYS.DBMS_CRYPTO.RANDOMBYTES(32));
RETURN v_salt;
END generate_salt;
FUNCTION generate_hash(
p_password IN VARCHAR2,
p_salt IN VARCHAR2
) RETURN VARCHAR2 IS
v_result RAW(4000);
v_iterations NUMBER := 10000;
BEGIN
v_result := SYS.DBMS_CRYPTO.MAC(
src => UTL_I18N.STRING_TO_RAW(p_salt || p_password, 'AL32UTF8'),
typ => SYS.DBMS_CRYPTO.HMAC_SH512,
key => UTL_I18N.STRING_TO_RAW(p_salt, 'AL32UTF8')
);
FOR i IN 1 .. v_iterations - 1 LOOP
v_result := SYS.DBMS_CRYPTO.MAC(
src => v_result,
typ => SYS.DBMS_CRYPTO.HMAC_SH512,
key => UTL_I18N.STRING_TO_RAW(p_salt, 'AL32UTF8')
);
END LOOP;
RETURN RAWTOHEX(v_result);
END generate_hash;
FUNCTION pbkdf2_alternative(
p_password IN VARCHAR2,
p_salt IN VARCHAR2,
p_iterations IN NUMBER DEFAULT 10000,
p_key_length IN NUMBER DEFAULT 32
) RETURN VARCHAR2 IS
v_result RAW(4000);
BEGIN
v_result := SYS.DBMS_CRYPTO.MAC(
src => UTL_I18N.STRING_TO_RAW(p_salt || '1', 'AL32UTF8'),
typ => SYS.DBMS_CRYPTO.HMAC_SH512,
key => UTL_I18N.STRING_TO_RAW(p_password, 'AL32UTF8')
);
FOR i IN 2 .. p_iterations LOOP
v_result := SYS.DBMS_CRYPTO.MAC(
src => v_result,
typ => SYS.DBMS_CRYPTO.HMAC_SH512,
key => UTL_I18N.STRING_TO_RAW(p_password, 'AL32UTF8')
);
END LOOP;
IF p_key_length < 64 THEN
v_result := UTL_RAW.SUBSTR(v_result, 1, p_key_length);
END IF;
RETURN RAWTOHEX(v_result);
END pbkdf2_alternative;
FUNCTION is_hash_strong_enough(
p_hash IN VARCHAR2
) RETURN BOOLEAN IS
BEGIN
RETURN LENGTH(p_hash) >= 64;
END is_hash_strong_enough;
END irap_crypto_pkg;
/
4. Function Descriptions
generate_salt
Purpose: Generates a 32-byte random salt.
Usage: Helps prevent rainbow table attacks.
Example:
DECLARE
v_salt VARCHAR2(255);
BEGIN
v_salt := irap_crypto_pkg.generate_salt();
DBMS_OUTPUT.PUT_LINE(v_salt);
END;
generate_hash
Purpose: Hashes a password using HMAC-SHA512 and salt, repeated 10,000 times.
Advantage: Computationally expensive to slow down brute-force attempts.
Example:
DECLARE
v_hash VARCHAR2(4000);
BEGIN
v_hash := irap_crypto_pkg.generate_hash('MyPassword123', 'ABC123SALT...');
DBMS_OUTPUT.PUT_LINE(v_hash);
END;
pbkdf2_alternative
Purpose: Custom implementation mimicking PBKDF2.
Parameters:
p_iterations
: Number of HMAC iterations (default: 10,000)p_key_length
: Desired key length in bytes (default: 32)
Example:
DECLARE
v_pbkdf2_hash VARCHAR2(4000);
BEGIN
v_pbkdf2_hash := irap_crypto_pkg.pbkdf2_alternative(
p_password => 'StrongPass!',
p_salt => 'F7A1B2C3D4...',
p_iterations => 15000,
p_key_length => 48
);
DBMS_OUTPUT.PUT_LINE(v_pbkdf2_hash);
END;
is_hash_strong_enough
Purpose: Verifies if a given hash meets the minimum required strength.
Example:
DECLARE
v_ok BOOLEAN;
BEGIN
v_ok := irap_crypto_pkg.is_hash_strong_enough('ABCDEF...');
IF v_ok THEN
DBMS_OUTPUT.PUT_LINE('Hash is strong.');
ELSE
DBMS_OUTPUT.PUT_LINE('Hash is weak!');
END IF;
END;
5. Security Best Practices
Recommendation | Description |
Use Salt | Always generate unique salts per user/password. |
Repeat the Hashing | Increases resistance against brute-force attacks. |
Avoid MD5/SHA1 | These algorithms are obsolete and insecure. |
Validate Hash Length | Ensure minimum strength before storage. |
Tune Iterations | Use higher iterations (e.g., 100,000) for high-security systems. |
6. Limitations & Future Improvements
This version is not a full RFC-compliant PBKDF2 implementation.
However, it offers practical cryptographic strength using native PL/SQL.
In newer Oracle versions, consider using
PKCS5_PBKDF2
,ARGON2
, orSCRYPT
.For multi-block key derivation, extend this implementation to handle block chaining.
7. Conclusion
The irap_crypto_pkg
package provides a secure, modular, and practical solution for password hashing in Oracle PL/SQL—especially in environments where advanced cryptographic functions are unavailable.
Subscribe to my newsletter
Read articles from Mahdi Ahmadi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Mahdi Ahmadi
Mahdi Ahmadi
Founder & CEO at Artabit | Oracle APEX Expert | Building Innovative HR Solutions | UAE & Iran