Performing encryption and decryption using the same language might be straight forward and many resources can be found in this regard. But when it comes to across languages, mismatches appear even if you are using the same algorithm to encrypt and decrypt. One of the reasons for this is that each language has its own specification that it supports. The second reason is that each language has different default values for its specification that you need to be aware of. The basic specification that should match in both sides i.e. encryption and decryption are:
- Algorithm
- Secret Key
- Secret Key Size
- Initialization Value (IV)
- Padding
- Mode
Lets take the example of .Net and Oracle. We will use C# for encryption and Pl/SQL for decryption. The C# code for encryption can be found here, and the PL/SQL code for decryption can be found in our previous blog Encryption/Decryption in PL/SQL. Here is a copy of the code:
C# Code
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
namespace EncryptConsoleApp
{
class Program
{
static void Main(string[] args)
{
string text = "Test1234";
string key = "5379075357908764";
byte[] textBytes = new byte[text.Length];
textBytes = ASCIIEncoding.ASCII.GetBytes(text);
byte[] keyBytes = new byte[key.Length];
keyBytes = ASCIIEncoding.ASCII.GetBytes(key);
byte[] encrptedBytes = Encrypt(textBytes, keyBytes);
Console.WriteLine("Encrypted Text: " + ByteArrayToHexString(encrptedBytes));
}
public static byte[] Encrypt(byte[] clearData, byte[] Key)
{
MemoryStream ms = new MemoryStream();
// Create a symmetric algorithm.
TripleDES alg = TripleDES.Create();
alg.Key = Key;
CryptoStream cs = new CryptoStream(ms,alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();
return encryptedData;
}
}
}
PL/SQL Code
create or replace FUNCTION DecryptPassword(EncryptedText IN VARCHAR2,EncKey IN VARCHAR2) RETURN VARCHAR2 IS encdata RAW(2000); BEGIN encdata:=dbms_obfuscation_toolkit.DES3Decrypt(input=>hextoraw(EncryptedText),key=>UTL_RAW.CAST_TO_RAW(EncKey)); return (utl_raw.cast_to_varchar2(encdata)); END DecryptPassword;
If you run the C# code to encrypt Test1234, you will get the following:
Encrypted Text: 5F48C32F78F63971E19764659E3E57F7
Now lets take this output and decrypt it using the PL/SQL code, you should get Test1234 again. Unfortunately, that will not happen. You will see totally different output. Something like ¿ E ¿¿¿(¿u¿¿¿ -¿ or some symbols that are not been displayed in my application.
Now you might be wondering, Why is it so? What went wrong over here? So, lets take a closer look.
The default configurations for the Triple DES algorithm in .Net and Oracle are as follow:
|
|
.Net |
Oracle |
| Mode | CBC | CBC |
| Padding | PKCS7 | Not supported |
| IV in HEX | C992C3154997E0FB | 0123456789ABCDEF |
| Secret Key Size | 192 bits | 192 bits |
As you can notice in the above table, the padding and the IV default setting are different in each framework. Thus the result of encryption and decryption will not match. In order to resolve the mismatch we need to change the configuration in C# code as Oracle 9i does not provide any mean to change these configuration. So we will set the value of IV to be the identical to the IV value in Oracle. But what about the padding? It is not supported at all in Oracle 9i. We can not set it to none as then we will be restricted to specific data length. The alternative solution we can think of is to set one of the padding mode that we can manually remove in Oracle. We will set the padding mode in .Net to ANSIX923 . The ANSIX923 padding string consists of a sequence of bytes filled with zeros before the length. In oracle we will check the last two digits i.e. the length and based on that we will remove all the zeros i.e. the padded part. The following example shows how the ANSIX923 mode works. Given a blocklength of 8, a data length of 9, the number of padding octets equal to 7, and the data equal to FF FF FF FF FF FF FF FF FF:
FF FF FF FF FF FF FF FF FF 00 00 00 00 00 00 07
Below is the modified code.
C# Code
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
namespace EncryptConsoleApp
{
class Program
{
static void Main(string[] args)
{
string text = "Test1234";
string key = "5379075357908764";
string iv = "0123456789ABCDEF";
byte[] textBytes = new byte[text.Length];
textBytes = ASCIIEncoding.ASCII.GetBytes(text);
byte[] keyBytes = new byte[key.Length];
keyBytes = ASCIIEncoding.ASCII.GetBytes(key);
byte[] ivBytes = new byte[iv.Length];
ivBytes = HexStringToByteArray(iv);
byte[] encrptedBytes = Encrypt(textBytes, keyBytes, ivBytes);
Console.WriteLine("Encrpted Text: " + ByteArrayToHexString(encrptedBytes));
}
public static byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)
{
MemoryStream ms = new MemoryStream();
// Create a symmetric algorithm.
TripleDES alg = TripleDES.Create();
alg.Padding = PaddingMode.ANSIX923;
alg.Key = Key;
alg.IV = IV;
CryptoStream cs = new CryptoStream(ms,alg.CreateEncryptor(), CryptoStreamMode.Write);
cs.Write(clearData, 0, clearData.Length);
cs.Close();
byte[] encryptedData = ms.ToArray();
return encryptedData;
}
}
}
PL/SQL Code
create or replace FUNCTION DecryptPassword(EncryptedText IN VARCHAR2,EncKey IN VARCHAR2) RETURN VARCHAR2 IS encdata RAW(2000); numpad NUMBER; result VARCHAR2(100); BEGIN encdata:=dbms_obfuscation_toolkit.DES3Decrypt(input=>hextoraw(EncryptedText),key=>UTL_RAW.CAST_TO_RAW(EncKey)); result :=rawtohex(encdata); numpad:=substr(result,length(result)-2); result:= substr(result,1,length(result)-(numpad*2)); result := hextoraw(result); result := utl_raw.cast_to_varchar2(result); return result; END DecryptPassword;
Now if you encrypt Test1234 using C# you should get the following output:
Encrypted Text: 109F3C4AD99AE1B0899596AB525D5D59
Lets try to decrypt the output using PL/SQL code. The output is
Test1234
Finally, C# code and PL/SQL code matches. In the same lines any other programming language can be used.
Hi
It’s better to put reference link
I’ve found a small bug in your PL/SQL code.
The line:
numpad:=substr(result,length(result)-2);
returns the last three characters, this leed to an error if length of the last block is seven and the last digit of the hex value is != 0.
eg if you encrypt strings like ‘123456#’ or ‘ABCDEFGH123456#’
you can’t decrypt them.
Change line 11 of your listing to:
numpad:=substr(result,length(result)-1);
Then everythink works fine!
Thanks for your excellent example!
Hi,
Due to the difference between Oracle and C# I could not decrypt the data on the file, the only possible way is to change the default setting of the Oracle’s built-in dbms_obfuscation_toolkit that generates the file, then we can decrypt data by using C#;
please help me to find solution. oracle pl/sql code is
CREATE OR REPLACE package body sp_crypt as
Function encrypt(unencrypted_string in varchar2)
return varchar2 Is
ls_in_string varchar2(64) := rpad(unencrypted_string,24,’ ‘);
ls_key_string varchar2(64) := ‘b0st0nrs’;
ls_encrypted_string varchar2(256);
lr_in_string raw(2048);
lr_key raw(2048);
Begin
If (ls_in_string is NULL) Then
return(NULL);
Else
lr_in_string := utl_raw.cast_to_raw(ls_in_string);
lr_key := utl_raw.cast_to_raw(ls_key_string);
dbms_obfuscation_toolkit.DESEncrypt(input => lr_in_string,
key => lr_key,
encrypted_data => ls_encrypted_string);
return(ls_encrypted_string);
End If;
End encrypt;
Function decrypt(encrypted_string in varchar2)
return varchar2 Is
ls_key_string varchar2(64) := ‘b0st0nrs’;
lr_key raw(2048) := utl_raw.cast_to_raw(ls_key_string);
ls_unencrypted_string varchar2(64);
Begin
If (encrypted_string is NULL) Then
return(NULL);
Else
dbms_obfuscation_toolkit.DESDecrypt(input => encrypted_string,
key => lr_key,
decrypted_data => ls_unencrypted_string);
return(rtrim(utl_raw.cast_to_varchar2(ls_unencrypted_string)));
End If;
End decrypt;
End sp_crypt;
/
Thanks
Nitin
Hi Nitin,
We have also been in the same boat as you are right now. We also tried to override the default behaviour of Oracle DBMS_OBFUSCATION_TOOLKIT but unfortunately it is not supported. However, if you were to use the new package DBMS_CRYPTO which is more powerful and advanced, this does give you these facilities(I guess this is in 10g Oracle DB only).
We were encrypting data(in the example above) with C# and decrypting it with PL/SQL. Here C# had PaddingMode.ANSIX923 standard followed. If you read how ANSIX923 padding works with encryption you can easily write your decrypter in PL/SQL.
In your case you are encrypting data with PL/SQL. I would suggest that you pad data as per ANSIX923 standard and use C# decryption to decrypt the data. Would work like a charm. Let me know if it works out.
Hi hansw,
The line:
numpad:=substr(result,length(result)-2);
returns the last 2 characters which represent the number of hex characters that have been padded by the .Net padding method (ANSIX923) which is in our example 07.
The .Net padding method (ANSIX923) always pad the string your are encrypting even if the string was of the specified block size. Thus the code always gives you the correct answer.
Thanks,
Suad
Sorry, but where to get that ByteArrayToHexString function?
As I understand it is the same as rawtohex, as well as HexStringToByteArray same as hextoraw?
I really cant remember now. It was written by another author. I believe when she is available she will respond.
Until then, check out this link
http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa-in-c
Hope it helps!!
I implemented similar. Just wants to make sure it is same as hextoraw/rawtohex.