1

私は 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;
4

1 に答える 1

0

すべて直しました!コードも読みやすくなりました。

警告のメモ: これは何らかの理由で大きな文字列を復号化せず (私のテストでは 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;
于 2013-06-11T13:48:38.467 に答える