我是 SQL 和 Java 的新手,正在从事一个项目,用 Java 加密数据,在 SQL oracle 数据库中发送和解密数据。我正在使用 3DES、加盐和发送带有加密字符串的 MAC(连接)。我可以分别在 Java 和 SQL 端对消息进行编码/解码,但是当我发送字符串时遇到了很多麻烦,我认为它与编码有关(十六进制、base64 和 UTF-8)。我目前没有在 SQL 端检查 MAC,但我稍后会更改它。如果你们可以看看我的代码,我真的很感激 :) 哦,很抱歉评论是用德语写的:/
我收到错误:
ORA-28817: PL/SQL function returned an error.
ORA-06512: at "SYS.DBMS_CRYPTO_FFI", line 67
ORA-06512: at "SYS.DBMS_CRYPTO", line 44
ORA-06512: at "SCOTT.PASSWORD", line 272
ORA-06512: at line 9
JAVA
import java.util.Arrays;
import java.util.Random;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import verschlüsseln.FalscheMACOderSaltException;
public static byte[] verschlüsseln(String daten) throws Exception {
// Benötigt: daten, DreifachDES.password, DreifachDES.macString
// Ändert: saltString
// Ausführt: Verschlüsselt "daten," 3DES mit Salt und ein MAC wird
// benutzt.
// hash(DreifachDES.password + salt) ist der Schlüssel.
// Der Output ist ein byte[]
// Erzeugen Digest für Passwort + Salt
password="key12345key54321key15243";
final MessageDigest md = MessageDigest.getInstance("SHA1");
// Erzeugen zufällig 24 Byte Salt
Random züfallig = new SecureRandom();
byte[] salt = new byte[24];
züfallig.nextBytes(salt);
String saltString = Arrays.toString(salt);
// Digest Passwort + Salt um der Schlüssel zu erzeugen
final byte[] digestVonPassword = md.digest((password + saltString)
.getBytes("UTF-8"));
new Base64(true);
String b64Daten = Base64.encodeBase64String(digestVonPassword);
// Wir brauchen nur 24 Bytes, benutze die Erste 24 von der Digest
final byte[] keyBytes = Arrays.copyOf(digestVonPassword, 24);
// Erzeugen der Schlüssel
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
// Erzeugen eine züfallig IV
byte[] ivSeed = new byte[8];
züfallig.nextBytes(ivSeed);
final IvParameterSpec iv = new IvParameterSpec(ivSeed);
// Erzeugen Cipher mit 3DES, CBC und PKCS5Padding
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
// Erzeugen byte[] von String message
final byte[] plainTextBytes = daten.getBytes("UTF-8");
byte[] vorIvCipherText = cipher.doFinal(plainTextBytes);
// Erzeugen die MAC (Message Authentication Code, Mesage
// Authentifizierung Chiffre)
// Später mache ich einmal ein zufällig String, und wir benutzen das
// immer.
SecretKeySpec macSpec = new SecretKeySpec(
(password + saltString).getBytes("UTF-8"), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(macSpec);
byte[] macBytes = mac.doFinal(macString.getBytes());
// Erzeugen byte outputStream um die Arrays zu verbinden
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
// Verbinden IV, Salt, MAC, und verschlüsselt String
ostream.write(cipher.getIV());
ostream.write(salt);
ostream.write(macBytes);
ostream.write(vorIvCipherText);
final byte[] cipherText = ostream.toByteArray();
return cipherText;
}
SQL
FUNCTION Decryptraw (datas VARCHAR2,
c_encrypt_key VARCHAR2)
RETURN VARCHAR2
IS
l_enc_val RAW (4000);
v_receivedsalt RAW(4000);
v_receivedmac RAW(4000);
mac RAW(4000);
macsource RAW(4000);
rawtohash RAW(4000);
l_enc_algo PLS_INTEGER;
l_in RAW (4000);
l_iv RAW (4000);
l_ret VARCHAR2 (4000);
daten RAW(4000);
BEGIN
daten := utl_encode.Base64_decode(utl_raw.cast_to_raw(datas));
--Parse the received string for data required for decryption
--String | IV | Salt | MAC | EncryptedData
--Bytes 8 16 20 The rest
--HexChars 16 32 40 The rest
l_iv := Substr(daten, 1, 16);
v_receivedsalt := Substr(daten, 17, 24);
v_receivedmac := Substr(daten, 41, 40);
l_in := Substr(daten, 81);
--Source of the MAC, the same value is hardcoded in here
--and Decrypt and in the Java Code
macsource := utl_raw.Cast_to_raw('myMacString');
--Concatenate the key (password) and salt so that they can be
--hashed together
rawtohash := Concat(utl_raw.Cast_to_raw(c_encrypt_key), v_receivedsalt);
--Hash the key+salt string using SHA1
--A stronger algorithm is preferred but not currently available in
--oracle SQL
rawtohash := dbms_crypto.Hash(rawtohash, dbms_crypto.hash_sh1);
--Hash the MAC source using hmac_sh1, with the
--password + receivedsalt as the key
mac := dbms_crypto.Mac(src => macsource, KEY => rawtohash, typ =>
dbms_crypto.hmac_sh1);
--Decrypt the data using the hashed password+salt as the key
l_enc_val := dbms_crypto.Decrypt (src => l_in, KEY => rawtohash, iv =>
l_iv,
typ
=>
dbms_crypto.des_cbc_pkcs5);
RETURN utl_raw.Cast_to_varchar2(l_enc_val);
END decryptraw;
编辑1: 我已经找到了几个问题,我想我已经把它缩小到最后一个。我现在正在生成相同的密钥和数据以在 Java 和 SQL 中加密/解密,但是当我为每一个执行最后一步时,我得到了不同的答案。我相信问题是加密的“来源”。我相信这个错误与在 SQL 和 Java 中处理字节的方式有关。在 Java 中,我们使用 byte[] 作为密码,而在 SQL 中,我们使用 RAW,通常以 HEX 表示(至少我认为是这样,因为当我输出 raw 时,它会打印 hex 值)。所以我的猜测是 SQL 正在用 HEX 做一些事情,而 Java 正在做......其他事情。同样的错误发生在同一行:
l_enc_val := dbms_crypto.Decrypt (src => l_in, KEY => rawtohash, iv =>
l_iv,
typ
=>
dbms_crypto.des_cbc_pkcs5);
这是我的新代码:
JAVA
public static byte[] verschlüsseln(String daten) throws Exception {
// Benötigt: daten, DreifachDES.password, DreifachDES.macString
// Ändert: saltString
// Ausführt: Verschlüsselt "daten," 3DES mit Salt und ein MAC wird
// benutzt.
// hash(DreifachDES.password + salt) ist der Schlüssel.
// Der Output ist ein byte[]
// Erzeugen Digest für Passwort + Salt
password="testForNathan";
final MessageDigest md = MessageDigest.getInstance("SHA1");
// Erzeugen zufällig 24 Byte Salt
Random züfallig = new SecureRandom();
byte[] salt = new byte[24];
String saltString = Arrays.toString(salt);
new Base64(true);
saltString = new String(salt, "UTF-8");
byte[] unhashedBytes = (password+saltString).getBytes("UTF-8");
final byte[] keyBytes2 = unhashedBytes;
System.out.println("Hex key before hash: " + bytesToHex(unhashedBytes));
//Hash the pw+salt
byte[] digestVonPassword = md.digest(keyBytes2);
byte[] digestVonPassword2 = new byte[digestVonPassword.length + salt.length];
System.arraycopy(digestVonPassword, 0, digestVonPassword2, 0, digestVonPassword.length);
System.arraycopy(salt, 0, digestVonPassword2, digestVonPassword.length, salt.length);
// Wir brauchen nur 24 Bytes, benutze die Erste 24 von der Digest
final byte[] keyBytes = Arrays.copyOf(digestVonPassword2, 24);
// Erzeugen der Schlüssel
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
// Erzeugen eine züfallig IV
byte[] ivSeed = new byte[8];
final IvParameterSpec iv = new IvParameterSpec(ivSeed);
// Erzeugen Cipher mit 3DES, CBC und PKCS5Padding
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
// Erzeugen byte[] von String message
final byte[] plainTextBytes = daten.getBytes("UTF-8");
byte[] vorIvCipherText = cipher.doFinal(plainTextBytes);
// Erzeugen die MAC (Message Authentication Code, Mesage
// Authentifizierung Chiffre)
// Später mache ich einmal ein zufällig String, und wir benutzen das
// immer.
SecretKeySpec macSpec = new SecretKeySpec(
keyBytes2, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(macSpec);
byte[] macBytes = mac.doFinal(macString.getBytes());
System.out.println("Hex version of MAC: " + bytesToHex(macBytes));
// Erzeugen byte outputStream um die Arrays zu verbinden
ByteArrayOutputStream ostream = new ByteArrayOutputStream();
// Verbinden IV, Salt, MAC, und verschlüsselt String
ostream.write(cipher.getIV());
ostream.write(salt);
ostream.write(macBytes);
ostream.write(vorIvCipherText);
final byte[] cipherText = ostream.toByteArray();
return cipherText;
}
SQL
FUNCTION Decryptraw (datas VARCHAR2,
c_encrypt_key VARCHAR2)
RETURN VARCHAR2
IS
l_enc_val RAW (4000);
v_receivedsalt RAW(4000);
v_receivedmac RAW(4000);
mac RAW(4000);
macsource RAW(4000);
rawtohash RAW(4000);
l_enc_algo PLS_INTEGER;
l_in RAW (4000);
l_iv RAW (4000);
l_ret VARCHAR2 (4000);
daten RAW(4000);
BEGIN
daten := utl_encode.Base64_decode(utl_raw.cast_to_raw(datas));
l_iv := Substr(daten, 1, 16);
v_receivedsalt := Substr(daten, 17, 48);
v_receivedmac := Substr(daten, 65, 40);
l_in := Substr(daten, 105);
--Source of the MAC, the same value is hardcoded in here
--and Decrypt and in the Java Code
macsource := utl_raw.Cast_to_raw('myMacString');
--Concatenate the key (password) and salt so that they can be
--hashed together
rawtohash := Concat(utl_raw.Cast_to_raw(c_encrypt_key), v_receivedsalt);
--Hash the key+salt string using SHA1
--A stronger algorithm is preferred but not currently available in
--oracle SQL
rawtohash := dbms_crypto.Hash(rawtohash, dbms_crypto.hash_sh1);
rawToHash := utl_raw.concat(rawtohash, v_receivedsalt);
rawtohash := utl_raw.substr(rawtohash, 1, 24);
--Hash the MAC source using hmac_sh1, with the
--password + receivedsalt as the key
mac := dbms_crypto.Mac(src => macsource, KEY => rawtohash, typ =>
dbms_crypto.hmac_sh1);
l_enc_val := dbms_crypto.Decrypt (src => l_in, KEY => rawtohash, iv =>
l_iv,
typ
=>
dbms_crypto.des_cbc_pkcs5);
RETURN utl_raw.Cast_to_varchar2(l_enc_val);
END decryptraw;