Buscar

ATPS Desenvolvimento de Software Seguro - Etapa 2

Prévia do material em texto

FACULDADE ANHANGUERA – UNIDADE CAMPINAS I
TECNOLOGIA EM ANÁLISE E DESENVOLVIMENTO DE SISTEMAS
DESENVOLVIMENTO DE SOFTWARE SEGURO – ETAPA 02
ALUNO RA
QUEMUEL SANTOS DE AQUINO 8309737322
PAULO VICTOR DE MENEZES 2848233367
SIDMAR PORFÍRIO 7989711257
LEONARDO CUENCAS 7700620660
ATIVIDADE PRÁTICA SUPERVISIONADA (ATPS)
TUTOR: PROF. RICARDO AUGUSTO DA SILVA
CAMPINAS, 27 DE MAIO DE 2015.
SUMÁRIO
INTRODUÇÃO .........................................................................................................................................3
CRIPTOGRAFIA NOS DIAS DE HOJE ......................................................................................................15
USANDO CRIPTOGRAFIA BASE64 NAS LINGUAGENS JAVA E ASP .........................................................18
RELATÓRIO 01 – UTILIZANDO CRIPTOGRAFIA .....................................................................................30
REFERÊNCIAS BIBLIOGRÁFICAS ............................................................................................................47
2
INTRODUÇÃO
A necessidade de troca de informações entre os seres humanos, sem perigo de interceptação,
existe desde os tempos de Roma antiga. Foi lá que surgiu o código de César, que consistia
numa forma de embaralhar as letras de uma mensagem. Com o aparecimento dos
computadores, novas formas de codificar a informação foram criadas, de modo que os
usuários pudessem se comunicar, com proteção. Entretanto, essa tecnologia estava restrita a
governos e organizações militares.
O desejo de enviar mensagens de forma segura sem que ninguém mais, a não ser o
destinatário, consiga ler, é tão antigo quanto a invenção da escrita. 
Abaixo estremos descrevendo de forma clara e objetiva informações sobre procedimentos de
segurança usados em sistemas e em soluções web.
INTRODUÇÃO À CRIPTOGRAFIA
Em guerras primitivas tem-se relato de generais que raspavam a cabeça de um escravo e sobre
a cabeça escreviam mensagens secretas de guerra. Após o cabelo crescer, o emissário era
enviado (esta técnica de esconder mensagens é chamada de esteganografia e sua versão
moderna consiste em esconder mensagens em imagens ou músicas).
Basicamente existem duas formas primárias de obscurecer uma mensagem (criptografar), que
pode ser pelo uso de substituição ou transposição. Na substituição troca-se alguma coisa por
outra, de acordo com algum critério. Os passatempos encontrados em bancas de revistas
exploram a substituição, onde o leitor é levado a descobrir que onde tem triângulo deve ser
considerado a letra "A", por exemplo.
Já na transposição apenas troca-se as coisas de lugar. Usando transposição simples, pode-se
escrever a palavra "teste" como "ettse", transpondo uma letra com a sua vizinha.
3
O método matemático de substituição para inviabilizar a leitura mais antigo que se tem
registro é a cifra de César. Elaborada por Júlio César, esta cifra inaugurou as chamadas cifras
de substituição monoalfabéticas.
Na infantil Cifra de César, cada letra do alfabeto é substituída pela sua terceira vizinha, ou
seja, o "A" é transformado em "D", em um alfabeto circular, onde o "Z" vira "C"
(...VWXYZABC...), conforme ilustrado na figura. Nota-se aí a existência de duas operações
distintas:
a) cifrar: para cifrar uma mensagem, deve-se substituir cada letra pela terceira vizinha
subsequente. Em C usando string isto poderia ser feito com:
strCIF[i] = strTXT[i] + 3;
No entanto, um programador experiente verá erro na fórmula acima: ela só funciona até o
"W" que somado com 3 virará "Z". Para as letras "X", "Y" e "Z" a fórmula é falha! Aí entraria
em ação a operação de módulo, muito utilizada em criptografia (fica como dever de casa).
b) decifrar: já para recuperar a mensagem outro método matemático precisa ser usado. Ao
invés de trocar pela terceira subsequente, deve-se trocar pela terceira anterior ("D" deve virar
"A"). Percebe-se nitidamente duas operações distintas para cifrar ou decifrar.
Uma variação interessante da cifra de césar é a Rot13, onde se troca pela décima terceira
vizinha. Ela é interessante pois como o alfabeto tem exatos 26 letras, o processo de cifrar e
decifrar é o mesmo! Para cifrar basta achar a vizinha 13 subsequente e para decifrar também
(a vizinha de "A" é "M" e a vizinha de "M" é o "A").
Ninguém irá cifrar nada sério usando César. Serve apenas como fundamento didático para
explicar alguns conceitos, como o de chave, por exemplo. 
Atualmente a criptografia consiste em uma série de fórmulas matemáticas, em que se utiliza
um segredo (chamado de chave) para cifrar e decifrar a informação. Este segredo pode ser o
4
mesmo para as duas operações (criptografia simétrica), ou pode haver segredos diferentes, um
para cifrá-la e outro para decifrá-la, ou vice-versa (criptografia assimétrica).
Por meio de um software especial e um par de chaves, um usuário pode então utilizar essa
tecnologia, para proteger suas informações pessoais.
O par de chaves possui as seguintes características:
Chave pública - disponível para qualquer usuário que queira se comunicar de forma segura;
Chave privada - chave secreta, que somente o dono do par de chaves conhece.
Através de software e um par de chaves, um usuário pode:
- Assinar digitalmente um documento (garantir que o documento não foi alterado após a
assinatura e que o mesmo foi assinado pelo dono do par de chaves que foi usado);
- Cifrar um documento, de modo que só o dono do par de chaves destino seja capaz de ler a
informação cifrada;
- Assinar e cifrar um documento.
Infelizmente, o uso do software apenas garante que o dono do par de chaves realizou as
operações, e não que o par de chaves em questão pertence realmente a uma pessoa. Não existe
nenhum mecanismo que impeça a um usuário gerar um par de chaves com o nome de outro
usuário. Para resolver esse problema, utilizam-se técnicas como certificação digital (versão
eletrônica para os já conhecidos cartórios) e web-of-trust, que consiste em criar uma cadeia de
confiança entre diferentes chaves. Este conceito será visto, adiante, com mais detalhes.
- PGP e GnuPG
O PGP surgiu inicialmente como um produto gratuito, disponível para diversas plataformas.
Entretanto, a partir de 2004 o PGP deixou de ser gratuito, o que fez com que muitos usuários
de tecnologia migrassem para outros produtos, como o GnuPG (gpg), que é uma versão
software livre do PGP, disponível para diversas plataformas. O gpg e o pgp são compatíveis,
de modo que, a partir de um dos programas, é possível cifrar, decifrar, assinar e verificar
assinaturas entre eles.
5
- Teia de confiança (Web of Trust)
Conforme foi dito, anteriormente, existe um problema em se certificar que uma determinada
chave realmente pertence a um usuário, site ou empresa. Essa dificuldade não é relativa
apenas ao PGP, mas a qualquer sistema que use criptografia. Uma das soluções mais comuns
consiste na compra de um certificado de uma entidade certificadora, que funciona como um
cartório digital, atestando que uma determinada chave realmente pertence a um usuário
específico.
Como no PGP, a idéia é disponibilizar criptografia para o público em geral, sem custos. Para
isso, foi criado um método alternativo para se verificar se uma chave pertence a uma
determinada pessoa, chamado web-of-trust. Neste método, a confiança vai sendo estabelecida
através de uma rede de transitividade, onde se A confia em B e B confia em C, então A confia
em C. Essa rede é construída por meio de uma relação pessoal entre dois indivíduos,
constatação da identidade da chave, e assinatura da chave pública de um usuáriopelo outro.
Essas etapas acabam por gerar um laço de confiança. Essas relações de confiança convertem-
se, então, em uma rede de confiança, como pode ser visto abaixo.
Após a verificação da identidade do par de chaves, o próximo passo é a publicação desta
chave num servidor de chaves, de modo que qualquer um que queira se comunicar possa obter
facilmente a chave pública deste usuário. Quando uma chave é publicada, as assinaturas das
pessoas que confiaram nesta chave também são publicadas, de modo que basta ao usuário
confiar em uma das assinaturas da chave para que passe a confiar na chave.
- Servidor de Chaves
Um servidor de chaves consiste em um software especial que possui uma interface via web,
ou via e-mail, capaz de cadastrar, buscar e invalidar chaves públicas de usuários em qualquer
parte do mundo. Estes servidores normalmente formam uma rede, de forma que, se um
usuário publicar uma chave em um servidor na Espanha, por exemplo, esta atualização seja
propagada para todos os servidores participantes da rede.
Você provavelmente chegou neste texto, por causa do servidor de chaves do CAIS/RNP, que
pode ser encontrado no endereço: http://www.rnp.br/keyserver.
6
O servidor do CAIS pertence à rede mundial, e, através dele, é possível consultar, adicionar e
cancelar chaves na rede mundial de servidores. Para mais informações sobre o funcionamento
específico do servidor de chaves, deve-se consultar a documentação incluída no endereço
acima. Para outras informações a respeito de PGP e GPG, as referências podem ser
consultadas na margem direita da página http://www.rnp.br/keyserver.php
- Conceito de chave
A Cifra de César não tem chave. Toda a "segurança" está na descoberta do método
matemático. Se alguém descobrir que deve-se somar 3, acabou o algoritmo.
Outro problema da Cifra de César é que se eu compartilho o método com 10 pessoas, todas as
10 estarão aptas a ler todas as mensagens. Mas se eu quisesse, usando esta cifra, trocar uma
mensagem cifrada com a pessoa A, mas de forma que a pessoa B não possa ler, mesmo
sabendo o método?
Uma variação da Cifra de César consiste em trocar a letra pela vizinha k ao invés da 3. Se eu
usar k como 3, terei que o "A" troca por "D". Se eu usar k=6, terei que o "A" troca por "G", e
assim por diante. A ideia deste algoritmo é que todos conheçam o método (trocar por uma
vizinha), mas ninguém sabe por qual vizinha é.
Neste caso eu poderia combinar com a pessoa A que sempre que enviasse uma mensagem
para ela eu usaria k como 3. Já com a pessoa B eu combino k=6. Todos conhecem o método,
mas a pessoa B não saberia como ler mensagens que enviei a pessoa A pois ela não sabe que
com A eu usei k=3.
Eis o conceito de chave. O método é o mesmo, mas a chave (k) varia.
Considerando que a pessoa B queira muito ler a mensagem que eu enviei para a pessoa A,
tudo que ela precisa é descobrir qual valor de k eu usei para o A, já que o método é o mesmo.
A ideia é que não exista segredo algum no método de criptografia (muito embora alguns
fabricantes insistem em basear sua segurança nisto).
7
E como pode ser feita esta descoberta? Uma das forma é através da força bruta.
 
