sábado, 22 de junio de 2013

Simplify Encryption with DB2 Field Procedures












Datos considerados a encriptar
  • Todo lo que se considera confidencial para las organizaciones, sus empleados y sus clientes
  • Numero de tarjetas de crédito
  • Información personal
  • Información de los salarios
  • Información sobre la salud
  • Número de cuentas bancarias
  • Número de licencia de conducir
  • Datos financieros


  1. La encriptación es el proceso de transformar un texto plano (plain text) en un texto cifrado (cipher text)
  2. La encriptación esconde el verdadero significado del mensaje pero no el mensaje en sí
  3. AES es uno de los algoritmos más populares usados en criptografía simétrica




Los servicios de seguridad siempre se presentan como la mayor amenazay han crecido de forma importante en los recientes años. Las empresas deben cumplir con las regulaciones de la  industria y con el gobierno para asegurar la integridad de la información como los mencionados anteriormente. Para tal propósito la encriptación de datos es indispensable.

En DB2 para i 7.1 provee conocida como field procedure para enviar 

La versión i 7.1 de DB2  ofrece una tecnología que se conoce como field procedure (FieldProc) para realizar implementaciones de cifrado transparente a nivel de columna. Mediante el uso de la cláusula de FIELDPROC de CREATE TABLE y ALTER TABLE, se puede registrar un procedimiento de campo para una columna. 

Un field procedure es una rutina de salida escrito por el usuario diseñado para transformar los valores en una sola columna. Cuando se cambian los valores de la columna, o se insertan nuevos valores, el field procedure se invoca para cada valor de y puede transformar ese valor (codificar) en cualquier forma, y luego lo almacena. 

Cuando los valores se recuperan de la columna, el field procedure se invoca para cada valor codificado, que entonces decodifica de nuevo al valor original. Cualquier índices definidos en una columna no derivada que utiliza un procedimiento de campo se construyen con valores codificados.








  • Asegure cifrado: Los datos, índices y revistas almacenadas en discos duros o cintas son una versión transformada (encriptada) de los datos. No se pueden obtener los datos descifrados sin el programa FieldProc.



  • Uso fácil : Para activar el field procedure, sólo tiene que escribir un programa FieldProc (o llamar a funciones de cifrado / descifrado de terceros) y luego registrarlo en la tabla.

  • Más flexibilidad :Se puede cambiar fácilmente a otros métodos de cifrado mediante la adición, dejar caer o moverse a otros programas FieldProc. Tenga en cuenta que a pesar de que un valor codificado puede ser más largo que el valor definido del campo, que es flexible y se puede cambiar con los programas FieldProc. No se necesita ningún cambio en la definición de la tabla inicial.




Si bien los field procedures permiten implementar el cifrado y descifrado de forma transparente y sin cambios en las aplicaciones, esta flexibilidad tiene un equilibrio de rendimiento. El registro de programas de procedimiento implica una sobrecarga de campo similar a la de una llamada de programa externo para cada interfaz que escribe o lee los valores de esa columna. Las operaciones realizadas por el propio programa de field procedure también será un factor en la sobrecarga de rendimiento. Por ejemplo, el cifrado y descifrado de procesamiento son conocidos por la cantidad de ciclos de CPU que ellos demandan. Por lo tanto, las pruebas de rendimiento debe ser una parte fundamental de la implementación del field procedure.




Como desarrollador de aplicaciones, sólo se tiene que utilizar la cláusula FIELDPROC para registrar el procedimiento de campo a una tabla. En el siguiente ejemplo, se crea una tabla de empleados con datos sensibles (salario) que está codificada por un procedimiento de campo:


                Create TABLE employee 
                ( 
                   ID char(10),
                   salary decimal(10,2) FieldProc FP.userpgm
                )

O una tabla de empleados existente puede ser modificado para encriptar los datos sensibles de salario columna:

               ALTER TABLE employee alter column salary set FieldProc FP.userpgm
                  //We can alter to add a FieldProc on both SQL table and Native table.


