有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用3DES在Java中加密,在SQL Oracle中解密

我不熟悉SQL和Java,正在做一个用Java加密数据、发送数据并在SQL oracle数据库中解密的项目。我使用的是3DES,用盐腌制并发送一个带有加密字符串的MAC(连接)。我可以分别在Java和SQL端对消息进行en/decode,但当我发送字符串时遇到了很多麻烦,我认为这与编码有关(十六进制、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

爪哇

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中,我们使用字节[]作为密码,在SQL中,我们使用通常用十六进制表示的原始值(至少我这么认为,因为当我输出原始值时,它会打印十六进制值)。所以我猜SQL正在用十六进制做一些事情,而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;

共 (1) 个答案

  1. # 1 楼答案

    都修好了!代码现在也更容易阅读了

    警告:无论出于什么原因,这都不会解密大字符串(在我的测试中失败,有1800个字符),它会抛出一个错误

    爪哇

    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;