- Ataque por força bruta
Neste tipo de ataque testa-se todas as possibilidades de chave possíveis, até encontrar uma que
sirva.
Voltando ao infantil método de César com a modificação e presença do k, se B quisesse muito
saber o que escrevi para A, ele poderia tentar usar os possíveis valores de k até que a
mensagem faça sentido. Neste caso k pode assumir uma variedade limitada de valores. Ele
pode ser 0 (onde "A" seria trocado pelo mesmo "A". Ridículo, mas matematicamente é uma
chave viável), pode ser 1, 2, 3, ... 25, sendo que neste último o "A" seria trocado pelo "Z".
Veja que existem apenas 26 possíveis valores para k neste método. Um ataque por força bruta
não exigiria mais que 26 tentativas.
Matematicamente se usa a estatística aqui, sendo que ninguém é tão sortudo para acertar na
primeira tentativa e nem tão azarado para acertar na última! Na média se considera que o
ataque terá sucesso em 13 tentativas.
Logo, neste caso, um ataque por força bruta terá um esforço médio de 13 tentativas. Se eu
usar papel e caneta (como devia ser no tempo de César) e conseguir testar no ritmo de uma
tentativa por minuto, levaria 13 minutos em média para quebrar o algoritmo. Este seria o
esforço de um ataque de força bruta à Cifra de César, o que o torna, como disse, infantil.
Mas se eu mudasse a cifra monoalfabética um pouco. Se ao invés de ter que trocar uma letra
por uma k vizinha eu usasse uma tabela de trocas:
Letra A B C D E F G H I J K L M ...
troca X J M A F W C N O K B E T ...
Será que um ataque de força bruta seria possível com este novo algoritmo?
8
Um ataque de força bruta sempre é possível, a questão é quanto tempo em média ele levaria.
O tempo é de acordo com as possíveis combinações de chave. Neste caso de um algoritmo
monoalfabético com uma tabela de troca, cada letra pode ser uma das outras 25, sem poder
repetir, ou seja, se já decidi que "A" troca pelo "X", ninguém mais poderá ser trocado pelo
"X". A matemática nos ensina que neste caso haverão 26! possibilidades (fatorial de 26) para
a chave.
As aparências enganam, pois fatorial de 26 resulta em 403.291.461.126.605.635.584.000.000
possíveis valores para chave e isto é muito, mas muito mesmo.
Como calcular este fatorial no Linux? Usando bc, uma alternativa bem criativa é com o
comando:
$ seq -s '*' 1 26|bc
O comando seq irá gerar a sequência:
1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20*21*22*23*24*25*26
que será avaliada pelo bc.
Se eu tivesse um computador capaz de realizar 1 bilhão de tentativas por segundo
(1.000.000.000) e tiver 1 bilhão destes computadores para uso, o ataque de força bruta ainda
levaria 201.645.730 segundos, ou aproximadamente 6 anos de processamento!
Verifique você mesmo no bash:
$ echo "403291461126605635584000000 / 2 / 1000000000 / 1000000000 / 60 / 60 / 24 / 365"
| bc
Divido o número de tentativas por 2, pois na média se acerta na metade das tentativas, divido
pela quantidade de quebras por segundo de um computador, que é um bilhão, divido pelo
número de computadores que tenho. Depois divido por 60 para transformar segundos em
9
minutos, por 60 novamente para transformar em horas, por 24 tem-se dias e dividindo por 365
tem em anos.
Logo, por força bruta, este algoritmo de substituição monoalfabética é infalível e inquebrável,
apenas com o desconforto de ter que memorizar uma k que é uma tabela.
Costumo instigar meus alunos neste momento perguntando-lhes se está aí o algoritmo perfeito
e inquebrável! Então?
Por força bruta, SIM, eis um algoritmo ideal. Só que existem outros meios de se quebrar um
algoritmo de cifra que é através da criptoanálise. 
- Ataque por criptoanálise
O ataque por força bruta é possível em todos os casos. Ainda não existem algoritmos imunes a
este ataque, ou seja, ele sempre poderá ser tentado. A questão é o tempo que levará. Deseja-se
um algoritmo com tantas possibilidades de chave que o ataque levaria anos ou mesmo
milhares de anos, de sorte que ninguém sequer irá tentar este ataque por ser inviável (em
tempo: estudos sobre computação quântica sugerem a possibilidade da construção de
algoritmos de criptografia imunes ao força bruta).
A cifra de substituição monoalfabética com uma tabela de trocas, citada no capítulo anterior, é
um caso de um algoritmo para o qual demonstrou-se que o foça bruta é inviável. Mas existe a
criptoanálise.
Simon (ver referências) define a criptografia como a eterna guerra entre criptógrafos e
criptoanalistas.Um constrói para que o outro descubra como destruir! No caso mais
específico da substituição monoalfabética ela é frágil e sucumbe ao ataque de "Análise de
Frequência".
Notoriamente todos começam tentando a letra "A" ou, no máximo, indo pelas vogais. Porque?
Ora, pelo simples fato de que em um texto em português a letra "A" é a letra que mais se
repete! É improvável que um texto não possua uma boa quantidade de letras "A"s. Isto é
análise de frequência.
10
Se estou tentando quebrar uma cifra monoalfabética (onde uma letra trocou por outra),
sabendo que o texto é em português a primeira coisa que faço é contar as letras. Se, e ao
contar, descubro que a mais repetida é o "W" é provável que o "W" está substituindo a letra
"A". Onde tem "W" coloco "A" e vou brincando até o texto fazer algum sentido para mim. 
Todo o algoritmo de substituição monoalfabético é vulnerável ao ataque de análise de
frequência. Se o texto for em inglês, sabe-se que a letra que mais se repete é o "E".
Mas entenda que substituição monoalfabética não quer dizer apenas letras assim como análise
de frequência não se aplica somente a letras que mais se repetem! Posso cifrar o arquivo
binário com uma tabela de 256 trocas, onde o byte 00000000 será trocado por 00110110, por
exemplo, e assim por diante.
Ainda é considerado substituição monoalfabética. No caso, se era um executável, posso
realizar análise de frequência nele, descobrir quais os bytes mais presentes em um EXE típico
e usar o mesmo princípio! Fabricantes de processadores fazem isto para descobrir as
instruções mais usadas em programas a fim de otimizá-las.
Logo, foi para o brejo nosso algoritmo perfeito: ele perece pelo ataque de "Análise de
Freqüência".
Alternativas interessantes ao longo da história foram usadas para melhorar a cifra
monoalfabética como variar a troca para os mais frequêntes. Como exemplo, a letra "A",
sendo a que mais se repete, pode ser trocada por "X", por "#" ou por "*". Isto, se bem usado,
evita a análise de freqüência, mas impõe alguns problemas, como o fato do alfabeto de troca
ter que possuir mais símbolos que o original. Enfim, a cifra de Viginère resolveu de forma
criativa este problema e ficou inquebrável por séculos até que Babage a quebrou usando,
pasmem, Análise de freqüência (Simon).
Ainda existem outras técnicas de criptoanálise como por exemplo as colas. Chama-se de cola
quando um atacante tem uma vaga ideia de trechos da mensagem. 
11
Na segunda guerra mundial os alemães pisaram na bola neste sentido, pois toda a
comunicação tinha a saudação nazista, logo ao menos um trecho da mensagem era previsível
(Simon). 
Hoje os modernos algoritmos de cifras devem ser imunes as colas, ou seja, mesmo que eu lhe
dê centenas de textos cifrados e as suas centenas de textos originais correspondentes, ainda
assim você não deve ser capaz de descobrir qual chave eu usei (esta propriedade é muito
interessante e parece utó[*****], mas não é). 
- Simétricos ou assimétricos?
método: o algoritmo matemático que faz a cifra e a decifragem. Não deve ser segredo.
chave: único segredo. Uma informação que alimenta o algoritmo para cifrar ou decifrar.
Quanto mais possibilidades, mais inviável é o força bruta. 
Assim como uma porta de uma casa com sua fechadura que só pode ser aberta por quem tiver
a chave. Considerando que a fechadura é a prova de criptoanálise, ou seja, a prova de
"chaveiros", a única forma de abrir a porta sem a chave correta é se um chaveiro viesse com
uma sacola com todas as possíveis chaves para aquela fechadura e ficasse tentando até uma
servir. Se forem bilhões de chaves possíveis, torna inviável (isto seria um força bruta).
Mas veja que neste exemplo a mesma chave que fecha também abre. Existe apenas uma chave
e ela serve para trancar a porta e também para destrancá-la! Neste caso tem-se o que se chama
de algoritmo simétrico, onde existe apenas uma única chave e ela serve para cifrar e decifrar.
Um problema crônico existe com os algoritmos simétricos e ele atormentou os cientistas
durante muito tempo: como pode ocorrer de forma segura a troca das chaves?
Se eu quero trocar uma mensagem com a pessoa B como conto ao B que usarei k=10 para
cifrar a mensagem? Se tiver a oportunidade de me encontrar pessoalmente com ele, ok,
converso de forma reservada e lhe conto a chave. Mas e se ele estiver distante, sem a
possibilidade de um encontro pessoal? Telefono para ele? E se alguém estiver com uma
12
escuta telefônica? Envio um email? Veja, não tem como realizar esta comunicação de forma
segura.
Este problema atormentou muita gente. Na segunda guerra o livro dos códigos da Enigma,
com chaves diárias de um mês inteiro, era entregue em mãos aos operadores de rádio. Este
tinha ordens para, sob ataque, imediatamente destruir a máquina e o livro. Se os aliados
tivessem acesso a este livro seriam capazes de quebrar as comunicações de um mês inteiro (o
filme "U-571 - A Batalha do Atlântico" explora este fato, onde os aliados disfarçam um
submarino como sendo alemão a fim de tentar capturar a Enigma).
Simon conta detalhes deste fato histórico e revela que uma das primeiras formas que os
aliados acharam para ler as mensagens foi subornar um oficial alemão (depois eles quebraram
com colas e depois com máquinas implementadas por Alan Turing chamadas de "bombas")!
Seria possível a troca de chaves de forma segura usando um meio de comunicação inseguro?
Isto se consagrou como matematicamente impossível, exceto para os "loucos" Marin Hellman
e Whitfield Diffie. Loucos pois aparentemente estavam pesquisando algo impossível, para o
qual já se havia desistido: um algoritmo de troca de chaves seguro em um meio inseguro.
Este artigo ficaria muito longo se começasse a contar a história do famoso algoritmo Diffie-
Hellman e o quanto ele revolucionou a ciência da criptografia. Cabe apenas ressaltar o
raciocínio que embasou estes "loucos". Eles pensaram facilmente em um protocolo de troca
de informações sigilosas via correio. É interessante para ilustrar.
Imagine que quero enviar documentos sigilosos pelo correio para "P" e que eu não confio no
carteiro. Colocar os documentos em uma caixa com cadeado não serve, pois como enviaria a
chave do cadeado para o "P"? Pelo correio? Por teletransporte? Se não confio no meio, não
adianta variar os métodos usando este meio de transporte no qual não confio. Estamos falando
de criptografia onde se leva as teorias ao extremo.
13
Mas DH pensaram em um método (eu sou o "E" enviando documentos para "P"):
- "E" compra um cadeado em uma ferragem;
- "E" coloca os documentos em uma caixa e coloca o seu cadeado. Pressupõe-se que tanto a
caixa como o cadeado são invioláveis e que a única forma de abrir a caixa é com a chave;
- "E" envia a caixa pelo Correio. Veja que ninguém poderá abir, pois "E" não enviou a chave.
O carteiro curioso nada pode fazer;
- "P" recebe a caixa. Bom, "P" também não a pode abrir pois ele não tem a chave (surpreso?).
Mas "P" ao invés de abrir, compra o seu cadeado e coloca ele também na caixa. A caixa agora
tem DOIS cadeados: o de "E" e o de "P";
- "P" devolve a caixa pelo correios ao "E";
- Legal, agora nem mesmo "E" consegue abrir sua caixa. Mas ele apenas usa a sua chave para
tirar o seu cadeado, mantendo o cadeado de "P";
- "E" devolve a caixa para "P" que agora pode abrí-la. 
Observe que neste protocolo jamais houve uma troca de chaves. DH pensavam que deveria
existir um princípio matemático que reproduzisse este efeito e depois de muita pesquisa o
encontraram na aritmética modular usando números primos(mais tarde se soube que eles não
foram pioneiros, pois pesquisas secretas neste sentido haviam sido realizadas muitos anos
antes).
Um algoritmo assimétrico, portanto, é quando se usa uma chave para cifrar, porém,
magicamente, esta chave não serve para abrir o que se cifrou. Não tem espaço aqui para
explicar isto em termos matemáticos (se alguém quiser, me peça), mas o fato é que tais
algoritmos existem.
Muitos chamam estes algoritmos de chave pública e privada, mas o nome técnico deles é
algoritmos assimétricos. Esta confusão no nome é porque uma das chaves, normalmente a que
serve para cifrar, é tornada pública e a outra é mantida sob sigilo conhecida como chave
privada (eu tenho que tomar muito cuidado quando falo disto. Sempre "chave privada". 
14
Digamos que eu queira enviar um documento para "P". Bastaria isto:
- "P" compra um cadeado e me envia ele ABERTO pelo correio. Não envia a chave;
- "E" recebe o cadeado aberto de "P" e o usa para fechar a caixa. Uma vez fechada, "E" já não
tem mais condições de abrí-la;
- "E" envia a caixa com o cadeado fechado de "P";
- "P" a abre pois tem a chave. 
Nesta analogia a chave pública seria o cadeado aberto: ele só serve para cifrar.
Estendendo a analogia, "P" poderia pedir a um fabricante de cadeados que confeccionasse
milhares de cadeados com a mesma chave. O cadeado de "P" estaria em todas as agências de
correios, por exemplo. Esta é a ideia.
Claro que para que isto funcione o cadeado deve ser forte e a chave deve possuir muitas
combinações, senão um ataque de força bruta seria viável. Deve também ser imune a
criptoanálise, ou seja, nenhum chaveiro do mundo seria capaz de construir uma nova chave
apenas analisando o cadeado.
Voltando aos termos "informáticos" a ideia do cadeado foi implementada pelo algoritmo RSA
baseado na fatoração de números primos. Constitui em duas chaves, uma chamada de Ke (K
para "e"ncriptar) e Kd (k para "d"ecriptar). Uma complementa a outra. O que cifra-se usando
Ke apenas com a Kd é possível recuperar (não vou descrever o funcionamento do RSA).
Um atacante conhece Ke pois a tornei pública. Contudo, mesmo tendo ela, ele está
computacionalmente inviabilizado de calcular Kd. RSA garante isto através do uso de
números primos gigantes, hoje com tamanhos de 512 bits. 
CRIPTOGRAFIA NOS DIAS DE HOJE
Na informática tudo se resume a bits. Em um algoritmo de criptografia o que se mede é o
tamanho da chave em bits. Se uma chave possui 3 bits, ela pode ser uma dentre os seguintes
valores:
15
000, 001, 010, 011, 100, 101, 110 e 111
Isto nos dá apenas 8 possibilidades para k. Na matemática, o número de possíveis chaves é de
2 elevando ao número de bits (23 = 8).
Assim sendo, se um algoritmo simétrico tiver 128 bits de chave, como é o caso do AES,
significa que ele tem 2128 possíveis chaves ou
340282366920938463463374607431768211456.
Como nosso computador testa 1 bilhão de possibilidades e dispõe de 1 bilhão destas para usar,
com este hardware eu levaria 170141183460469231731 segundos, já considerando a metade
das tentativas. Em uma calculadora, dividindo isto por 60 para ter minutos, por 60 novamente
para ter horas, por 24 para ter dias e por 365 para anos, chega-se a estrondosa quantia de
5.395.141.535.403 anos!
É pura falta de conhecimento quem afirma que algoritmos simétricos de 128 bits são
quebráveis por força bruta. Só mesmo Dan Brown em seu livro "Fortaleza Digital" e seu
computador quântico de bilhões de bits para quebrar tal algoritmo!
Se considerar o AES128, não estou dizendo que ele é inquebrável pois pode ter fraquezas
exploráveis por criptoanálise. Se houver ou (a) não se descobriu ou (b) quem descobriu não
nos contou! O que estou afirmando é ser inquebrável quanto a força bruta!
Literaturas afirmam que um algoritmos simétrico de 256 bits tem segurança eterna, pois nem
todo o silício do universo daria para construir uma máquina que o quebrasse (parenteses
agora: da maneira como se constrói computadores hoje em dia. Alguns cientistas vem falando
da computação quântica e de como ela mudaria a maneira de fazer as coisas. Computação
quântica é cercada de mistério e de exageros. O fato é que já existe uma máquina com sete
bits quânticos disponível).
Agora o que muitos realmente confundem é quanto a força bruta dos algoritmos assimétricos!
É muito, mas muito diferente!
16
Em um simétrico, se eu tenho 8 bits de chave, tenho 256 combinações possíveis.
Se pegar o caso do RSA que é um assimétrico e se tiver uma chave de 8 bits, significa que o
valor do N é de 256 bits. Sem querer descrever o RSA aqui, posso dizer que N é o resultado
da multiplicação de dois números primos de 4 bits. Quebrar a chave significa achar um destes
números. E não é de 2 elevado na 4 tentativas, pois os valores 2, 4, 6, 8, 9, 10 não precisam
ser testados pois não são primos!
No universo de 4 bits só existem estes primos: 2, 3, 5, 7, 11,13. Só! Mata-se em seis tentativas
no máximo!
Para algoritmos assimétricos, como no caso do RSA, o cálculo do esforço matemático não é
simplesmente de 2NumeroDeBits. É muito, mas muito menos que isto.
Exatamente por isto é que os algoritmos assimétricos precisam de uma chave muito maior,
hoje perto dos 1024 bits. No caso do RSA, dizer que ele é de 1024 bits, significa que o valor
de N é de 1024 bits, logo quebrar significa encontrar um número primo de até 512 bits que
sirva.
- Criptografia com Base64
A função base64_encode() retorna um dado condificado com base64. Esta codificação é
designada para que dados binários durem no transporte sobre camadas de transorte que não
são 8-bit clean, como mensagens de e-mail.
O método Base64 oferece maneira de descriptografar o código e torná-lo igual ao o que era
antes da ação. Exemplo:
<?php
// PARA ENCRIPTAR
$palavra = "palavra, valor ou texto a ser encriptado.";
echo base64_encode($palavra);
// RESULTADO:
 cGFsYXZyYSwgdmFsb3Igb3UgdGV4dG8gYSBzZXIgZW5jcmlwdGFkby4=
17
// PARA DESFAZER A OPERAÇÃO:
$palavra = "cGFsYXZyYSwgdmFsb3Igb3UgdGV4dG8gYSBzZXIgZW5jcmlwdGFkby4=";
echo base64_decode($palavra);
 
// RESULTADO: 
palavra, valor ou texto a ser encriptado.
?>
Com esses recursos é possível deixar a aplicação bem mais segura e, por que não, organizada.
USANDO CRIPTOGRAFIA BASE64 NAS LINGUAGENS JAVA E ASP
O envio e recebimento de informações sigilosas é uma necessidade desde tempos atrás. Com
o avanço da tecnologia, a chegada da internet e a facilidade de transmitir dados de maneira
extremamente rápida, a criptografia tornou-se uma ferramenta fundamental nos dias de hoje
para o envio e recebimento desse tipo de informação, onde apenas o emissor e o receptor
tenham acesso a essas informações.
A transmissão de dados e o armazenamento de informações têm que possuir segurança e para
isso ocorrer são utilizadas criptografias que ajudam a embaralhar as informações, evitando
que pessoas não autorizadas vejam. 
Como essa Etapa trata da parte prática, estaremos falando um pouco sobre este tipo de
solução nas linguagens e dando exemplos práticos.
- Criptografia Base64 em ASP.NET
O namespace System.Security.Cryptography é principal namespace usado para a criptografia,
de modo que você normalmente necessidade de adicionar uma declaração para este
namespace no topo do seu código para usar as classes contidas nele.
A criptografia trabalha com bits e bytes, de forma que em geral você deve converter os dados
para o formato binário antes de realizar a criptografia. Depois de descriptografar, você deve
converter os bytes descriptografados para o tipo original.
18
Na codificação, usando o mecanismo de codificação base64 por exemplo,não é preciso usar
uma chave de codificação ou decodificação. O mecanismo de codificação Base64 é usando
quando você precisa representar dados binários em um ambiente baseado em texto.
Dessa forma o Simple Mail Transfer Protocol (SMTP) é um protocolo baseado em texto para
o envio de e-mail. E para enviar um anexo binário você tem que codificar o anexo usando o
mecanismo base64.
Para converter para e a partir de base64 podemos usar a classe Convert. Veja exemplo abaixo
para VB .NET e C#:
Dim binario = New Byte() {1, 2, 3, 4, 5}
Dim codificacao = Convert.ToBase64String(binario)
Dim retornaBinario = Convert.FromBase64String(codificacao)
var binario = new Byte[] { 1, 2, 3, 4, 5 };
var codificacao = Convert.ToBase64String(binario);
var retornaBinario = Convert.FromBase64String(codificacao);
- Criptografia Simétrica
A Criptografia simétrica fornece o tipo mais rápido e mais básico de criptografia. Neste tipo
de criptografia, você usa a mesma chave secreta para criptografar e descriptografar dados.
A criptografia simétrica remonta ao Egito antigo, e a cifra de César foi uma maneira bem
conhecida de realizar a criptografia simétrica, que remonta a Júlio César. Você já ouviu falar
do anel decodificador secreto; você usa o mesmo anel decodificador secreto para criptografar
e descriptografar mensagens. 
Um anel decodificador secreto (ou decodificador secreto) é um dispositivo que permite
descodificar uma cifra de substituição simples, ou criptografar uma mensagem, trabalhando
no sentido oposto.
19
A Criptografia simétrica é rápida e existem muitos algoritmos simétricos; sendo tais
algoritmos complexos e eficientes, razão pela qual ainda é o núcleo de muitos protocolos de
comunicação criptografada.
A Criptografia simétrica usa uma chave secreta para criptografar e descriptografar uma
mensagem.
Uma chave secreta, que pode ser um número, uma palavra ou apenas uma seqüência de letras
aleatórias, é aplicada ao texto de uma mensagem para alterar o conteúdo de uma maneira
específica. Isso pode ser tão simples quanto Deslocar cada letra por um número de locais no
alfabeto. Desde que tanto o remetente quanto destinatário saibam a chave secreta, eles podem
criptografar e descriptografar todas as mensagens que usam essa chave.
As chaves usadas são relativamente pequenas, geralmente chaves menores que 256 bits de
forma a não onerar o processo.
E o que isso significa ?
Significa que quanto maior a chave mais forte é criptografia e mais recursos você terá que
usar tanto para criptografar como para decriptografar.
Abaixo temos uma tabela que mostra o tempo médio de busca, através do método de força
bruta para a quebra da chave de criptografia simétrica.
20
Se você resolver usar uma chave com 1 megabyte de tamanho você vai criar uma criptografia
muito forte mas vai gastar muito mais recursos (em tempo de processamento) para
criptografar e descriptografar uma mensagem.
Os algoritmos de chave simétrica ou criptografia de chave única são uma classe de algoritmos
para a criptografia que usam chaves criptográficas relacionadas para as operações de cifragem
ou decifragem.
A chave de cifragem pode ser idêntica à de decifragem ou poderá existir uma transformação
simples entre as duas chaves. As chaves representam um segredo, partilhado entre duas ou
mais partes, que podem ser usadas para manter um canal confidencial de informação. Usa-se
uma única chave, partilhada por ambos os interlocutores, na premissa de que esta é conhecida
apenas por eles.
A grande desvantagem da criptografia simétrica é o gerenciamento de chaves e a sua
distribuição.
Se você usa a mesma chave para criptografar e descriptografar os dados, como você vai
transferir com segurança a chave para alguém descriptografar os dados da mensagem ?
21
Se um usuário malicioso intercepta a chave, ele pode decodificar suas mensagens. (Uma
forma de atenuar este problema é adicionar camadas de segurança na criptografia simétrica.)
Abaixo apresentamos alguns exemplos de algoritmos de criptografia simétrica:
1- O DES ("Data Encryption Standard") - O DES é um mecanismo de cifragem tradicional
(simétrico) desenvolvido nos anos setenta, utiliza uma chave de 56 bits que é aplicada a
blocos de dados com 64 bits, o objetivo destes algoritmos é que seja muito difícil calcular a
chave K, mesmo conhecendo o algoritmo DES, uma mensagem cifrada C e uma mensagem
original M: C = DES(K,M);
2- Rivest Ciphers (RC2; RC4 e RC5) - Esta é uma sucessão de algoritmos bastante usados que
possuem maior flexibilidade e possibilitam maior segurança do que o DES simples. Todos são
algoritmos simétricos (a mesma chave é usada para cifrar e para decifrar).;
3- International Data Encryption Algorithm (IDEA) - É mais uma técnica de cifragem
simétrica em bloco que é usada nos dias atuais. Usa blocos fixos com 64 bits (8 bytes) e usa
chaves com 128 bits (16 bytes). É considerada segura, mas ao contrário dos algoritmos RC
usa chaves de comprimento fixo que podem comprometer segurança;
4-BLOWFISH - É um algoritmo simétrico conhecido pela sua velocidade, sendo mais rápido
do que o RC2 e o IDEA. Usa blocos fixos com 64 bits (8 bytes), mas as chaves podem ter
qualquer comprimento (128 bits é o mais usado);
5- ARCFOUR - É um algoritmo considerado equivalente ao RC4, tal como o RC4 não usa
blocos de entrada mas sim um fluxo contínuo de bytes e as chaves podem ter comprimento
variável (128 bits é o mais usado);
O algoritmo simétrico mais usado atualmente é o AES (Advanced Encryption Standard). O
Advanced Encryption Standard (AES, ou Padrão de Criptografia Avançada), também
conhecido por Rijndael, é uma cifra de bloco adotada como padrão de criptografia pelo
governo dos Estados Unidos.
22
O AES foi anunciado pelo NIST (Instituto Nacional de Padrões e Tecnologia dos EUA) como
U.S. FIPS PUB (FIPS 197) em 26 de Novembro de 2001, depois de 5 anos de um processo de
padronização. Tornou-se um padrão efetivo em 26 de Maio de 2002.
A plataforma .NET contém o algoritmo Rijndael, que você deve usar para todos os novos
projetos que exigem a criptografia simétrica. Assim você pode usar a classe RijndaelManaged
para realizar a criptografia e descriptografia simétrica.
Esta classe exige que você forneça uma chave e um vetor de inicialização (VI). O VI ajuda a
garantir que ao criptografar a mesma mensagem várias vezes produz-se textos cifrados
diferentes. O mensagem criptografada é comumente conhecido como texto cifrado.
Para descriptografar os dados, você deve usar a mesma chave e o VI usado para criptografar
os dados. A chave deve ser composta de bytes de dados que compõem o comprimento total da
chave.
Por exemplo, uma chave de 128 bit é composta por 16 bytes. Se a chave é gerada pela
aplicação, utilizar bytes para a chave não é um problema, no entanto, se um ser humano está
gerando a chave, é comum querer que a chave seja uma palavra ou frase. Isso pode ser feito
usando a classe Rfc2898DeriveBytes, que pode derivar uma chave de uma senha fornecida.
- Criando um Sistema de Criptografia 
A seguir estaremos exemplificando alguns passos para criar um Sistema de Criptografia:
Passo 1: 
Crie um novo projeto do tipo Windows Forms Application com o nome
CriptografiaSimetrica;
A seguir no formulário form1.vb inclua os controles Label, TextBox e Button
disponibilizando: Mensagem a ser criptografada, Senha, Botão para disponibilizar a
criptografia e o espaço para apresentação da mensagem criptografada.
Passo 2:
23
Declare os namespaces usados no projeto:
Imports System.Security.Cryptography
Imports System.IO
Imports System.TextA seguir declare as seguintes variáveis no início do formulário (conforme exemplo a seguir):
Dim textoCifrado As Byte()
Dim sal() As Byte = {&H0, &H1, &H2, &H3, &H4, &H5, &H6, &H5, &H4, &H3, &H2, &H1, &H0}
Dim senha As String = ""
Dim mensagem As String = ""
Agora no evento Click do botão de comando inclua o código abaixo:
Private Sub btnCriptografar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles btnCriptografar.Click
 senha = txtSenha.Text
 mensagem = txtMensagem.Text
 Dim chave As New Rfc2898DeriveBytes(senha, sal)
 ' criptografa os dados
 Dim algoritmo = New RijndaelManaged()
 algoritmo.Key = chave.GetBytes(16)
 algoritmo.IV = chave.GetBytes(16)
 Dim fonteBytes() As Byte = New System.Text.UnicodeEncoding().GetBytes(mensagem)
 Using StreamFonte = New MemoryStream(fonteBytes)
 Using StreamDestino As New MemoryStream()
 Using crypto As New CryptoStream(StreamFonte, algoritmo.CreateEncryptor(),
CryptoStreamMode.Read)
 moveBytes(crypto, StreamDestino)
 textoCifrado = StreamDestino.ToArray()
 End Using
24
 End Using
 End Using
 'MessageBox.Show(String.Format("Mensagem : {0}{1} Criptografada : {2}", mensagem,
Environment.NewLine, Convert.ToBase64String(textoCifrado)))
 txtResultado.Text = txtResultado.Text + Convert.ToBase64String(textoCifrado) + " :: senha