Por supuesto, el procedimiento de campo también se puede quitar, y se puede utilizar otro procedimiento de campo para encriptar los datos sensibles:

                ALTER TABLE FP.userpgm alter column salary drop FieldProc   
                              // drop the old field procedure
                ALTER TABLE employee alter column salary set FieldProc FP.userpgm2    
                             //then add the new one              





El field procedure que se especifica para una columna se invoca en tres situaciones generales:


  • En el caso del campo de definición, cuando se ejecuta la sentencia CREATE TABLE o ALTER TABLE que da nombre al procedimiento. Durante esta invocación, se prevé que el procedimiento para determinar la validez de los tipos de datos y atributos de la columna, verifique la lista literal, y proporcionar la descripción del campo de la columna.

  • En el caso de codificación de campo, cuando un valor de columna es para ser codificada en campo. Esto ocurre para cualquier valor que se inserta en un SQL INSERT o MERGE, para una escritura nativa, se cambia por actualización o se copian los datos a una columna de destino con un field procedure registrado.



  • En el caso de campo-decodificación, cuando un valor almacenado es ser campo-decodificado de nuevo a su valor original. Esto ocurre para cualquier valor que se recupera de una SQL SELECT o FETCH declaración, por una lectura nativa, o los datos se copian a una columna de destino sin un procedimiento de campo registrados.


Tres ejemplos de código de implementación del programa FieldProc.


