Inicio.

    O objetivo deste tutorial é demonstrar a integração da biblioteca QCA - Qt Cryptographic Architecture a um aplicativo Qt.
    A biblioteca QCA pode ser obtida em: http://delta.affinix.com/qca/ e é um projeto Open Source. No site os autores a definem como uma simples API multi plataforma de criptografia, utilizando convenções e tipos de dados Qt, sendo multi plataforma roda Linux e Windows®.
    Após baixar os arquivos do site, compile seguindo as instruções.
    Leia com muita atenção a documentação da biblioteca QCA, pois o tema é complexo e se você for utilizar comercialmente em um aplicativo seu, pode ser uma questão critica.

    Este tutorial é meramente didático, portanto você não deve copiar literalmente o código aqui descrito, adapte ao seu aplicativo e teste exaustivamente antes de liberar seu aplicativo para uso, lembrando que o material contido neste tutorial não tem nenhuma garantia implícita ou explicita decorrente de seu uso.

    Existem dois métodos disponíveis na QCA, a criptografia com chave, propriamente dita e o hash de senha, que no nosso caso, onde iremos guardar a senha num banco de dados, o hash é o mais indicado.
    O hash é uma especie de espalhamento ou dispersão dos dados, sendo uma via de mão única, ou seja, uma vez gerado o hash da senha, não se pode voltar atrás, não se pode “descriptografar” a senha.
    Outra explicação é que o hash é uma forma de transformar um texto em uma seqüência de caracteres de comprimento fixo, sendo quase impossível criar duas seqüências iguais para textos de entrada diferentes, quando se utiliza a função de hash.
    Existem varios algoritmos de hash, sendo os mais conhecidos o MD5 e o SHA1.
    Você deve estar se perguntado porque a senha não pode ser descriptografada, é porque não é preciso, basta, no login do sistema, gerar o hash da senha que o usuário digitou e comparar com o valor armazenado no banco, que também foi criptografado por hash, se ambos os valores criptografados forem iguais, a senha confere.
    No caso de o usuário esquecer a senha, não é possível recupera-la, sendo necessário criar uma nova senha.
    Em sistemas cuja politica de uso exija que a senha possa ser recuperada em caso de esquecimento do usuário, existe a opção de enviar a senha atual por e-mail ou exibí-la mediante confirmação de determinados dados pessoais.
    Neste caso a criptografia com chave é a solução, a chave de criptografia pode ser única para todas as senhas e é mantida, de alguma forma, dentro da aplicação ou do banco.
    Este procedimento implica num nível mais baixo de segurança, a segurança da senha depende da segurança da chave. Se a chave puder ser obtida, então a segurança das senhas armazenadas estará comprometida.
    No caso da senha criptografada com chave, no login existe duas possibilidades, descriptografar a senha armazenada no banco e comparar com a digitada pelo usuário ou criptografar a senha digitada e comparar com a armazenada. No nosso caso, onde desenvolvemos sistemas em Qt acessando bancos de dados, o hash é mais indicado, mas eu irei demonstrar neste tutorial os dois casos.

    Neste tutorial iremos enfocar mais o código que iremos criar e menos o uso das ferramentas de programação como o Kdevelop e o QtDesigner, para isso meus tutoriais anteriores tem bastante exemplos. Portanto neste tutorial iremos andar mais rapido nos passos de construções de telas no designer e nos passos necessários no Kdevelop. Também faremos maior uso de recursos do banco de dados como triggers e procedures.

    Obs: Baixe aqui os fontes do aplicativo.

    Comece criando a tela principal no Designer com QMainWindow: .

    Abaixo:Janela principal.(Clique na imagem para ampliar).

    >Janela principal

    Propriedades:

    objectName: fPrincipal_Parent
    windowModality: applicationModal

    No menu, crie as opções Clientes, Usuários e Sair.

    De o titulo que quiser e o tamanho a vontade, salve na sua pasta com o nome de fPrincipal_Parent.ui.

    Agora vamos criar a tela de login usando Qdialog:

    Abaixo, Tela de login.(Clique na imagem para ampliar).

    >Tela de login

    Temos aqui dois QlineEdit, dois Qlabels, dois QpushButton e dois Qframe.

    Propriedades:
    Qdialog:
      objectName:fLogin_Parent
      windowModality: applicationModal
    De o titulo e o tamanho que quiser, salve como fLogin_Parent.ui
    QlineEdit:
      objectName :
    edit_Usuario e edit_Senha, no edit senha de a propriedade echoMode o valor Password.

    QpushButton:
      objectName:botao_OK e botao_Cancelar, os dois com autoDefault=true ou checado no designer, default=false no botao_OK e default=true no botao_Cancelar.

    Agora, vamos criar o cadastro de usuários, onde iremos cadastrar os nossos usuários com as senhas correspondentes. Este cadastro dará opção de escolher entre hash e criptografia, e se escolher hash, dará opção de escolher entre o padrão sha1 e md5.
    Use como modelo o exemplo do meu primeiro tutorial:
    http://www.sistemasparalinux.com.br/tutorial.html

    Também vamos escolher no cadastro o nível de acesso que o usuário terá dentro do sistema, mais uma medida para aumentar o nível de segurança do sistema.

    Abaixo, cadastro de usuários.(Clique na imagem para ampliar).

    >Cadastro de usuários

    Use Qwidget para o cadastro. Veja na imagem abaixo as propriedades:

    Abaixo,propriedades do cadastro de usuários.(Clique na imagem para ampliar).

    > Kugar Designer

    Veja abaixo a lista de todos os componentes do cadastro, no Object Inspector do Designer:

    Abaixo, componentes do cadastro.(Clique na imagem para ampliar).

    >Object inspector

    Os QpushButton terão as propriedades autoDefault e default desmarcadas ou false.
    O QlineEdit edit_Senha tera echomode=Password.
    O QcomboBox combo_Nivel terá as seguintes opções Administrador, Ler e gravar, Ler, você clica duas vezes em cima do combo no Designer e o edit combobox se abrira.
    Salve o cadastro com o nome de fCadUsuarios_Parent.ui.
    Agora vamos criar o banco no PostgreSql, crie um banco com o nome de teste_senha.
    Abaixo, o script da estrutura do banco:

    create language plpgsql;
    
    create table usuarios(
    id_usuarios int primary key,
    nome varchar(20) unique not null check (nome <> ''),
    senha bytea not null,
    nivel int not null check (nivel >= 1),
    tipo_senha integer not null check (tipo_senha >= 0 and tipo_senha<=1));
    
    
    create table codigos(
    id_codigos int primary key,
    codigo varchar(100) not null check (codigo <> ''),
    chave bytea not null,
    vetor bytea not null,
    tipo_hash integer not null check (tipo_hash >= 0 and tipo_hash <=1));
    
    alter table codigos add FOREIGN KEY (id_codigos) references usuarios(id_usuarios);
    
    CREATE OR REPLACE FUNCTION deleta_Complemento() RETURNS trigger AS $deleta_Complemento$
    BEGIN
       delete from
         codigos
       where
         id_codigos=old.id_usuarios;
      return OLD;
    END;
    $deleta_Complemento$ LANGUAGE plpgsql; 
    
    CREATE TRIGGER deleta_Complemento BEFORE DELETE ON usuarios
    FOR EACH ROW EXECUTE PROCEDURE deleta_Complemento();
    
    
    CREATE OR REPLACE FUNCTION insere_Usuario(integer,varchar(20),bytea,integer,integer,varchar(100),bytea,bytea,integer)
    RETURNS void
    AS '
    insert into
      usuarios
      (id_usuarios,
       nome,
       senha,
       nivel,
       tipo_senha)
    values
      ($1,$2,$3,$4,$5);
    insert into
      codigos
      (id_codigos,
       codigo,
       chave,
       vetor,
       tipo_hash)
    values
      ($1,$6,$7,$8,$9);
    ' LANGUAGE SQL;
    
    CREATE OR REPLACE FUNCTION altera_Usuario(integer,varchar(20),bytea,integer,integer,varchar(100),bytea,bytea,integer)
    RETURNS void
    AS '
    update
      usuarios
    set
       nome=$2,
       senha=$3,
       nivel=$4,
       tipo_senha=$5
    where
        id_usuarios=$1;
    update
      codigos
    set
        codigo=$6,
        chave=$7,
        vetor=$8,
        tipo_hash=$9
    where
        id_codigos=$1;
    ' LANGUAGE SQL;


    Na primeira linha criamos a “linguagem” plpgsql necessária para a trigger, o Postgre dispõem de varias linguagens para construirmos as suas funções, nele tudo é função, triggers e stored procedures para ele são funções. A linguagem mais comum é a propria sql, que usamos para as procedures, porem as triggers só são possíveis na linguagen plpgsql.
    Verifique as linguagens instaladas com o comando “select * from pg_language;”
    Na tabela usuarios guardaremos o nome, a senha criptografada, por isso o campo é do tipo bytea(array de bytes), o nível de acesso, definido por um numero inteiro e o tipo da senha, se hash ou criptografada, também definida por um numero inteiro.

    A tabela codigos é de vital importância, ela guardara no campo codigo uma seqüência de caracteres que o sistema gerara de forma aleatória, esta seqüência é concatenada a senha que o usuário escolher no momento em que a senha é gerada, ou seja, no momento que o usuário for cadastrado no sistema.
    Isto sera feito no caso de de se escolher o hash para a senha, é chamado de “sal” ou hash salting, evita de no caso de acidentalmente dois usuários escolherem a mesma senha o hash para ambas ser igual e serem gravadas senhas idênticas no banco.
    No caso de uma consulta indevida no banco se previne este problema e também que o invasor recorra a uma tabela ou dicionario de hash para verificar que seqüência de caracteres deu origem aquele hash, na verdade dificulta, porque não existe segurança infalível.
    Portanto, a técnica do sal é usada quando escolhemos o hash e não a criptografia com chave.
    Os campos chave e vetor são as chaves de criptografia que o algoritmo da QCA gera, e são necessários para descriptografar a senha, aqui vem uma consideração, no caso de se escolher a criptografia com chave, onde guardar a chave?
    Obviamente que grava-la no banco diminui a segurança, aqui a tabela codigos, pelo nome, não significa muita coisa, os nomes dos campos podem ser outros que não esses do meu exemplo, para não serem tão evidentes, o nome “chave” para o campo é muito evidente, você pode mudar até o nome da tabela de codigos para outro qualquer.

    Um eventual invasor pode não ter conhecimentos de SQL, mas se os tiver, pode ver pela chave estrangeira que usuarios se relaciona com codigos, ai mesmo que os nomes dos campos estejam disfarçados ele percebera a evidente ligação.
    Pode-se deixar uma chave fixa dentro do fonte mas ao compilar e gerar o binário, se abrirmos ele com um editor de texto os caracteres da chave ainda podem estar visíveis lá.
    Outra opção seria guardar num arquivo texto num diretório qualquer, mas ainda assim seria relativamente inseguro. Escolhi guardar os valores de senha, chave e vetor no formato byte array por dificultar mais a visualização, mas a QCA permite a conversão de byte array para caracteres legíveis, se você quiser pode guardar no banco no formato string.
    No nosso exemplo sera usada uma função da QCA que gera chaves e vetores aleatoriamente, e sera usada uma para cada senha, fazendo assim se tem uma chave individual para cada senha ao invés de uma chave única para todas as senhas. O ultimo campo da tabela codigos, tipo_hash informara se o hash é sha1 ou md5.
    Veremos então como gerar os dois tipos de criptografia, hash e criptografia com chave, sendo o hash o mais indicado para armazenar senhas de login do sistema em banco de dados.
    A tecnica do sal pode ser usada também na criptografia com chave, não precisa ficar restrita ao hash.

    Na seqüência do script, temos a criação da chave estrangeira relacionando usuarios e codigos.
    Depois temos uma trigger em usuarios para excluir os dados na tabela codigos relativos à aquele usuário, já que a chave estrangeira não permite a exclusão em usuarios com dados relativos em outra tabela, a trigger é before delete, ou seja, exclui em codigos primeiro, depois a operação de exclusão em usuarios é realizada.
    Escolhi a trigger em detrimento de um índice de exclusão em cascata(cascade) que seria mais simples, para termos um maior controle, o índice não faz perguntas, é automático e mecânico na exclusão, mas a trigger nos permite uma eventual intervenção permitindo que se coloque condições onde a exclusão não sera permitida.

    Em seguida temos duas procedures escritas em SQL mesmo, procedures simples no Postgre não precisam ser em plpgsql desde que não tenham comandos não sql.
    A primeira procedure é para inserir dados nas duas tabelas, a segunda para alterar.
    Iremos chamar as procedures de dentro de nosso aplicativo Qt, mas darei um exemplo de como gravar nas tabelas sem as procedures, lembrando que ao se gravar em seqüência duas ao mais tabelas que são relacionadas, é imprescindível o uso de transações, no caso das procedures o Postgre inicia as transações de forma implicita, você não precisa explicitar com algo do tipo “start transaction” mas se gravar sem procedures, a partir do aplicativo terá de explicitar a transação, irei demonstrar isso mais adiante.
    Copie o script acima para um arquivo texto, chame-o de banco.txt e rode a partir to psql, por exemplo:
    rodolfo@Programador:~$ psql teste_senha

    Bem vindo ao psql 8.3.7, o terminal iterativo do PostgreSQL.

    Digite:   \copyright para mostrar termos de distribuição
                  \h para ajuda com comandos SQL
                   \? para ajuda com comandos do psql
                  \g ou terminar com ponto-e-vírgula para executar a consulta
                  \q para sair

    teste_senha=#\i   /home/rodolfo/sistema_bloqueio/banco/banco.txt

    Agora vamos criar o codigo do aplicativo, lembrando que neste tutorial não vou demonstrar o passo a passo do QtDesigner nem do Kdevelop, para isso veja o tutorial:

    http://www.sistemasparalinux.com.br/tutorial.html

    Crie um projeto novo no Kdevelop, ele ira gerar os fontes que você ira substituir pelos nosso, se quiser fazer sem o Kdevelop, lei meu outro tutorial:

    http://www.sistemasparalinux.com.br/tutorial_qmake_uic_moc.html

    Se for usar o Kdevelop, depois de escolher a opção :
    novo projeto:
      C++:
        Qmake project:
          Aplicação do Qt4
    Feito isso ele ira gerar o src.pro, substitua neste arquivo os fontes do exemplo do Kdevelop pelos nossos.

    Vou colocar abaixo os fontes e comentar, lembrando que você precisa antes baixar os arquivos da QCA no site que indiquei acima e compilar com a seqüência ./configure, make e make install.

    Main.cpp

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado                         *
     *   rodolfo@Programador                                                   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    
    #include <QApplication>
    #include "fPrincipal.h"//Para a janela principal do aplicativo
    #include <QSettings>//Para leitura do arquivo de configuração
    #include <QMessageBox>
    #include <QSqlDatabase>//Para acesso ao banco de dados
    #include "fLogin.h"//Para a tela de login
    #include <QtCrypto>//QCA
    #include <QTextCodec>//Para o locale
    
    int main(int argc, char *argv[])
    {
    
      QCA::Initializer init;//Todo aplicativo que usa QCA de ter este código aqui no main.cpp
    
      QTextCodec::setCodecForTr(QTextCodec:: codecForName ( "UTF-8"));//Para locale
      QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); 
      QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); 
    
      QApplication app(argc, argv);
         
    
      QString caminho=QCoreApplication::applicationDirPath();//Acha o caminho do binario, no mesmo diretório esta o arquivo config.txt
      caminho.append("/config.txt");
    
      QString database,host,databasename,linha;
      QSettings settings(caminho,QSettings::IniFormat);//Ler os parâmetros de conexão ao banco no config.txt
    
      database=settings.value("banco/database").toString();
      host=settings.value("banco/hostname").toString();
      databasename=settings.value("banco/databasename").toString();
        
      if (database=="" || host=="" || databasename=="")
      {
        QMessageBox::information(0,"Iniciando o Sistema","Não foi possivel ler o arquivo de configuração");      
        return 1;
      }
      else
      {
        QSqlDatabase db = QSqlDatabase::addDatabase(database);//Instacia a conexão com os parametros.
        db.setHostName(host);
        db.setDatabaseName(databasename);
        db.setUserName("");//Coloque aqui seu usuario no banco
        db.setPassword("");//Coloque aqui sua senha
        bool ok = db.open();
         
        if (ok)
        {
             
          fPrincipal* fTela_Prin = new fPrincipal(caminho);//Cria o objeto da classe fPrincipal e mostra na tela
          fTela_Prin->show();
           
          bool testa_Login=false;//O endereço desta variavel é passado a tela de login para saber se o login foi correto(usuarios e senhas ok)
          int nivel_Acesso;//Para guardar o nivel de acesso do usuário no aplicativo
          fLogin* fAcesso=new fLogin(0,&testa_Login,&nivel_Acesso);
          fAcesso->exec();
          if (testa_Login==false)//Se o login falhou, destroi os objetos criados e sai do sistema.
          {
            delete fAcesso;
            delete fTela_Prin;
            return 1;
          }
          else//Se o login foi correto, destroi a tela de login.
          {
            fTela_Prin->pega_Nivel(nivel_Acesso);//Passa a janela principal o nive de acessso, que por sua vez passara os demais cadastros.
            delete fAcesso;
          } 
        }
        else
        {
          QMessageBox::information(0,"Iniciando o Sistema","Não foi possivel conectar-se ao banco de dados!");
          return 1; 
        }
      }
      return app.exec();
    }


    No main.cpp inicializamos a QCA, lemos o arquivo de configuração onde estão os parâmetros de conexão ao banco, fazemos a conexão com o banco, depois instanciamos a tela principal e a tela de login, sendo que esta tem dois parametros no seu constructor que retornam a aplicação se o login foi bem sucedido, ou seja, usuário e senha ok e o nivel de acesso do usuário dentro do aplicativo.
    Se o login foi correto, chame a função publica pega_Nivel definida na janela principal(fTela_Prin) para passar o valor do nível de acesso a janela principal que por sua vez passar aos demais cadastros.
    Se o login falhar a tela de login é destruída e o aplicativo finalizado.

    config.txt

    [banco]
    database = QPSQL
    hostname = localhost
    databasename = teste_senha
    [timer]
    tempo =100000

    Lembrando que o config.txt deve estar no mesmo diretório do binario.
    O parâmetro tempo servira para o nosso próximo tutorial, onde veremos como bloquear o sistema após inatividade do mouse e do teclado.

    fLogin.h

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado                         *
     *   rrmdeveloper@ig.com.br                                                *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    #ifndef FDESBLOQUEIA_LOGIN_H
    #define FDESBLOQUEIA_LOGIN_H
    
    #include "ui_fLogin_Parent.h"
    
    class QSqlQuery;
    class fLogin:public QDialog,private Ui::fLogin_Parent
    {
      Q_OBJECT
      
      public:
        fLogin(QObject *parent = 0,bool* login=false,int* nivel=0);
        ~fLogin();
    
      private:
        QSqlQuery* query_Login;
        bool* login_2;
        int* niv_Acesso;
    
        void keyPressEvent(QKeyEvent* e);
    
    
      private slots:
        void botao_OK_Click();
    
      protected:
        class pesq_Senha* testa_Senha;
    };
    
    #endif
    O header da classe fLogin com as declarações de variáveis e funções, farei os comentários no source (.cpp).

    fLogin.cpp

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado   *
     *   rrmdeveloper@ig.com.br   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    #include "fLogin.h"
    #include <QSqlQuery>
    #include <QMessageBox>
    #include <QKeyEvent>//Para tabular com enter.
    #include <QDesktopWidget>
    #include "pesq_Senha.h"
    
    fLogin::fLogin(QObject *parent,bool* login,int* nivel)
    {
      setupUi(this);
    
      connect(botao_OK,SIGNAL(clicked()),this,SLOT(botao_OK_Click()));
    
    
    
      this->setFixedSize (this->size());//Para que não se possa redimensionar a janela
    
    
      QRect rect = QApplication::desktop()->availableGeometry(this);//para centralizar na tela.
      this->move(rect.center() - this->rect().center());
    
    
      edit_Usuario->setFocus();
      
      query_Login=new QSqlQuery;
    
      login_2=login;
      niv_Acesso=nivel;
    
      testa_Senha=new pesq_Senha;//Para as funções da QCA
    }
    
    fLogin::~fLogin()
    {
      delete query_Login;
      delete testa_Senha;
    }
    
    void fLogin:: botao_OK_Click()
    {
      QString senha=edit_Senha->text();
      QByteArray senha_Hash;  //Abre a tabela de usuários, o parâmetro da consulta é o nome digitado, por isso na tabela a um índice que não permite usuários duplicados.
      query_Login->prepare(" select "
                                    " a.id_usuarios,"
                                    " a.nome,"
                                    " a.senha,"
                                    " a.nivel,"
                                    " a.tipo_senha, "
                                    " b.codigo,"
                                    " b.chave, "
                                    " b.vetor, "
                                    " b.tipo_hash "
                           " from "
                                    " usuarios a,"
                                    " codigos b "
                           " where "
                                    " a.id_usuarios=b.id_codigos and "
                                    " a.nome=:nom");
      query_Login->bindValue(":nom",edit_Usuario->text());
      query_Login->exec();
      query_Login->next();
      if (query_Login->size()>0)
      {
        if (query_Login->value(4).toInt()==0)//Testa se a senha é hash ou criptografada.
        {
          senha=senha.append(query_Login->value(5).toString());//Recupera a string aleatória gerada no momento da gravação da senha, e concatena com a senha digitada.
          switch (query_Login->value(8).toInt())//Testa se o padrão do hash e md5 ou sha1.
          {
            case 0:
            {
              senha_Hash=testa_Senha->gera_hash_Senha(senha,0);//Gera o hash da senha digitada
              break;
            }
            case 1:
            {
              senha_Hash=testa_Senha->gera_hash_Senha(senha,1);
              break;
            }
          }
          if (senha_Hash==query_Login->value(2).toByteArray())//Compara o hash da senha digitada com a senha gravada
          {
            *login_2=true;
            *niv_Acesso=query_Login->value(3).toInt();
            this->close();
          }
          else//Se a senha for incorreta, nega o acesso.
          {
            edit_Senha->clear();
            edit_Senha->setFocus();
            QMessageBox::information(0,"Acesso ao Sistema","Senha incorreta!");
          }  
        }
        else
          if (query_Login->value(4).toInt()==1)//Senha criptografada
          {
            if (senha==testa_Senha->descriptografa(query_Login->value(2).toByteArray(),query_Login->value(6).toByteArray(),query_Login->value(7).toByteArray()))//Descriptografa a senha gravada e compara com a digitada.A rotina usa como parâmetros a senha gravada, a chave o vetor gerados e gravados no banco.
            {
              *login_2=true;//Acerta os valores de vivel de acesso e login correto.
              *niv_Acesso=query_Login->value(3).toInt();
              this->close();
            }
            else
            {
              edit_Senha->clear();
              edit_Senha->setFocus();
              QMessageBox::information(0,"Acesso ao Sistema","Senha incorreta!");
            }
          }
          else
            QMessageBox::information(0,"Acesso ao Sistema","Tipo de senha desconhecido!");
      }  
      else
      { 
        edit_Usuario->setFocus();
        QMessageBox::information(0,"Acesso ao Sistema","Usúario não existe no banco de dados!");
      }  
    }
    
    void fLogin::keyPressEvent(QKeyEvent* e)
    {
      if ( e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return )//Tabula com enter.
        QApplication::focusWidget()->nextInFocusChain()->setFocus();
    }


    A tela de login recebe o nome do usuário e sua senha, o nome é passado como parâmetro de uma consulta as tabela usuarios e codigos, consulta essa realizada por um objeto QsqlQuery, o campo nome na tabela usuarios tem uma restrição “unique” para que não haja nomes repetidos.
    A senha digitada passara pelos procedimentos de conferencia conforme o seu tipo, se for hash ou criptografada. O campo tipo_senha vai informar se é hash ou criptografada, se for hash o campo tipo_hash na tabela codigos ira informar se é sha1 ou md5.
    Se a senha for hash, ao valor digitado é concatenado o valor aleatorio (o sal) que foi gerado no momento do cadastramento do usuário e que é retornado pela consulta.
    Após isso testa-se se o hash é sha1 ou md5 e chama-se a função gera_hash_Senha() com os parâmetros senha e tipo de hash.
    Em seguida compara-se o hash gerado a partir da senha digitada com o valor gravado no banco, se igual libera senão bloqueia.
    Se a senha foi criptografada com chave, o processo é semelhante, chama-se a função descriptografa() para descriptografar a senha gravada, que foi recuperada pela consulta e compara-la à digitada, observe que é possível fazer o inverso, criptografar a senha digitada pelo usuário e comparar com a gravada.
    A função descriptografa() tem como parâmetros a senha recuperada do banco pela consulta, ou seja, a senha gravada, a chave e o vetor, valores gerados pelo algoritmo da QCA no momento do cadastramento e gravados no banco. O valor retornado pela função é comparado com o valor digitado, se igual libera, senão bloqueia.
    As funções gera_hash_Senha() e descriptografa() estão definidas numa classe abstrata nomeada pesq_Senha, no constructor de fLogin instancia-se um objeto da classe pesq_Senha com o nome de testa_Senha.

    Observe que quando você for executar o aplicativo pela primeira vez, não havera usuário e senha algum gravados no banco, portanto você deve contornar a chamada da tela de login, ou setar *login_2=true; no inicio da função botao_OK_Click(), assim você pode clicar no botão ok sem digitar nada, ele ira reclamar dizendo que o usuário não existe no banco, mas você pode fechar atela de login que a janela principal continuara aberta, dando acesso ao cadastro de usuários, você cadastra um primeiro usuário e depois retira a condição de livre acesso para testar o aplicativo.

    fPrincipal.h

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado   *
     *   rodolfo@Programador   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    
    #ifndef FPRINCIPAL_H
    #define FPRINCIPAL_H
    
    #include "ui_fPrincipal_Parent.h"
    
    class fPrincipal:public QMainWindow,private Ui::fPrincipal_Parent
    {
      Q_OBJECT
    
      public:
        fPrincipal(QString caminho="");
        ~fPrincipal();
    
        void pega_Nivel(int pegNivel);//Declara a função que pega o nivel de acesso.
    
    
      protected:
        class fCadUsuarios* fCadUs;//Declara o cadastro de usuários.
    
      private slots:
        void on_action_Usuarios_triggered();//Slot que ira instanciar o objeto da classe  fCadUsuarios.
    
      private:
        int nivel;
    
    
    };
    
    #endif


    O header da janela mãe do aplicativo, com as declarações do slot para instanciar o cadastro de usuários, as declarações de classe e a variavel nivel e a função publica pega_Nivel().

    fPrincipal.cpp

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado   *
     *   rodolfo@Programador   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    
    #include "fPrincipal.h"
    #include <QDesktopWidget>
    #include "fCadUsuarios.h"
    
    fPrincipal::fPrincipal(QString caminho)
    {
      setupUi(this);
    
      this->setAttribute(Qt::WA_DeleteOnClose,true);//para auto destruir o objeto e liberar a memoria.
    
      QRect rect = QApplication::desktop()->availableGeometry(this);//para centralizar na tela.
      this->move(rect.center() - this->rect().center());
    
      this->setFixedSize (this->size());//Para que não se possa redimensionar a janela
      
    }
    
    fPrincipal::~fPrincipal()
    {
    
    }
    
    void fPrincipal::on_action_Usuarios_triggered()
    {
      fCadUs=new fCadUsuarios(this,nivel);//Instancia o cadastro de usuários.
      fCadUs->show();
    }
    
    void fPrincipal::pega_Nivel(int pegNivel)
    {
      nivel=pegNivel;//Acerta a variavel nivel com o valor de pegNivel que vem do main.cpp
     
    }


    Janela principal do sistema, com a atribuição de instanciar o cadastro de usuários e controlar o nível de acesso que ele terá dentro do sistema.

    fCadUsuarios.h

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado   *
     *   rodolfo@Programador   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    
    #ifndef FCADUSUARIOS_H
    #define FCADUSUARIOS_H
    
    #include "ui_fCadUsuarios_Parent.h"
    
    class QSqlQuery;
    class QSqlQueryModel;
    class QSqlDatabase;
    class QSignalMapper;
    
    class fCadUsuarios:public QWidget,private Ui::fCadUsuarios_Parent
    {
      Q_OBJECT
      
      public:
        fCadUsuarios(QWidget* parent=0,int nivel=0);
        ~fCadUsuarios();
    
      private:
        QButtonGroup* group_Tipo_Senha;
        QButtonGroup* group_Tipo_Hash;
        QSqlQuery* query_Usuarios;//Nossa query de trabalho.
        QSqlQueryModel* model_Usuarios;//Um grid para mostrar os dados
        int gera_Aleatorio(int min,int max,int semente);//Função C++ para gerar numeros aleatorios.
        QSignalMapper* signalMapper;
    
      private slots:
        void tipo_Senha();
        void grava_na_Tabela(int tipo);
        void exclui_na_Tabela();
        void limpa_Tela();
    
      protected:
        class pesq_Senha* testa_Senha;//Declara um objeto da classe abstrata pesq_Senha que tem as funções da QCA.
      
    };
    
    
    #endif


    Header do cadastro de usuários, comentários no fonte.

    fCadUsuarios.cpp

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado   *
     *   rodolfo@Programador   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    #include "fCadUsuarios.h"
    #include <QDesktopWidget>
    #include <QButtonGroup>
    #include <QSqlQueryModel>
    #include <QSqlQuery>
    #include <QtCrypto>
    #include <QMessageBox>
    #include <QSqlError>
    #include "pesq_Senha.h"
    #include <QSignalMapper>
    
    fCadUsuarios::fCadUsuarios(QWidget* parent,int nivel)
    {
      setupUi(this);
    
      this->setAttribute(Qt::WA_DeleteOnClose,true);//para auto destruir o objeto e liberar a memoria.
    
      QRect rect = QApplication::desktop()->availableGeometry(this);//para centralizar na tela.
      this->move(rect.center() - this->rect().center());
    
      this->setFixedSize (this->size());//Para que não se possa redimensionar a janela
    
      if (nivel>1)//Bloqueia o acesso aos botões conforme o nivel do usuário.
      {
        botao_Incluir->setEnabled(false);
        botao_Alterar->setEnabled(false);
        botao_Excluir->setEnabled(false);
      }
    
      testa_Senha=new pesq_Senha;//Instancia um objeto da classe pesq_Senha.
    
      group_Tipo_Senha=new QButtonGroup(this);//cria o QbuttonGroup
      group_Tipo_Senha->setExclusive(true);//clicou em um, desmarcou o outro
      group_Tipo_Senha->addButton(botao_hash);//adiciona o RadioButton ao QbuttonGroup
      group_Tipo_Senha->setId(botao_hash,0);// indice do RadioButton dentro do QbuttonGroup
      group_Tipo_Senha->addButton(botao_Cripto);
      group_Tipo_Senha->setId(botao_Cripto,1);
    
      connect(group_Tipo_Senha,SIGNAL(buttonClicked(int)),this,SLOT(tipo_Senha()));
    
      group_Tipo_Hash=new QButtonGroup(this);
      group_Tipo_Hash->setExclusive(true);//clicou em um, desmarcou o outro
      group_Tipo_Hash->addButton(botao_sha1);//adiciona o RadioButton ao QbuttonGroup
      group_Tipo_Hash->setId(botao_sha1,0);// indice do RadioButton dentro do QbuttonGroup
      group_Tipo_Hash->addButton(botao_md5);
      group_Tipo_Hash->setId(botao_md5,1);
    
      signalMapper = new QSignalMapper(this);//Para que os botões incluir e alterar acessem o mesmo slot e se saiba qual botão esta acessando.
      signalMapper->setMapping(botao_Incluir, 1);
      signalMapper->setMapping(botao_Alterar, 2);
    
      connect(botao_Incluir,SIGNAL(clicked()),signalMapper,SLOT (map()));
      connect(botao_Alterar,SIGNAL(clicked()),signalMapper,SLOT (map()));
    
      connect(signalMapper, SIGNAL(mapped(int )),this,SLOT(grava_na_Tabela(int )));
    
    
      connect(botao_Excluir,SIGNAL(clicked()),this,SLOT(exclui_na_Tabela()));
      connect(botao_limpar_Tela,SIGNAL(clicked()),this,SLOT(limpa_Tela()));
    
      query_Usuarios=new QSqlQuery;
    
      model_Usuarios=new QSqlQueryModel(this);
      model_Usuarios->setQuery("select * from usuarios order by id_usuarios");
      model_Usuarios->setHeaderData(0, Qt::Horizontal, tr("Código"));
      model_Usuarios->setHeaderData(1, Qt::Horizontal, tr("Nome"));
      model_Usuarios->setHeaderData(2, Qt::Horizontal, tr("Senha"));
      model_Usuarios->setHeaderData(3, Qt::Horizontal, tr("Nivel"));
    
      tableView_Usuarios->setModel(model_Usuarios);//Conecta o model ao tableView para visualizarmos a tabela.
      tableView_Usuarios->setColumnWidth(0,40);
      tableView_Usuarios->setColumnWidth(1,80);
      tableView_Usuarios->setColumnWidth(2,270);
      tableView_Usuarios->setColumnWidth(3,40);
      tableView_Usuarios->show();
    
      edit_Codigo->setFocus(); 
    
    
    }
    
    fCadUsuarios::~fCadUsuarios()
    {
      delete group_Tipo_Senha;
      delete testa_Senha;
      delete group_Tipo_Hash;
      delete query_Usuarios;
      delete model_Usuarios;
      delete signalMapper;
    }
    
    void fCadUsuarios::tipo_Senha()
    {
      switch (group_Tipo_Senha->checkedId())//Bloqueia alternadamente as opções de hash e criptografada.
      {
        case 0:
        {
          group_hash->setEnabled(true);
          break;
        }
        case 1:
        {
          group_hash->setEnabled(false);
          break;
        }
      }
      
    }
    
    void fCadUsuarios::grava_na_Tabela(int tipo)
    {
    
      QString senha;
      senha=edit_Senha->text();
      QByteArray senha_Cripto;
      QString complemento="0";
      QByteArray* chave_Usuario=new QByteArray;
      QByteArray* vetor_Usuario=new QByteArray;
    
      switch (group_Tipo_Senha->checkedId())//Testa se a senha sera hash ou criptografada.
      {
        case 0:
        {
          
          int j,k;
          //gera uma sequencia de 10 numeros aleatorios, de 65 a 90, seus characters ASCII correspondestes são concatenados em uma string que por sua vez é concatenada à senha digitada.Isto serve para evitar senhas iguais.
          for (j=1;j<=10;j++)
          {
            k=gera_Aleatorio(65,90,j);
            complemento[j]=QChar(k);
          }
          QMessageBox::information(0,"Sistema de Criptografia",complemento);
          senha=senha.append(complemento);
          switch (group_Tipo_Hash->checkedId())//Testa se o hash é sha1 ou md5.
          {
            case 0:
            {
              senha_Cripto=testa_Senha->gera_hash_Senha(senha,0);//Gera o hash conforme o tipo.
              break;
            }
            case 1:
            {
              senha_Cripto=testa_Senha->gera_hash_Senha(senha,1);
              break;
            }
          }
          break;
        }
        case 1:
        {
          senha_Cripto=testa_Senha->criptografa(senha,chave_Usuario,vetor_Usuario);//Senha criptografada.
          break;
        }
      }
    
      bool gravou=false;
      QString erro;
    
      //Abaixo, o código que ira gravar a senha na tabela. O objeto QSqlQuery ira chamar uma procedure no banco(PostgreSql) que ira gravar nas duas tabelas, usuários e códigos, na tabela usuários sera gravada o nome, a senha, o tipo de senha, se hash ou criptografada, o nível de acesso dentro do sistema, na tabela códigos sera gravada o complemento e a chave e vetor no caso de senha criptografada. São duas procedures, uma para inserir e outra para alterar.
    
    
      if (tipo==1)//Testa se esta incluindo ou alterando.
        query_Usuarios->prepare("select insere_Usuario(:cod,:nom,:sen,:niv,:tp,:comp,:chv,:vet,:hash)");
      else
        if (tipo==2)
          query_Usuarios->prepare("select altera_Usuario(:cod,:nom,:sen,:niv,:tp,:comp,:chv,:vet,:hash)");
      query_Usuarios->bindValue(":cod", edit_Codigo->text());//Parâmetros.
      query_Usuarios->bindValue(":nom", edit_Nome->text());
      query_Usuarios->bindValue(":sen",senha_Cripto);
      query_Usuarios->bindValue(":niv", combo_Nivel->currentIndex()+1);
      switch (group_Tipo_Senha->checkedId())
      {
        case 0:
        {
          query_Usuarios->bindValue(":tp", 0);
          query_Usuarios->bindValue(":chv","0");
          query_Usuarios->bindValue(":vet","0");
          switch (group_Tipo_Hash->checkedId())
          {
            case 0:
            {
              query_Usuarios->bindValue(":hash",0);
              break;
            }
            case 1:
            {
              query_Usuarios->bindValue(":hash",1);
              break;
            }
          }      
          break;
        }
        case 1:
        {
          query_Usuarios->bindValue(":tp",1);
          query_Usuarios->bindValue(":chv",*chave_Usuario);
          query_Usuarios->bindValue(":vet",*vetor_Usuario);
          query_Usuarios->bindValue(":hash",0);
          break;
        }      
      }
      query_Usuarios->bindValue(":comp",complemento);
      gravou=query_Usuarios->exec();
      if (! gravou)
      {
        erro=query_Usuarios->lastError().text();
        QMessageBox::critical(0,"Sistema de Criptografia",erro,QMessageBox::Cancel);
      }
      delete chave_Usuario;
      delete vetor_Usuario;
    
      model_Usuarios->setQuery("select * from usuarios order by id_usuarios");//Atualiza o grid
      tableView_Usuarios->show();
      limpa_Tela();
    }
    
    
    void fCadUsuarios::exclui_na_Tabela()
    {
      query_Usuarios->prepare("delete from usuarios where id_usuarios=:cod");
    
      query_Usuarios->bindValue(":cod", edit_Codigo->text());
      if (!query_Usuarios->exec())
        QMessageBox::critical(0,"Sistema de Criptografia",query_Usuarios->lastError().text(),QMessageBox::Cancel); 
    
      model_Usuarios->setQuery("select * from usuarios order by id_usuarios");
      tableView_Usuarios->show();
      limpa_Tela();
    }
    
    void fCadUsuarios::limpa_Tela()
    {
      //Limpa os campos de edição.
      edit_Codigo->clear();
      edit_Nome->clear();
      edit_Senha->clear();
      edit_Codigo->setFocus();
    }
    
    int fCadUsuarios::gera_Aleatorio(int min,int max,int semente)
    {
    
      int r;
    
      if(!semente)
      {
        srand((unsigned)semente);
      }
    
      r=min+rand()%(max-min+1);
      
      return r;
    }


    O cadastro de usuários ira cadastrar os usuarios e gerar suas senhas.
    No contructor ele testa o nível de acesso e bloqueia os botões se necessario, instancia um objeto da classe pesq_senha que contem as definições das funções QCA.
    Depois conecta os botões aos slots, cria os QbuttonGroup para agrupar os radio botões, cria um QsignalMapper para que dois botões acessem o mesmo método podendo-se identificar dentro do método(ou slot) quem chamou.
    A função mais importante e a grava_na_Tabela(int tipo), onde sera gerada a senha criptografada e gravada no banco. Começa testando se foi escolhido hash ou criptografia, se foi hash gera uma seqüência de numeros aleatórios entre 69 a 90, seus respectivos caracteres ascii são concatenados a senha digitada(o sal).
    Observe que você pode alterar este processo, pode usar a tabela ascii inteira se quiser ou ao invés de dez caracteres pode usar mais ou menos.
    Também pode usar este processo na senha criptografada com chave e não somente no hash.
    Após isto a função gera_hash_Senha é chamada, função esta definida na classe pesq_Senha que teve um objeto derivado instanciado no constructor, testa_Senha.
    Então chamamos testa_Senha->gera_hash_Senha(senha,0) ou testa_Senha->gera_hash_Senha(senha,1), sendo os parâmetros senha a senha digitada pelo usuário concatenada com a seqüência de caracteres aleatórios gerados, e os numeros 0 ou 1 a escolha entre sha1 ou md5.
    A função retorna o valor para uma variavel QbyteArray, senha_Cripto, byte array porque definimos o campo senha na tabela como byte array também.
    Se foi escolhido criptografia com chave, é chamada a função criptografa, também definida em pesq_Senha, recebe como parâmetros a senha digitada e ponteiros para as variáveis chave e vetor, a função gera a chave e o vetor e o valor é retornado pelos ponteiros, a função retorna a senha criptografa para a variável senha_Cripto.

    Após isto inicia-se o processo de gravar na tabela, passa-se os parâmetros ao objeto QsqlQuery, query_Usuarios, conforme for hash ou criptografia com chave, query_Usuarios chama as funções insere_Usuario ou altera_Usuario definidas dentro do PostgreSQL, para saber se esta inserindo ou alterando testa-se a variavel tipo que é passada como parâmetro pelo botão escolhido, por isso usamos QsignalMapper para mapear os sinais e sabermos qual botão chamou o SLOT, assim economizamos código.
    Abaixo, segue um exemplo de como gravar sem usar as procedures do banco, usando somente código Qt, o exemplo não contem todos os campos da tabela, é apenas para ilustrar o que a Qt oferece neste caso.
    Desta forma você terá de explicitar a transação:

    bool gravou,gravou2=false;
      QSqlDatabase::database().transaction();
    
      QString erro;
    query_Usuarios->prepare(" insert into usuarios (id_usuarios,nome,senha,nivel,tipo_senha) values (:cod,:nom,:sen,:niv,:tp) ");
      query_Usuarios->bindValue(":cod", edit_Codigo->text());//Parâmetros.
      query_Usuarios->bindValue(":nom", edit_Nome->text());
      query_Usuarios->bindValue(":sen",senha);
      query_Usuarios->bindValue(":niv", 1);
      switch (group_Tipo_Senha->checkedId())
      {
        case 0:
        {
          query_Usuarios->bindValue(":tp", 0);
          break;
        }
        case 1:
        {
          query_Usuarios->bindValue(":tp", 1);
          break;
        }      
      }
      gravou=query_Usuarios->exec();
      if (! gravou)
      {
        erro=query_Usuarios->lastError().text();
        erro.append(" ");
      }
      QSqlQuery* query_Complemento=new QSqlQuery;
      query_Complemento->prepare(" insert into codigos (id_codigos,codigo,chave) values (:id,:cod,:chv) ");
      query_Complemento->bindValue(":id", edit_Codigo->text());
      query_Complemento->bindValue(":cod",complemento);
      switch (group_Tipo_Senha->checkedId())
      {
        case 0:
        {
          query_Usuarios->bindValue(":chv","0");
          break;
        }
        case 1:
        {
          query_Usuarios->bindValue(":chv",*chave_Usuario);
          break;
        }      
      }
      gravou2=query_Complemento->exec();
      
      if (gravou && gravou2)
        QSqlDatabase::database().commit();
      else
      {
        QSqlDatabase::database().rollback();
        erro.append(query_Complemento->lastError().text());
        QMessageBox::critical(0,"Sistema de Criptografia",erro,QMessageBox::Cancel);
      } 


    Observe que temos de usar dois objetos QsqlQuery e testar se a gravação ocorreu nos dois para usarmos commit() ou rollback(), usando as procedures definidas dentro do banco não temos este trabalho.

    Podemos usar também uma única procedure no banco, testa-se o valor da variável tipo como no caso de duas procedures, mas chama-se apenas uma única procedure com um parâmetro a mais, que é o próprio tipo, porem esta nova procedure tem de ser definida no banco usando a linguagem plpgsql, ao contrario das duas anteriores que usam sql, porque neste caso temos de usar estruturas de controle e condicionais com “if”, para decidirmos dentro da procedure se a operação é de inserção ou alteração de dados mas o resultado é mais econômico em termos de codigo a ser escrito e mais elegante também:

    CREATE OR REPLACE FUNCTION insere_altera_usuario(integer,varchar(20),bytea,integer,integer,varchar(100),bytea,bytea,integer,integer) RETURNS void AS $$
    DECLARE
      codigo_usuario integer;
      nome_usuario varchar(20);
      senha_usuario bytea;
      nivel_usuario integer;
      tipo_senha_usuario integer;
      complemento_usuario varchar(100);
      chave_usuario bytea;
      vetor_usuario bytea;
      tipo_hash_usuario integer;
      controle integer;
    BEGIN
      codigo_usuario:=$1;
      nome_usuario:=$2;
      senha_usuario:=$3;
      nivel_usuario:=$4;
      tipo_senha_usuario:=$5;
      complemento_usuario:=$6;
      chave_usuario:=$7;
      vetor_usuario:=$8;
      tipo_hash_usuario:=$9;
      controle:=$10;
      if controle=1 then
        insert into
          usuarios
          (id_usuarios,
           nome,
           senha,
           nivel,
           tipo_senha)
        values
          (codigo_usuario,nome_usuario,senha_usuario,nivel_usuario,tipo_senha_usuario);
        insert into
          codigos
          (id_codigos,
          codigo,
          chave,
          vetor,
          tipo_hash)
        values
          (codigo_usuario,complemento_usuario,chave_usuario,vetor_usuario,tipo_hash_usuario);
      else
        if controle=2 then
          update
            usuarios
          set
            nome=nome_usuario,
            senha=senha_usuario,
            nivel=nivel_usuario,
            tipo_senha=tipo_senha_usuario
          where
            id_usuarios=codigo_usuario;
          update
            codigos
          set
            codigo=complemento_usuario,
            chave=chave_usuario,
            vetor=vetor_usuario,
            tipo_hash=tipo_hash_usuario
          where
            id_codigos=codigo_usuario;
        else
          raise exception 'Informe corretamente o parametro da operação: Inserir=1, alterar=2';
        end if;
      end if;
    END;
    $$ LANGUAGE plpgsql;
    


    Dentro do aplicativo a chamada da procedure fica assim:

    if (tipo==1)//Testa se esta incluindo ou alterando.
      query_Usuarios->prepare("select insere_altera_usuario(:cod,:nom,:sen,:niv,:tp,:comp,:chv,:vet,:hash,1)");
    else
      if (tipo==2)
        query_Usuarios->prepare("select insere_altera_usuario(:cod,:nom,:sen,:niv,:tp,:comp,:chv,:vet,:hash,2)");
    


    pesq_Senha.h

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado   *
     *   rodolfo@Programador   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    #ifndef PESQ_SENHA_H
    #define PESQ_SENHA_H
    
    
    class QString;
    class QbyteArray;
    
    class pesq_Senha
    {
      public:
        pesq_Senha();
        ~pesq_Senha();
        QByteArray gera_hash_Senha(QString senha,int tipo_Hash);
        QString descriptografa(QByteArray senha,QByteArray chave_Usuario,QByteArray vetor_Usuario);
        QByteArray criptografa(QString senha,QByteArray* chave_Usuario,QByteArray* vetor_Usuario);
    };
    
    
    #endif


    pesq_Senha.cpp

    /***************************************************************************
     *   Copyright (C) 2009 by Rodolfo Ribeiro Machado                         *
     *   rodolfo@Programador                                                   *
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    
    #include "pesq_Senha.h"
    #include <QString>
    #include <QMessageBox>
    #include <QtCrypto>
    
    pesq_Senha::pesq_Senha()
    {
    }
    
    pesq_Senha::~pesq_Senha()
    {
    }
    
    QByteArray pesq_Senha::gera_hash_Senha(QString senha,int tipo_hash) 
    {
      QByteArray result="";
      QCA::SecureArray arg = senha.toAscii();
      int meio=arg.size()/2;
      switch (tipo_hash)
      {
        case 0:
        {
          if( !QCA::isSupported("sha1") )
            QMessageBox::information(0,"Sistema de Criptografia","SHA1 não suportado");
          else
          {
           QCA::Hash senha_hash("sha1");
           senha_hash.update(senha.toAscii());
           result=senha_hash.final().toByteArray();
          }
          break;
        }
        case 1:
        {
          if( !QCA::isSupported("md5") )
            QMessageBox::information(0,"Sistema de Criptografia","MD5 não suportado");
          else
          {
            QCA::SecureArray part1(arg.toByteArray().left(meio)); 
            QCA::SecureArray part2(arg.toByteArray().mid(meio)); 
    
            QCA::Hash hashObject("md5");
            hashObject.update(part1);
            hashObject.update(part2);
            QCA::SecureArray resultArray = hashObject.final();
            result = resultArray.toByteArray();
            break;
          }
        }
      }
      return result;
    }
    
    QString pesq_Senha::descriptografa(QByteArray senha,QByteArray chave_Usuario,QByteArray vetor_Usuario)
    {
      QString result="";
    
      if(!QCA::isSupported("aes128-cbc-pkcs7"))
        QMessageBox::information(0,"Sistema de Criptografia","Método AES128-CBC não suportado"); 
      else
      {
        QByteArray chave=chave_Usuario;
        QCA::SymmetricKey key(chave);
    
        QByteArray vetor=vetor_Usuario;
        QCA::InitializationVector iv(vetor);
    
        QCA::Cipher cipher(QString("aes128"),QCA::Cipher::CBC,QCA::Cipher::DefaultPadding,QCA::Encode,key,iv);
    
        cipher.setup( QCA::Decode, key, iv );
            
        QCA::SecureArray cipherText = senha;
    
        QCA::SecureArray plainText = cipher.update(cipherText);
    
        if (!cipher.ok()) 
        {
          QMessageBox::information(0,"Sistema de Criptografia","Update falhou");
        }
    
        plainText = cipher.final();
        if (!cipher.ok())
        {
          QMessageBox::information(0,"Sistema de Criptografia","Final falhou");
        }
    
        result=plainText.data();
      }
    
      return result;
    }
    
    QByteArray pesq_Senha::criptografa(QString senha,QByteArray* chave_Usuario,QByteArray* vetor_Usuario)
    {
      QByteArray result;
      
      QCA::SymmetricKey key(16);
      *chave_Usuario=key.toByteArray();
    
      
      QCA::InitializationVector iv(16);
      *vetor_Usuario=iv.toByteArray();
    
      QCA::SecureArray arg=senha.toAscii();
    
      QCA::Cipher cipher(QString("aes128"),QCA::Cipher::CBC,QCA::Cipher::DefaultPadding,QCA::Encode,key,iv);
      QCA::SecureArray u = cipher.update(arg);
    
      if (!cipher.ok())
      {
        QMessageBox::information(0,"Sistema de Criptografia","Update falhou");
        result="";
      }
      else
      {
    
        QCA::SecureArray f = cipher.final();
    
        if (!cipher.ok())
        {
          QMessageBox::information(0,"Sistema de Criptografia","Final falhou");
          result="";
        }
        else
        {
          QCA::SecureArray cipherText = u.append(f);
          result=cipherText.toByteArray();
        }
      }
      return result;
    
    }


    As funções da biblioteca QCA. Eu adaptei dos exemplos disponíveis na documentação, mais especificamente os arquivos hashtest.cpp e ciphertest.cpp.
    Lá você encontrara os comentários e as explicações sobre as funções e seu funcionamento.

    Abaixo, imagens do aplicativo rodando.(Clique na imagem para ampliar).

    >Aplicativo rodando

    >Cadastro de usuários

    Obs: Baixe aqui os fontes do aplicativo(os fontes são deste mais o proximo tutorial).


    Gostou deste tutorial?, ele foi útil para você, no seu trabalho ou nos seus estudos? De a sua opinião:

    Tutorial anterior: Compilando a Qt no Windows®
    Próximo Tutorial: Bloqueando um sistema Qt após inatividade do mouse ou teclado.

    Pagina inicial


Comentários:


Nenum comentário

Deixe seu comentário:


 *
 *

 *
  Campos marcados com * são obrigatórios.