=> " + txtSenha.Text + Environment.NewLine
 End Sub
Digite a seguir o código da rotina moveBytes() abaixo:
Private Sub moveBytes(ByVal fonte As Stream, ByVal destino As Stream)
 Dim bytes(2048) As Byte
 Dim contador = fonte.Read(bytes, 0, bytes.Length - 1)
 While (0 <> contador)
 destino.Write(bytes, 0, contador)
 contador = fonte.Read(bytes, 0, bytes.Length - 1)
 End While
 End Sub
Entendendo o código:
A mensagem a ser criptografada é informada na caixa de texto txtMensagem.Text e atribuída
a variável mensagem e o resultado da criptografia é armazenado na variável textoCifrado;
- Estamos usando uma senha e um variável (sal) para criar uma chave usando a classe
Rfc2898DeriveBytes : Dim chave As New Rfc2898DeriveBytes(senha, sal)
Para descriptografar a mensagem você deve usar o mesmo valor de sal.
- Em seguida criamos uma instância da classe RijndaelManaged : Dim algoritmo = New
RijndaelManaged()
25
- Preenchemos os valores para a chave e para o Vetor de Inicialização (VI):
algoritmo.Key = chave.GetBytes(16)
algoritmo.IV = chave.GetBytes(16)
- Cada vez que você chama chave.GetBytes(16) obtemos bytes diferentes , logo para
descriptografar temos que executar a mesma sequência;
 Primeiro chamar Key
 A seguir chamar GetBytes para preencher Key
 E finalmente, chamar key.GetBytes para preencher o VI
