我是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
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时会打印十六进制值)。所以我的猜测是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;
答案 0 :(得分:0)
全部修好了!代码现在也更容易阅读。
警告提示:无论出于何种原因,这都不会解密大字符串(我的测试中有1800个字符失败),它会抛出错误。
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 String verschluesselnZuBase64String(String daten) throws Exception{
String b64Daten;
byte[] datenArray = verschlüsseln(daten);
new Base64(true);
b64Daten = Base64.encodeBase64String(datenArray);
return b64Daten;
}
public static String bytesToHex(byte[] bytes) {
final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
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="pw";
macstring="mac";
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);
ByteArrayOutputStream pwsalt = new ByteArrayOutputStream();
pwsalt.write(password.getBytes("UTF-8"));
pwsalt.write(salt);
byte[] unhashedBytes = pwsalt.toByteArray();
//Hash the pw+salt
byte[] digestVonPassword = md.digest(unhashedBytes);
//SHA1 only generates 20 bytes and we need more, so concatenate the salt onto the end.
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);
// 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(
digestVonPassword, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(macSpec);
byte[] macBytes = mac.doFinal(macString.getBytes());
// Wir brauchen nur 24 Bytes, benutze die Erste 24 von der Digest
final byte[] keyBytes = Arrays.copyOf(digestVonPassword2, 24);
// Erzeugen eine züfallig IV
byte[] ivSeed = new byte[8];
züfallig.nextBytes(ivSeed);
final IvParameterSpec iv = new IvParameterSpec(ivSeed);
// Erzeugen der Schlüssel
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
// 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 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(c_encypy_key是密码)
FUNCTION Decryptraw (datas VARCHAR2,
c_encrypt_key VARCHAR2)
RETURN VARCHAR2
IS
l_enc_val RAW (8000);
v_receivedsalt RAW(8000);
v_receivedmac RAW(8000);
mac RAW(8000);
macsource RAW(8000);
mackey RAW(8000);
rawtohash RAW(8000);
l_enc_algo PLS_INTEGER;
l_in RAW (8000);
l_iv RAW (8000);
l_ret VARCHAR2 (8000);
daten RAW(8000);
BEGIN
daten := utl_encode.Base64_decode(utl_raw.Cast_to_raw(datas));
macSource := utl_raw.cast_to_raw('mac');
--Parse the received string for data required for decryption
--String | IV | Salt | MAC | EncryptedData
--Bytes 8 24 20 The rest
--HexChars 16 48 40 The rest
l_iv := Substr(daten, 1, 16);
v_receivedsalt := Substr(daten, 17, 48);
v_receivedmac := Substr(daten, 65, 40);
l_in := Substr(daten, 105);
--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);
macKey := rawtohash;
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 => macKey, typ =>
dbms_crypto.hmac_sh1);
--In the case that the MAC generated in this function
--does not match the MAC parsed from the received data,
--something has gone wrong during the data sending process.
--The data will not be decrypted or parsed, because it has
--most likely been tampered with or corrupted.
IF ( mac != v_receivedmac ) THEN
Raise_application_error(-20101,
'Recieved MAC or Salt don''t match the generated MAC.'
);
END IF;
--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.des3_cbc_pkcs5);
RETURN utl_raw.Cast_to_varchar2(l_enc_val);
END decryptraw;