Optimize cryptographic signature in Oracle Part 1: Java out, DBMS_CRYPTO in
🎯 Customer request: Can we speed up the cryptographic signature?
✅📉 Sure. Let’s reduce the duration of the signing process by 99.96%.
How? Migrating the cryptographic signature from a Java class in Oracle Database to a PL/SQL function leveraging DBMS_CRYPTO.
Side benefit: Simpler and easier to maintain code, and recovering a lost private key, as part 2 will tell.
What is a cryptographic signature?
Here is a good definition from .Net documentation:
Cryptographic digital signatures use public key algorithms to provide data integrity. When you sign data with a digital signature, someone else can verify the signature, and can prove that the data originated from you and was not altered after you signed it.
In the current context, the company must sign records within a tax declaration using a private key. This declaration is sent to a government agency. The governement agency can then use the public key to confirm the source of the declaration and that the data was unaltered. An existing database PL/SQL job does the signing process and leverages a Java class.
Sample Old Java Code
This Java class is actually stored and executed directly within Oracle. Here is a similar Java code that does that.
import java.nio.charset.Charset;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class SignMessage {
public static String Sign(String message) throws Exception {
// The private key as an array of byte
byte[] privateKeyBytes = new byte[] {
48, -126, 2, 120, ...
};
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// Initialize the Signature object with SHA-1 algorithm
Signature signature = Signature.getInstance("SHA1withRSA");
// Initialize the Signature object with the private key
signature.initSign(privateKey);
// Update the Signature object with the data to be signed
signature.update(stringToByteArray(message));
// Generate the signature
byte[] signatureBytes = signature.sign();
// Convert the signature to Base64 for easy representation
String base64Signature = Base64.getEncoder().encodeToString(signatureBytes);
return base64Signature;
}
public static byte[] stringToByteArray(String myString) throws Exception
{
return myString.getBytes(Charset.forName("UTF-8"));
}
}
Create that Java Class inside Oracle with:
CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED SignMessage AS
import java.nio.charset.Charset;
...
public class SignMessage {
...
}
/
Create a PL/SQL Function to map to the java method in the class:
CREATE OR REPLACE FUNCTION SignMessage(message VARCHAR2)
RETURN VARCHAR2
AS LANGUAGE JAVA
NAME 'SignMessage.Sign(java.lang.String) return java.lang.String';
/
Sample New PL/SQL Code leveraging DBMS_CRYPTO
Since Oracle Database ships with built-in crypto libraries, let’s try to use those and see how it performs.
CREATE OR REPLACE FUNCTION SignMessage(message VARCHAR2) RETURN VARCHAR2 IS
-- The private key
prvkey VARCHAR (2000) := 'MIICXg...==';
kType PLS_INTEGER := DBMS_CRYPTO.KEY_TYPE_RSA;
sType PLS_INTEGER := DBMS_CRYPTO.SIGN_SHA1_RSA;
sign_raw RAW(32767);
BEGIN
-- Sign the message with the private key
sign_raw := DBMS_CRYPTO.SIGN
(
src => UTL_I18N.STRING_TO_RAW(message,'AL32UTF8'),
prv_key => UTL_I18N.STRING_TO_RAW(prvkey, 'AL32UTF8'),
pubkey_alg => kType,
sign_alg => sType
);
-- Remove end of lines characters
RETURN REPLACE(UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(sign_raw)), CHR(13) || CHR(10),'');
END;
/
Result: Response time comparison
Signing 1000 signatures takes :
- Old Java code: 50.97 seconds
- New PL/SQL code: 0.21 seconds
This is a 99.96% improvement in response time.
Followups: Private key
We encountered an unexpected problem: finding the correct private key. More on that in part 2. Coming soon…