A seguir convertemos os dados binários para que ele possa ser criptografado usando a classe
UnicodeEncoding();
New System.Text.UnicodeEncoding().GetBytes(mensagem)
- Criamos 3 Streams para mover os dados:
O primeiro Stream é um objeto MemoryStream e representa a fonte que é preenchida com os
bytes dos dados;
O segundo Stream é um objeto MemoryStream e representa o destino;
O próximo Stream é um objeto CryptoStream e é aqui que o serviço é feito
Usamos a rotina auxiliar moveBytes que copia os bytes de um fluxo de origem para um fluxo
de destino.
O objeto de Stream de criptografia é passado para moveBytes como a fonte, e o objeto
StreamDestino objeto é passado para moveBytes como destino.
Depois da chamada a moveBytes o objeto StreamDestino contém os dados criptografados.
Finalmente, a mensagem criptografada é exibida na caixa de texto :
26
txtResultado.Text = txtResultado.Text + Convert.ToBase64String(textoCifrado) + " :: senha
=> " + txtSenha.Text + Environment.NewLine
Para descriptografar a mensagem, precisamos apenas realizar o processo inverso (conforme
código a seguir):
Private Sub btnDescriptografar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
Handles btnDescriptografar.Click
 If (textoCifrado Is Nothing) Then
 MessageBox.Show("Os dados não estão criptografados!")
 Return
 End If
 Dim chave As New Rfc2898DeriveBytes(senha, sal)
 Dim algoritmo = New RijndaelManaged()
 algoritmo.Key = chave.GetBytes(16)
 algoritmo.IV = chave.GetBytes(16)
 Using StreamFonte = New MemoryStream(textoCifrado)
 Using StreamDestino As New MemoryStream()
 Using crypto As New CryptoStream(StreamFonte, algoritmo.CreateDecryptor(),
CryptoStreamMode.Read)
 moveBytes(crypto, StreamDestino)
 Dim bytesDescriptografados() As Byte = StreamDestino.ToArray()
 Dim mensagemDescriptografada = New
