私は SQL と Java を初めて使用し、Java でデータを暗号化して送信し、SQL oracle データベースで復号化するプロジェクトに取り組んでいます。私は 3DES を使用しており、暗号化された文字列 (連結) と共に MAC をソルティングして送信しています。Java 側と SQL 側の両方で個別にメッセージをエンコード/デコードできますが、文字列を送信するときに多くの問題が発生し、エンコードに関係があると思います (hex vs base64 vs 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
ジャワ
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 では、通常は HEX で表される RAW を使用します (少なくとも、raw を出力すると 16 進値が出力されるため、そう思います)。だから私の推測では、SQL は HEX で何かを行っており、Java は... 他のことを行っています。同じ行で同じエラーが発生します。
l_enc_val := dbms_crypto.Decrypt (src => l_in, KEY => rawtohash, iv =>
l_iv,
typ
=>
dbms_crypto.des_cbc_pkcs5);
ここに私の新しいコードがあります:
ジャワ
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;