Este ejemplo muestra cómo utilizar programas de cifrado / descifrado, o funciones de cifrado / descifrado de terceros para realizar el cifrado. El programa de cifra y descifra una cadena de caracteres 16 que sólo incluye números.






    #include <ctype.h> 
    #include <string.h> 
    #include <stdlib.h> 
    #include <SQLFP.h>
    
    void DESC(unsigned char *data,                         
                     unsigned char *mkey, char ctag[2])
    {
      ... //user created or third party encryption algorithm. 
    }
    
    main(int argc, void *argv[])                                           
    {                                                                      
      short *funccode = argv[1];                                           
      sqlfpFieldProcedureParameterList_T *optionalParms = argv[2];         
      char *sqlstate = argv[7];                                            
      sqlfpMessageText_T *msgtext = argv[8];                               
      sqlfpOptionalParameterValueDescriptor_T *optionalParmPtr;            
      char KEY[16]="0123456789ABCDEF";                                 
                                                                           
      if (*funccode == 8) /* create time */                                
      {                                                                    
        sqlfpParameterDescription_T *inDataType = argv[3];                 
        sqlfpParameterDescription_T *outDataType = argv[5];                
        if (inDataType->sqlfpSqlType !=452 &&                         
          inDataType->sqlfpSqlType !=453 )  /* only support fixed length */
          memcpy(sqlstate,"38002",5); 
    
       /* do something here to determine the result data type */          
       /* ..... */                                                        
       /* in this example input and output types are exactly the same */  
       /* so just copy */                                                 
       memcpy(outDataType, inDataType, sizeof(sqlfpParameterDescription_T));
      }                                                                    
      else if (*funccode == 0) /* encode */                                
      {                                                                    
        memcpy((char *)argv[6], (char *)argv[4], 16);              
        DESC((char*)argv[6], KEY, "0");                                
      }                                                                    
      else if (*funccode == 4) /* decode */                                
      {                                                                    
        memcpy((char *)argv[4], (char *)argv[6], 16);              
        memcpy((char *)argv[4], (char *)argv[6], 16);
        DESC((char *)argv[4], KEY, "1");                 
      }                                                      
      else /* unsupported option -- error */                 
        memcpy(sqlstate, "38003",5);                         
    }
    
    


    Este ejemplo muestra cómo utilizar IBM i APIs para cifrar y descifrar. El programa puede ser utilizado para cifrar y descifrar una longitud fija char, varchar o CLOB.


    <!-- In this example, there are some points which need attention: -->
    <!-- a. The input/output data type could be a fixed char or varchar or clob. -->
    <!--    But the block size of the encryption should be 16x(multiple time of 16).-->
    <!--    So, we should define enough space to store the encryption result. -->
    <!-- b. Avoid using pad option. Pad option will add a count of the pad characters -->
    <!--    in the end of original string.-->
    <!--    It may conduct one more block of the encryption result and the -->
    <!--    column does not have enough space to store. -->
    <!-- c. For better performance, ACTGRP(*CALLER), TERASPACE(*YES) and STGMDL(*INHERIT)-->
    <!--    were recommended when you compile your program.-->
    <!-- d. More information about IBM i Cryptographic Services APIs,  -->
    <!--    please refer to the link of the IBM i infocenter below:-->
    <!--    http://publib.boulder.ibm.com/infocenter/iseries/7.1m0/index.jsp -->
    #include <string.h> 
    #include <stdlib.h> 
    #include <stdio.h> 
    #include <QC3CCI.H>
    #include <QUSEC.H> 
    #include <QC3DTAEN.H> 
    #include <QC3DTADE.H> 
    #include <SQLFP.h>
    
    <!-- ---------------------  -->
    <!-- SQL data type define.  -->
    <!-- ---------------------  -->
    <!-- SQL data type CLOB  -->
    #define SQL_CLOB_1                 408
    #define SQL_CLOB_2                 409
    <!-- SQL data type VARCHAR  -->
    #define SQL_VARCHAR_1              448
    #define SQL_VARCHAR_2              449
    <!-- SQL data type CHAR  -->
    #define SQL_CHAR_1                 452
    #define SQL_CHAR_2                 453
    
    <!-- ------------------------------  -->
    <!-- Varlength SQL data type define. -->
    <!-- ------------------------------  -->
    
    typedef _Packed struct
    {
        unsigned short int len;
        char data[512];
    }T_VARCHAR;
    
    typedef _Packed struct
    {
        unsigned long len;
        char data[512];
    }T_CLOB;
    
    Qc3_Format_ALGD0200_T *ALGD0200;
    Qc3_Format_KEYD0200_T *KEYD0200;
    Qus_EC_t ERRCODE;
    
    main(int argc, void *argv[])
    {
      T_VARCHAR VarCharStr;
      T_CLOB ClobStr;
      short *funccode = argv[1];
      sqlfpFieldProcedureParameterList_T *optionalParms = argv[2];
      char *sqlstate = argv[7];
      sqlfpMessageText_T *msgtext = argv[8];
      sqlfpOptionalParameterValueDescriptor_T *optionalParmPtr;
      char Clear_Data[512];
      char Encrypted_Data[512];
      char Decrypted_Data[512];
      int InputDataLen;
      int EncryptedDataLen;
      int DecryptedDataLen;
      int RtnLen;
      int i;
      char Qc3_Any_CSP_Flag = Qc3_Any_CSP;
      ALGD0200 = (Qc3_Format_ALGD0200_T *)malloc(sizeof(Qc3_Format_ALGD0200_T));
      ALGD0200->Block_Cipher_Alg = Qc3_AES;
      ALGD0200->Block_Length = 16;
      ALGD0200->Mode = Qc3_ECB;
      ALGD0200->Pad_Option = Qc3_No_Pad;
      ALGD0200->Pad_Character = '\x00' ;
      ALGD0200->MAC_Length = 0;
      ALGD0200->Effective_Key_Size = 0;
      ALGD0200->Reserved = '\x00';
      memset(ALGD0200->Init_Vector,'\x00',32);
    
      KEYD0200 = (Qc3_Format_KEYD0200_T *)malloc(sizeof(Qc3_Format_KEYD0200_T) +16);
      KEYD0200->Key_Type = Qc3_AES ;
      KEYD0200->Key_String_Len = 16;
      KEYD0200->Key_Format = Qc3_Bin_String;
      memcpy((char *)KEYD0200->Reserved + 3, "0123456789ABCDEF", 16);
    
       if (*funccode == 8) /* create time */
      {
        sqlfpParameterDescription_T *inDataType = argv[3];
        sqlfpParameterDescription_T *outDataType = argv[5];
        /* do something here to determine the result data type */
        /* ..... */
        /* in this example input and output types are exactly the same */
        /* so just copy */
        memcpy(outDataType, inDataType, sizeof(sqlfpParameterDescription_T));
      }
      else if (*funccode == 0) /* encode */
      {
        sqlfpParameterDescription_T *inDataType = argv[3];
        InputDataLen = inDataType->sqlfpByteLength;
        if (inDataType->sqlfpSqlType == SQL_VARCHAR_1 ||
            inDataType->sqlfpSqlType == SQL_VARCHAR_2)
        {
          memcpy((char *)&VarCharStr, (char *)argv[4], InputDataLen+2);
          InputDataLen = VarCharStr.len;
          memcpy((char *)Clear_Data, (char *)VarCharStr.data, InputDataLen);
          if (InputDataLen % 16 > 0 || InputDataLen == 0)
          {
            memset((char *)Clear_Data + InputDataLen, '\x00',
              16 - InputDataLen % 16);
            InputDataLen = ((int)(InputDataLen / 16) + 1) * 16;
           }
        }
        else if (inDataType->sqlfpSqlType == SQL_CLOB_1 ||
                 inDataType->sqlfpSqlType == SQL_CLOB_2)
        {
          memcpy((char *)&ClobStr, (char *)argv[4], InputDataLen+4);
          InputDataLen = ClobStr.len;
          memcpy((char *)Clear_Data, (char *)ClobStr.data, InputDataLen);
          if (InputDataLen % 16 > 0 || InputDataLen == 0)
          {
            memset((char *)Clear_Data + InputDataLen, '\x00',
              16 - InputDataLen % 16);
            InputDataLen = ((int)(InputDataLen / 16) + 1) * 16;
           }
        }
        else
          memcpy((char *)Clear_Data, (char *)argv[4], InputDataLen);
        memset(Encrypted_Data,'\x00',sizeof(Encrypted_Data));
        EncryptedDataLen = sizeof(Encrypted_Data);
        Qc3EncryptData(Clear_Data,
                       &InputDataLen,
                       Qc3_Data,
                       (char *)ALGD0200,
                       Qc3_Alg_Block_Cipher,
                       (char *)KEYD0200,
                       Qc3_Key_Parms,
                       &Qc3_Any_CSP_Flag,
                       "          ",
                       Encrypted_Data,
                       &EncryptedDataLen,
                       &RtnLen,
                       &ERRCODE);
        if (inDataType->sqlfpSqlType == SQL_VARCHAR_1 ||
            inDataType->sqlfpSqlType == SQL_VARCHAR_2)
        {
          VarCharStr.len = RtnLen;
          memcpy((char *)VarCharStr.data, (char *)Encrypted_Data, RtnLen);
          memcpy((char *)argv[6], (char *)&VarCharStr, RtnLen+2);
        }
        else if (inDataType->sqlfpSqlType == SQL_CLOB_1 ||
                 inDataType->sqlfpSqlType == SQL_CLOB_2)
        {
          ClobStr.len = RtnLen;
          memcpy((char *)ClobStr.data, (char *)Encrypted_Data, RtnLen);
          memcpy((char *)argv[6], (char *)&ClobStr, RtnLen+4);
        }
        else
          memcpy((char *)argv[6], (char *)Encrypted_Data, RtnLen);
      }
      else if (*funccode == 4) /* decode */
      {
        sqlfpParameterDescription_T *inDataType = argv[3];
        InputDataLen = inDataType->sqlfpByteLength;
        if (inDataType->sqlfpSqlType == SQL_VARCHAR_1 ||
            inDataType->sqlfpSqlType == SQL_VARCHAR_2)
        {
          memcpy((char *)&VarCharStr, (char *)argv[6], InputDataLen+2);
          InputDataLen = VarCharStr.len;
          memcpy((char *)Encrypted_Data, (char *)VarCharStr.data, InputDataLen);
        }
        else if (inDataType->sqlfpSqlType == SQL_CLOB_1 ||
                 inDataType->sqlfpSqlType == SQL_CLOB_2)
        {
          memcpy((char *)&ClobStr, (char *)argv[6], InputDataLen+4);
          InputDataLen = ClobStr.len;
          memcpy((char *)Encrypted_Data, (char *)ClobStr.data, InputDataLen);
        }
        else
          memcpy((char *)Encrypted_Data, (char *)argv[6], InputDataLen);
        memset(Decrypted_Data,'\x00',sizeof(Decrypted_Data));
        DecryptedDataLen = sizeof(Decrypted_Data);
        Qc3DecryptData(Encrypted_Data,
                       &InputDataLen,
                       (char *)ALGD0200,
                       Qc3_Alg_Block_Cipher,
                       (char *)KEYD0200,
                       Qc3_Key_Parms,
                       &Qc3_Any_CSP_Flag,
                       "          ",
                       Decrypted_Data,
                       &DecryptedDataLen,
                       &RtnLen,
                       &ERRCODE);
        if (inDataType->sqlfpSqlType == SQL_VARCHAR_1 ||
            inDataType->sqlfpSqlType == SQL_VARCHAR_2)
        {
          VarCharStr.len = strlen(Decrypted_Data);
          memcpy((char *)VarCharStr.data, (char *)Decrypted_Data, VarCharStr.len);
          memcpy((char *)argv[4], (char *)&VarCharStr, VarCharStr.len+2);
        }
        else if (inDataType->sqlfpSqlType == SQL_CLOB_1 ||
                 inDataType->sqlfpSqlType == SQL_CLOB_2)
        {
          ClobStr.len = strlen(Decrypted_Data);
          memcpy((char *)ClobStr.data, (char *)Decrypted_Data, ClobStr.len);
          memcpy((char *)argv[4], (char *)&ClobStr, ClobStr.len+4);
        }
        else
          memcpy((char *)argv[4], (char *)Decrypted_Data, RtnLen);
      }
      else /* unsupported option -- error */
        memcpy(sqlstate, "38003",5);
    }
    
    



    Esta muestra es una versión RPG de ejemplo de código de FieldProc. Invierte los personajes de un globo o una columna varchar como método de cifrado.

         D FuncCode        S              2B 0
         D p_FuncCode      S               *
         D OptParms        DS                  LikeDs(SQLFOPVD)
         D*
         D EnCodTyp        DS                  LikeDs(SQLFPD)
         D*
         D DeCodTyp        DS                  LikeDs(SQLFPD)
         D*
         D EnCodDta        S            512
         D DeCodDta        S            512
         D*
         D SqlState        S              5
         D SqMsgTxt        DS                  LikeDs(SQLFMT)
         D*
         D En_Lob_Ds       Ds                  Qualified
         D  Len                           5B 0
         D  Data                          1    dim(512)
         D
         D De_Lob_Ds       Ds                  LikeDs(En_Lob_Ds)
         D
         D En_VChar_Ds     Ds                  Qualified
         D  Len                           2B 0
         D  Data                          1    dim(512)
         D
         D De_VChar_Ds     Ds                  LikeDs(En_VChar_Ds)
         D
         D i               S             10I 0
         D
          /COPY QSYSINC/QRPGLESRC,SQLFP
         C     *Entry        Plist
         C                   Parm                    FuncCode
         C                   Parm                    OptParms
         C                   Parm                    DeCodTyp
         C                   Parm                    DeCodDta
         C                   Parm                    EnCodTyp
         C                   Parm                    EnCodDta
         C                   Parm                    SqlState
         C                   Parm                    SqMsgTxt
          /Free
            If FuncCode = 8 ;
              // do something here to determine the result data type
              // .....
              // in this example input and output types are exactly the same
              // so just copy
              EnCodTyp = DeCodTyp ;
            ElseIf FuncCode = 0 ;  // encode
              If DeCodTyp.SQLFST = 408 or DeCodTyp.SQLFST = 409 ;  // clob
                // in this example, reverse the characters as encryption
                De_Lob_Ds = DeCodDta ;
                En_Lob_Ds.Len = De_Lob_Ds.Len ;
                i = De_Lob_Ds.Len ;
                DoW i > 0 ;
                  En_Lob_Ds.Data(De_Lob_Ds.Len-i+1) = De_Lob_Ds.Data(i) ;
                  i = i - 1 ;
                EndDo ;
                EnCodDta = En_Lob_Ds ;
              ElseIf DeCodTyp.SQLFST = 448 or DeCodTyp.SQLFST = 449 ;   // varchar
                // in this example, reverse the characters as encryption
                De_VChar_Ds = DeCodDta ;
                En_VChar_Ds.Len = De_VChar_Ds.Len ;
                i = De_VChar_Ds.Len ;
                DoW i > 0 ;
                  En_VChar_Ds.Data(De_VChar_Ds.Len-i+1) = De_VChar_Ds.Data(i) ;
                  i = i - 1 ;
                EndDo ;
                EnCodDta = En_VChar_Ds ;
              Else ;  // other data type, just put the same value.
                EnCodDta = DeCodDta ;
              EndIf ;
              SqlState = '00000' ;
            ElseIf FuncCode = 4 ;  // decode
              If EnCodTyp.SQLFST = 408 or EnCodTyp.SQLFST = 409 ;  // clob
                // in this example, reverse the characters as decryption
                En_Lob_Ds = EnCodDta ;
                De_Lob_Ds.Len = En_Lob_Ds.Len ;
                i = En_Lob_Ds.Len ;
                DoW i > 0 ;
                  De_Lob_Ds.Data(En_Lob_Ds.Len-i+1) = En_Lob_Ds.Data(i) ;
                  i = i - 1 ;
                EndDo ;
                DeCodDta = De_Lob_Ds ;
              ElseIf EnCodTyp.SQLFST = 448 or EnCodTyp.SQLFST = 449 ;  // varchar
                  En_VChar_Ds = EnCodDta ;
                  De_VChar_Ds.Len = En_VChar_Ds.Len ;
                  i = En_VChar_Ds.Len ;
                  DoW i > 0 ;
                    De_VChar_Ds.Data(En_VChar_Ds.Len-i+1) = En_VChar_Ds.Data(i) ;
                    i = i - 1 ;
                  EndDo ;
                  DeCodDta = De_VChar_Ds ;
              Else ;  // other data type, just put the same value.
                DeCodDta = EnCodDta ;
              EndIf ;
              SqlState = '00000' ;
            Else ;
              SqlState = '38003' ;
            EndIf ;
            *InLR = *On ;
            Return ;
          /End-Free
    
    



    .
    Existen varias pautas que se deben tener en cuenta al adoptar apoyo procedimiento campo. Los elementos clave incluyen:

    • El procedimiento de campo debe ser un objeto * PGM ILE. Tenga en cuenta que * SRVPGMs, OPM * PGM y objetos Java no son compatibles.
    • No se permite SQL en un procedimiento de campo.
    • El procedimiento de campo debe ser determinista. Es muy peligroso utilizar los procedimientos de campo que no son deterministas porque los datos codificados pueden no ser capaces de decodificar de nuevo a su valor original.
    • Cuando se utiliza el comando de archivo físico Change (CHGPF) con el parámetro SRCFILE para cambiar la definición de campo de un archivo físico, el comando CHGPF eliminará todos los procedimientos de campo registrados en el archivo físico sin ningún mensaje de advertencia.



    Con la utilización delfield procedure, se puede realizar fácilmente un cifrado transparente de nivel de columna de los datos almacenados en una base de datos. Usted puede utilizar FieldProc para cualquier tipo de esquema de codificación.

















    1 comentario:

    1. Como le puedo pasar un parametro a un programa que se llama desde el fieldproc ????

      ALTER TABLE sistemdb/client1 ALTER COLUMN CLI_TCHCOR
      SET FIELDPROC sistempgm/PROGRAMA PARM('001' '123456' ' ' ' ')

      DONDE PARM SON LOS PARÁMETROS PERO NO FUNCIONA LA CENETENCIA CON ESTA INSTRUCCIÓN (PARM) ?????

      ResponderBorrar