UnicodeEncoding().GetString(bytesDescriptografados)
 txtMensagemDescriptografada.Text = mensagemDescriptografada
 End Using
 End Using
 End Using
 End Sub 
27
- Criptografia Base64 em Java
Muitas vezes o desenvolvedor Java precisa transformar uma String em um array de bytes
convertido em uma forma meta-criptografada. Para isso, você pode usar um algoritmo bem
simples chamado de Base64. Existem diversas implementações do Base64 para Java e
Android. O próprio Android já possui uma implementação bem interessante do Base64. Para
codificar uma String você utiliza um método encode e para decodificar a String você utiliza
um método decode.
Apesar de existirem diversas implementações do Base64 disponíveis, neste documento
estaremos sugerindo a implementação OstermillerUtils. Esta implementação é bem
interessante e fácil de usar. Depois de utilizar esta implementação em diversos aplicativos,
não encontrei problemas de incompatibilidade com outras linguagens nem problemas de
conflitos com outras implementações em diferentes jars.
Para utilizar o OstermillerUtils, basta fazer o download do jar e acrescentá-lo em seu
classpath.
28
Exemplo:
String string = Base64.decode(s);
String string = Base64.encode(s);
Lembre-se que Base64 não possui criptografia através de senha. Para isso você pode utilizar
diversas implementações existem do Cipher.
Abaixo damos um exemplo mais completo usando a criptografia Base64:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.IOException;
 
public class TesteBase64{
public static void main(String args[]){
BASE64Encoder encoder = new BASE64Encoder();
 
String code = encoder.encodeBuffer("Teste Base64".getBytes());
 
//Vai imprimir "Teste Base64 -(codificado)- VGVzdGUgQmFzZTY0"
System.out.println("Teste Base64 -(codificado)- " + code);
 
BASE64Decoder decoder = new BASE64Decoder();
 
try{
byte[] decoded = decoder.decodeBuffer("VGVzdGUgQmFzZTY0");
 
//Vai imprimir "VGVzdGUgQmFzZTY0 -(decodificado)- Teste Base64"
System.out.println("VGVzdGUgQmFzZTY0 -(decodificado)- " + new
String(decoded));
}catch(IOException ex){
}
}
}
29
RELATÓRIO 01 - UTILIZANDO CRIPTOGRAFIA
O tema abordado é muito amplo e o conteúdo apresentado é apenas uma introdução para,
quem sabe, ser continuado em trabalhos futuros. 
Muita coisa ficou de fora, como os tipos de cifras (de bloco, de fluxo), algoritmos de HASH e
até mesmo a descrição dos algoritmos. 
Entender o funcionamento de um RSA por exemplo é muito gratificante, embora não
interesse a maioria. Para nós o mais importante é saber como empregá-lose não os seus
princípios matemáticos. Se os matemáticos dizem que que é inviável, que seja. A gente
acredita, implementa e usa!
Deve-se dizer que os algoritmos assimétricos são extremamente onerosos, ou seja, consomem
muita CPU. Seu uso deve ser evitado ou, pelo menos, minimizado pois o processador
agradece. Como os simétricos são absolutamente seguros e rápidos, sendo seu único problema
a troca de chave, é comum usar um assimétricos apenas para trocar a chave convergindo após
para um simétrico. O protocolo SSL é exatamente assim (quem está por trás do SSH e o
HTTPS, dentre outros).
- Criando uma criptografia segura no PHP
Se você éum desenvolvedor web profissional, segurança é um aspecto importante do seu
trabalho. Se você estiver planejando armazenar dados críticos ou sensíveis em sua aplicação
web, como senhas, cartões de credito etc, você deveria usar uma criptografia segura (strong
cryptography) para proteger esses dados.
O que é criptografia segura?
Criptografia segura é o uso de sistemas ou componentes que são considerados altamente
resistentes à criptoanálise, o estudo de métodos para decifrar códigos.
Teoricamente falando, se criptografarmos e armazenarmos dados confidenciais em um banco
de dados, arquivo, ou qualquer outra coisa, um atacante malicioso não será capaz de
descriptografá-los sem saber a chave, uma sequência de números usada para criptografar ou
30
para descriptografar dados. Como podemos provar que o atacante não será capaz de
descriptografar os dados?
Infelizmente, a resposta correta é que não podemos ter certeza. Podemos apenas ter um bom
nível de segurança usando algoritmos bem testados de criptografia (criptografia segura).
Por exemplo, a cifra ENIGMA, usada na Segunda guerra Mundial como um sistema para
criptografar a comunicação entre soldados alemães, hoje não é considerada
criptograficamente forte. O DES, um algoritmo padrão do FIST em 1976, que infelizmente
ainda é utilizado em muitos sistemas, não é considerado criptograficamente forte mais. Em
1998, a Electronic Frontier Foundation (EFF) construiu uma máquina, o EFF DES cracker,
para executar uma pesquisa por força bruta pela key space (o conjunto das possíveis senhas)
da encriptação cipher – ou seja, para descriptografar uma mensagem criptografada tentando
cada chave possível. 
O objetivo é provar que a chave DES não é mais segura. Essa máquina era capaz de encontrar
a chave de uma mensagem criptografada em menos de um dia, e estamos falando de 1998!
Atualmente, alguns dos algoritmos que podem ser considerados criptograficamente fortes sao:
Blowfish, Twofish, Advanced Encryption Standard (AES, Rijndael), 3DES, Serpent, RSA
etc. É importante dizer que a segurança de qualquer algoritmo é relacionada à força e ao
tamanho da chave, e iremos falar desse ponto a seguir.
Por que devemos usar criptografia segura?
Muitos desenvolvedores, quando têm a necessidade de criptografar alguma informação,
tentam implementar suas cifras pessoas usando diferentes abordagens. Na internet, você
consegue encontrar várias implementações de cifras domésticas no PHP.
Eu desencorajo fortemente o uso dessas cifras, e vou dizer por quê. Criar uma cifra segura é
algo bastante complicado e, se você não for um expert, eu diria que é uma tarefa praticamente
impossível. Além disso, estamos falando de criptografia segura, o que significa que mesmo se
você for capaz de criar uma nova cifra, você precisa de tempo para provar que ela é segura,
compartilhando a implementação do algoritmo na comunidade open source.
31
O PHP oferece diferentes implementações dos algoritmos de criptografia mais importantes.
Em particular, o PHP tem as extensões de criptografia a seguir: Hash, mcrypt e OpenSSL
A extensão Hash não precisa de bibliotecas externas e é acionada por padrão no PHP 5.1.2.
Essa extensão substitui a antiga extensão mhash. Com essa
extensão, você pode gerar hashes ou HMAC (Hash-based Message Authentication Code). Ela
suporta os algoritmos de geração hash mais comuns usados na
criptografia segura. Se você quiser saber quais algoritmos são suportados pelo seu ambiente
PHP, pode utilizar a função hash_algos(), que te dá uma lista
de todos os algoritmos suportados. 
A extensão mcrypt é uma interface para a biblioteca mcrypt, que suporta uma grande
variedade de blocos de algoritmos, como DES, 3DES, Blowfish (default), 3-WAY, SAFER-
SK64, SAFER-SK128, TWOFISH, TEA, RC2 e GOST nos métodos de encriptação CBC,
OFB, CFB e ECB. Essa extensão é a mais utilizada para criptografar
dados usando cifras simétricas. 
A extensão OpenSSL usa as funções do OpenSSL project para geração e verificação de
assinaturas e para lacrar (criptografar) e abrir (descriptografar) dados. Você pode utilizar o
OpenSSL para proteger dados usando a criptografia pública de chaves com o algoritmo RSA. 
- Melhores práticas no PHP
Até agora,discutimos os aspectos gerais da criptografia segura. Agora eu gostaria de te falar
quais são as melhores práticas em como usar a criptografia segura no PHP. Aqui você pode
encontrar algumas das minhas recomendações: Use algoritmos padrão.
Sempre use um algoritmo padrão para criptografar seus dados. Não tente implementar sua
cifra doméstica, você irá gastar muito tempo e energia sem conseguir obter uma segurança
real. Minha sugestão é usar os melhores algoritmos disponíveis. Exemplos de algoritmos
criptograficamente sólidos são:
- algoritmos de chave simétrica: AES, que é um padrão FIST 197 desde 2001;
32
– algoritmos de chaves públicas: RSA, um algoritmo padrão da industria utilizado em muitos
produtos;
– Funções Hash: SHA-x, em que x pode ser 1,256,384, e 512. SHA é um padrão NIST.
- Espaço da chave
Quando falamos em segurança de uma cifra, o espaço chave é um dos parâmetros mais
importantes. Se nenhum design sólido explícito é dado pela cifra, a força do design é igual ao
tamanho da chave. Por exemplo, a cifra DES usa uma chave de 56 bits, o que significa que o
espaço da chave é de 2^56. Estes números parecem imensos, mas computadores modernos
podem suportá-los, e o EFF prova isso com o cracker DES. Então o espaço da chave é muito
importante. Para cifras simétricas, eu diria que 128 bits é o tamanho mínimo para uma chave
de criptografia forte. Com relação à criptografia de chaves públicas, os experts recomendam
um tamanho mínimo de 2048 bits se quisermos proteger nossos dados por 20 anos.
- Princípio de Kerchoof
Auguste Kerckhoffs foi um linguista alemão e criptógrafo do Higher Commercial Studies em
Paris no final do século 19. Ele escreveu, em um artigo famoso do “le Journal des Sciences
Militaires”, a seguinte frase, que é considerada o máximo na criptografia moderna:
Um sistema de criptografia deve ser seguro mesmo se tudo que disser respeito ao sistema,
exceto a chave, seja de conhecimento público. 
Usando a interpretação da Shannon, “O inimigo conhece o sistema”. Na minha opinião, no
mundo do software, você somente tem segurança com o uso de algoritmos open source. Se o
código-fonte foi testado por milhares de pessoas ao redor do mundo, a probabilidade de
encontrar um bug de segurança e consequentemente de consertá-lo é mais alta usando um
software open source do que comparada à chance do uso de um software proprietário.
Não use rand() ou mt_rand()
Até para este ponto, não tente implementar seu gerador aleatório. Você não pode implementar
um gerador de números aleatório seguro usando a função rand() no PHP. Essa função usa a
biblioteca libc para gerar números pesudoaleatórios que não são seguros para aplicativos de
33
criptografia. Ela gera números aleatórios usando um métodoaditivo linear de feedback, com
um período curto, que é previsível. Até a função mt_rand() não é segura do ponto de vista da
criptografia. Ela uso o algoritmo Mersenne Twister para gerar números pseudoaleatórios. Essa
função é melhor que a rand(), porque é mais rápida e produz números pseudoaleatórios com
um período maior, mas ainda é um algoritmo determinista, então também é previsível. Para
gerar um número aleatório criptograficamente forte no PHP, você tem que usar a função
openssl_random_pseudo_bytes() da biblioteca OpenSSL. Essa função está disponível a partir
do PHP 5.3. Se você estiver usando uma versão antiga do PHP, pode usar esta
implementação: 
function secure_rand($length) {
 if(function_exists('openssl_random_pseudo_bytes')) {
 $rnd = openssl_random_pseudo_bytes($length, $strong);
 if($strong === TRUE)
 return $rnd;
 }
 for ($i=0;$i<$length;$i++) {
 $sha= sha1(mt_rand());
 $char= mt_rand(0,30);
 $rnd.= chr(hexdec($sha[$char].$sha[$char+1]));
 }
 return $rnd;
}
Nessa implementação, eu encriptei os outputs da mt_rand() para hash. Esse método aumenta a
segurança do Mersenne Twister, mas não tem o mesmo nível de segurança da implementação
OpenSSL.
Use um valor salt em funções hash
Se você estiver usando uma função hash para proteger os dados, como por exemplo uma
senha, concatene os dados com um valor randômico (salt) antes de gerar o
hash. Um salt randômico irá proteger seus dados de Dictionary attacks. 
34
- Tamanho e força de senhas
Não dê a possibilidade para os usuários da sua aplicação web de escolherem senhas bobas e
pequenas. Você deve sempre usar uma senha com no mínimo 8 caracteres misturados com
números e letras. Você pode usar a biblioteca CrackLib para testar a “força” de uma senha.
Não use senhas com texto puro como chave para encriptar
Uma boa prática na criptografia, usando cifras simétricas, é usar um valor hashed como chave
de uma cifra. Esse método aumenta a segurança dos dados criptografados ao adicionar maior
aleatoriedade. Isso significa que se você quiser gerar uma boa chave para a cifra, você deve
usar o hash da senha e usá-lo como a chave do seu algoritmo de criptografia.
Use Base64 para codificar dados criptografados
Se você precisar migrar dados criptografados entre sistemas diferentes, como transmitir dados
pela internet, é recomendado codificar os dados em Base64.
No PHP, você pode usar as funções base64_encode() e base64_decode(). Essa codificação irá
garantir que seus dados sejam armazenados corretamente, independentemente do sistema de
codificação utilizado no seu ambiente.
É claro que essas são apenas sugestões gerais, e os pontos relacionados ao uso correto da
criptografia são mais do isso.
- Partindo para a prática
Apesar de haver muita coisa escrita sobre a conversão de arquivos para Base64, resolvi
engrossar o caldo com a minha visão. Além de falar um pouco sobre a história do Base64, o
foco aqui é mostrar dois códigos que podem ser úteis aos que desejam compreender como
funciona a codificação.
Inicialmente, o Base64 foi desenvolvido para permitir o envio de dados binários através de
protocolos que suportavam apenas conteúdo escrito em ASCII. Tudo isso porque, como os
35
meios de transmissão eram extremamente falhos, convencionou-se por usar um dos oito bits
de um bytes como "bit de paridade" a fim de facilitar a detecção de erros em cada byte
transmitido. A ideia é transformar todo o conteúdo original numa sequência de caracteres
ASCII. Para isso foram usadas as 26 letras do alfabeto em suas formas minúscula e maiúscula,
os 10 números, o '+' e a '\', totalizando 64 caracteres.
Como toda grande mudança é onerosa e pode gerar um monte de problemas imprevistos,
todas as novidades que foram chegando precisavam tiveram que se adaptar ao que já existia.
Assim, mesmo não sendo mais tão necessária essa correção byte a byte, o Base64 continua
sendo usado da mesma forma que antes e em outro monte de a?licações. 
Por exemplo, tudo o que anexamos a um e-mail é codificado pelo Base64 antes de ser
enviado. Outro bom exemplo são os arquivos que precisam guardar dados binários e cuja
visualização seja interessante, como é o caso dos arquivos de chaves públicas e privadas que
são às vezes guardados em Base64.
Falando mais especificamente da codificação, a RFC 4648 diz que a conversão deve ser feita
com base nas seguintes regras:
- cria-se um vetor com os caracteres válidos, na seguinte ordem:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 
os bytes a serem convertidos são agrupados em blocos de até 3 bytes que são divididos em 4
conjuntos de 6 bits;
- Cada um desses conjuntos representa um número entre 0 e 63 e, assim, um elemento do
vetor anterior;
- Se o bloco de entrada não tiver 3 bytes, o bloco de saída é completado com o caracter '=';
por fim, cada linha do arquivo codificado deve ter 64 bytes, seguidos dos caracteres que
representam uma nova linha '\r\n'. Vejam um exemplo de conversor que fiz:
#include <stdio.h>
#include <stdlib.h>
#define safe_free(x) if (x) { free(x); x = NULL; }
36
/*
 * A codificacao consiste em pegar um bloco com ate 3 bytes e transformar
 * em quatro bytes nos padroes do Base64.
 *
 * +-----------------------------+
 * |123456 78|1234 5678|12 345678|
 * |123456|12 3456|1234 56|123456|
 * +-----------------------------+
 *
 *
 * cbEntrada: vetor com ate 3 posicoes
 * cbSaida : vetor sempre com 4 posicoes
 * cbTamanho: quantidade de bytes em cbEntrada
 */
void CodificaBloco(unsigned char *cbEntrada, 
unsigned char *cbSaida, unsigned char cbTamanho)
{
 char
cbBase[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 unsigned char cbConta = 0;
 unsigned char cbX;
 
 for(cbX = 0; cbX < cbTamanho; cbX++)
 {
 if (cbConta == 0)
 {
 cbSaida[cbConta++] = cbBase[(unsigned char)(cbEntrada[0] >> 2)];
 }
 else if (cbConta == 1)
 {
 cbSaida[cbConta++] = cbBase[(unsigned char)(((cbEntrada[0] & 3) << 4) | (cbEntrada[1]
>> 4))];
 }
 else if (cbConta == 2)
 {
37
 cbSaida[cbConta++] = cbBase[(unsigned char)(((cbEntrada[1] & 15) << 2) | ((cbEntrada[2]
& 192) >> 6))];
 }
 }
 
 // acerta "sobra" de bits
 cbX--;
 if (cbX == 0)
 {
 cbSaida[cbConta++] = cbBase[(unsigned char)((cbEntrada[0] & 3) << 4)];
 }
 else if (cbX == 1)
 {
 cbSaida[cbConta++] = cbBase[(unsigned char)((cbEntrada[1] & 15) << 2)];
 }
 else if (cbX == 2)
 {
 cbSaida[cbConta++] = cbBase[(unsigned char)(cbEntrada[2] & 63)];
 }
 
 // completa saida com '='
 while (cbConta < 4) cbSaida[cbConta++] = (unsigned char) '=';
}
/*
 * Codifica entradas de 48 bytes ou menos e retorna linhas
 * de ate 64 caracteres base64, finalizadas com '\r\n'
 *
 * clEntrada: vetor com ate 48 bytes
 * clSaida : vetor com ate 67 bytes
 * clTamanho: quantidade de bytes em clEntrada
 *
 * Retorno: quantidade de bytes em clSaida
 */
int CodificaLinha(unsigned char *clEntrada, 
38
unsigned char *clSaida, unsigned char clTamanho)
{
 unsigned char *clBloco64, *clBloco256, clX, clY, clZ, clTamanhoSaida;
 
 clBloco256 = (unsigned char*) malloc(sizeof(char) * 3);
 clBloco64 = (unsigned char*) malloc(sizeof(char) * 4);
 
 // limpa as variaveis
 for (clZ = 0; clZ < 67; clZ++)
 {
 if (clZ < 4)
 {
 clBloco64[clZ] = '\0';
 if (clZ < 3) clBloco256[clZ] = '\0';
 }
 clSaida[clZ] = '\0';
 }
 clY = 0;
 clTamanhoSaida= 0;
 
 // codifica a linha
 for (clX = 0; clX <= clTamanho; clX++)
 {
 // codifica o bloco
 if ((clY == 3) || ((clX == clTamanho) && (clY)))
 {
 clX--;
 CodificaBloco(clBloco256, clBloco64, clY);
 
 // copia bloco para a saida e limpa variaveis
 for (clZ = 0; clZ < 4; clZ++)
 {
 clSaida[clTamanhoSaida++] = clBloco64[clZ];
 clBloco64[clZ] = '\0';
 if (clZ < 3) clBloco256[clZ] = '\0';
 }
39
 clY = 0;
 }
 else
 {
 clBloco256[clY++] = clEntrada[clX];
 }
 }
 // finaliza a linha com CR + LF
 if (clTamanhoSaida)
 {
 clSaida[clTamanhoSaida++] = '\r';
 clSaida[clTamanhoSaida++] = '\n';
 clSaida[clTamanhoSaida] = '\0';
 }
 safe_free(clBloco256);
 safe_free(clBloco64);
 return clTamanhoSaida;
}
/*
 * Codifica um arquivo binario para base64
 *
 * caEntrada: arquivo binario a ser codificado
 * caSaida : arquivo base64 a ser gravado
 */
void CodificaArquivo(FILE *caEntrada, FILE *caSaida)
{
 unsigned char *caLinha256, *caLinha64, caX;
 
 caLinha256 = (unsigned char*) malloc(sizeof(unsigned char) * 48);
 caLinha64 = (unsigned char*) malloc(sizeof(unsigned char) * 67);
 caX = 0;
 while(!feof(caEntrada))
 {
 caLinha256[caX++] = (unsigned char)getc(caEntrada);
 if (feof(caEntrada) || (caX == 48))
40
 {
 // para desconsiderar o ultimo caracter
 if (feof(caEntrada)) caX--;
 if (!caX) break;
 caX = CodificaLinha(caLinha256, caLinha64, caX);
 fprintf(caSaida, "%s", caLinha64);
 caX = 0;
 }
 }
 safe_free(caLinha256);
 safe_free(caLinha64);
}
void ImprimeUso()
{
 printf("+----------------------------------------------+\n");
 printf("| Para usar o encode digite: |\n");
 printf("| encode ArquivoEntrada ArquivoSaida |\n");
 printf("| O ArquivoEntrada sera convertido para Base64 |\n");
 printf("| e salvo no ArquivoSaida |\n");
 printf("+----------------------------------------------+\n");
}
int main(int argc, char **argv)
{
 unsigned char *Linha256, *Linha64, *Bloco256, *Bloco64;
 unsigned char X = 0;
 FILE *ArquivoEntrada, *ArquivoSaida;
 if (argc != 3)
 {
 ImprimeUso();
 return 0;
 }
 else
41
 {
 ArquivoEntrada = fopen(argv[1], "rb");
 ArquivoSaida = fopen(argv[2], "wb");
 }
 CodificaArquivo(ArquivoEntrada, ArquivoSaida);
 fclose(ArquivoEntrada);
 fclose(ArquivoSaida);
 return 0;
}
Já a decodificação, seguindo a lógica inversa, seria:
#include <stdio.h>
#include <stdlib.h>
#define safe_free(x) if (x) { free(x); } x = NULL;
/*
 * A decodificacao consiste em separar o texto codificado
 * em blocos de 4 caracteres, identificar o numero
 * correspondente a sua pposicao no vetor de codificacao
 * e reagrupar os bits de forma a remontar os bytes originais
 * +-----------------------------+
 * |123456|12 3456|1234 56|123456|
 * |123456 78|1234 5678|12 345678|
 * +-----------------------------+
 *
 *
 * Decodifica um bloco em base64 para binario.
 * dcEntrada: vetor com 4 bytes
 * dcSaida : vetor com 3 bytes
 *
 * Retorno: tamanho do bloco decodificado
42
 */
int DecodificaBloco(unsigned char *dcEntrada, unsigned char *dcSaida)
{
 unsigned char dcTamanho, dcX, dcConta, dcPosicao;
 
 // limpa saida
 for (dcX = 0; dcX < 3; dcX++) dcSaida[dcX] = '\0';
 
 // acha o tamanho original do bloco
 dcTamanho = 4;
 while((dcEntrada[--dcTamanho] == '=') && (dcTamanho));
 
 // inicia decodificacao
 dcConta = 0;
 for (dcX = 0; dcX < (dcTamanho + 1); dcX++)
 {
 // define posicao do caracter na tabela
 if (DEBUGANDO) printf("dcX = %d\n", dcX);
 if ((dcEntrada[dcX] >= 'A') && dcEntrada[dcX] <= 'Z')
 dcPosicao = (dcEntrada[dcX] - 65);
 if ((dcEntrada[dcX] >= 'a') && dcEntrada[dcX] <= 'z')
 dcPosicao = (dcEntrada[dcX] - 71);
 if ((dcEntrada[dcX] >= '0') && dcEntrada[dcX] <= '9')
 dcPosicao = (dcEntrada[dcX] + 4);
 if (dcEntrada[dcX] == '+') dcPosicao = 62;
 if (dcEntrada[dcX] == '/') dcPosicao = 63;
 
 // recompoe os bits
 if (dcX == 0)
 {
 dcSaida[dcConta] = (unsigned char)(dcPosicao << 2);
 }
 else if (dcX == 1)
 {
 dcSaida[dcConta++] =
 (unsigned char) (dcSaida[0] | ((dcPosicao & 48) >> 4));
43
 if ((dcPosicao & 15) << 4)
 dcSaida[dcConta] = (unsigned char)((dcPosicao & 15) << 4);
 }
 else if (dcX == 2)
 {
 dcSaida[dcConta++] =
 (unsigned char) (dcSaida[1] | ((dcPosicao & 60) >> 2));
 if ((dcPosicao & 3) << 6)
 dcSaida[dcConta] = (unsigned char)((dcPosicao & 3) << 6);
 }
 else if (dcX == 3)
 {
 dcSaida[dcConta] = (unsigned char)(dcSaida[2] | dcPosicao);
 }
 }
 return --dcTamanho;
}
/*
 * Decodifica uma linha em base64 para binario, retornando o tamanho
 * da linha decodificada.
 *
 * dlEntrada: vetor com ate 64 bytes
 * dlSaida : vetor com ate 48 bytes
 *
 * Retorno: tamanho da linha decodificada
 */
int DecodificaLinha(unsigned char *dlEntrada, unsigned char *dlSaida)
{
 unsigned char *dlBloco256, *dlBloco64, dlConta, dlX,
 dlTamanhoBloco, dlTamanhoLinha;
 
 dlBloco256 = (unsigned char*) malloc(sizeof(unsigned char) * 3);
 dlBloco64 = (unsigned char*) malloc(sizeof(unsigned char) * 4);
 
 dlConta = 0;
44
 dlTamanhoLinha = 0;
 
 // limpa a variavel de saida
 for (dlX = 0; dlX < 49; dlSaida[dlX++] = '\0');
 
 // decodifica linha
 for (dlX = 0; (dlEntrada[dlX] != '\0'); dlX++)
 {
 dlBloco64[dlConta++] = dlEntrada[dlX];
 // decodifica bloco
 if (dlConta == 4)
 {
 dlTamanhoBloco = DecodificaBloco(dlBloco64, dlBloco256);
 // copia bloco decodificado para a saida
 for (dlConta = 0; dlConta <= dlTamanhoBloco;
 dlSaida[dlTamanhoLinha++] = dlBloco256[dlConta++]);
 dlConta = 0;
 }
 }
 safe_free(dlBloco256);
 safe_free(dlBloco64);
 return dlTamanhoLinha;
}
/*
 * Decodifica um arquivo em base64 para binario.
 *
 * daEntrada: arquivo base64 a ser decodificado
 * daSaida : arquivo binario a ser gerado.
 */
void DecodificaArquivo(FILE *daEntrada, FILE *daSaida)
{
 unsigned char *daLinha256, *daLinha64, daX;
 // porque o tamanho pode ser negativo
 char daTamanho;
 
45
 daLinha256 = (unsigned char*) malloc(sizeof(unsigned char) * 49);
 daLinha64 = (unsigned char*) malloc(sizeof(unsigned char) * 67);
 daX = 0;
 while(!feof(daEntrada))
 {
 daLinha64[daX++] = (unsigned char)getc(daEntrada);
 // ignora /r e /n
 if ((daLinha64[daX - 1] == '\n') || (daLinha64[daX - 1] == '\r'))
 {
 daLinha64[--daX] = '\0';
 continue;
 }
 if (feof(daEntrada) || (daX == 64) || (daLinha64[daX - 1] == '\0'))
 {
 if (daLinha64[daX - 1] != '\0') daLinha64[daX++] = '\0';
 daTamanho = DecodificaLinha(daLinha64, daLinha256);
 for (daX = 0; (daX < daTamanho); daX++)
 {
 putc((char)daLinha256[daX], daSaida);
 }
 daX = 0;
 }
 }
 safe_free(daLinha256);
 safe_free(daLinha64);return;
}
void ImprimeUso()
{
 printf("+-----------------------------------------------+\n");
 printf("| Para usar o decode digite: |\n");
 printf("| decode ArquivoEntrada ArquivoSaida |\n");
 printf("| O ArquivoEntrada sera convertido para Base256 |\n");
 printf("| e salvo no ArquivoSaida |\n");
 printf("+-----------------------------------------------+\n");
}
46
int main(int argc, char **argv)
{
 FILE *ArquivoEntrada, *ArquivoSaida;
 
 if (argc != 3)
 {
 ImprimeUso();
 return 0;
 }
 else
 {
 ArquivoEntrada = fopen(argv[1], "rb");
 ArquivoSaida = fopen(argv[2], "wb");
 }
 DecodificaArquivo(ArquivoEntrada, ArquivoSaida);
 fclose(ArquivoEntrada);
 fclose(ArquivoSaida);
 return 0;
}
REFERÊNCIAS BIBLIOGRÁFICAS
[1] BEAL, Adriana. Segurança da Informação: princípios e melhores práticas para a proteção
dos ativos de informação nas organizações – São Paulo: Atlas, 
2005.
[2] BSI BRASIL. ISO/IEC 27001 Segurança da Informação. Disponível em:
<http://www.bsibrasil.com.br/certificacao/sistemas_gestao/normas/iso_iec27001/>. Acesso 
em: 12 fev. 2013.
[3] HOLANDA, Roosevelt de. O estado da arte em sistemas de gestão da segurança da
Informação: Norma ISO/IEC 27001:2005 – São Paulo: Módulo Security Magazine, 
19 jan 2006. Disponível em <http://www.modulo.com.br>. Acesso em: 12 fev. 2013.
[4] ISO 17799. ABNT NBR ISO/IEC 17799:2005 – Tecnologia da Informação – Técnicas de
segurança. Código de prática para a gestão da segurança da informação. 
Associação Brasileira de Normas Técnicas – Rio de Janeiro: ABNT, 2005.
47
[5] LYRA, Maurício Rocha. Segurança e auditoria em sistemas de informação. Rio de
Janeiro: Ciência Moderna, 2008. 253 p.
[6] OLIVA, Rodrigo Polydoro; OLIVEIRA, Mírian. Elaboração, Implantação e Manutenção
de Política de Segurança por Empresas no Rio Grande do Sul em relação às 
recomendações da NBR/ISO17799 – ENANPAD. 2003.
[7] PARRA, Gislaine. Metodologia para análise de segurança aplicada em uma infraestrutura
de chave pública. 2002. 99f. Dissertação (Mestrado em Ciência da 
Computação) – Programa de Pós-Graduação em Ciência da Computação da Universidade
Federal de Santa Catarina, Florianópolis.
[8] SÊMOLA, Marcos. Gestão da Segurança da Informação: uma visão executiva – Rio de
Janeiro: Campus, 2003.
[9] THE ISO 27000 DIRECTORY. An Introduction to ISO 27001, ISO 27002….ISO 27008.
Disponível em <http://www.27000.org/index.htm>. Acesso em: 12 fev. 2013.
48
	SUMÁRIO
	INTRODUÇÃO À CRIPTOGRAFIA

Outros materiais

Materiais relacionados

Perguntas relacionadas

Perguntas Recentes