import java.util.*;
import java.text.*;
class ExemploCollator {
public static void main(String[] args) {
// Vamos comparar duas strings ignorando os acentos.
String st1 = "Gisele da Conceição Zózima Bündchen";
String st2 = "Gisele da Conceicao Zozima Bundchen";
String st3 = "GISELE DA CONCEICAO ZÓZIMA BUNDCHEN";
String st4 = "Gisele da Conceicao Zozimo Bundchen"; // note que esta string é diferente
Collator collator = Collator.getInstance (new Locale ("pt", "BR"));
collator.setStrength(Collator.PRIMARY); // importante!
if (collator.compare (st1, st2) == 0) {
System.out.println ("As duas Giseles são a mesma pessoa, só diferem pelos acentos");
}
if (collator.compare (st1, st3) == 0) {
System.out.println ("As duas Giseles são a mesma pessoa, só diferem pelos acentos e pela diferença de
caixa");
}
if (collator.compare (st1, st4) != 0) {
System.out.println ("As duas Giseles não são a mesma pessoa");
}
// Agora vamos mostrar um exemplo de ordenação ignorando os acentos.
String[] dados = {
"José Aparecido",
"João Simões",
"Jó Abedenego"
};
// Não usando "collator"
SortedSet<String> s1 = new TreeSet<String>();
for (String d : dados) {
s1.add (d);
}
// Deve imprimir [José Aparecido, João Simões, Jó Abedenego], que é
// ao contrário das regras da língua portuguesa
System.out.println (s1);
// Usando "collator", segue as regras:
// imprime "[Jó Abedenego, João Simões, José Aparecido]"
final Collator coll = Collator.getInstance (new Locale ("pt", "BR"));
SortedSet<String> s2 = new TreeSet<String>(new Comparator<String>() {
public int compare (String o1, String o2) {
return coll.compare (o1, o2);
}
});
for (String d : dados) {
s2.add (d);
}
System.out.println (s2);
// O código abaixo é equivalente e mais simples,
// porque Collator implementa Comparator.
s2 = new TreeSet<String>(coll);
System.out.println (s2);
}
}
Friday, June 06, 2008
Ordenar palavras ignorando acentos
Volta e meia você quer ordenar palavras ignorando a caixa (minúscula/maiúscula) e os acentos e cedilhas; a reação normal é criar um método que passe tudo para minúsculas e remova acentos e cedilhas. Embora isso seja necessário às vezes, é melhor usar um recurso do Java chamado "Collators". Exemplo:
Friday, May 30, 2008
Criptografia RSA
Posto abaixo um programa que escrevi faz alguns anos para servir de exemplo de uso das APIs de criptografia do Java. Dei uma reformatada (com o bom e velho Eclipse) e incluí alguns javadocs. Não é o jeito "correto" de usar as APIs, mas muita gente precisa de algo parecido com isso para trabalhos de escola, portanto divirtam-se!
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.spec.RSAKeyGenParameterSpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
// Exemplo de criptografia RSA.
//
// Para criptografar um bloco de dados com RSA, gera-se uma chave aleatória para
// usar com um algoritmo simétrico (como o AES), e então criptografa-se essa chave
// com o algoritmo RSA usando-se a chave pública do destinatário.
//
// Para o destinatário decifrar os dados, ele deve decifrar a chave recebida com sua
// chave privada, e então usar a chave decifrada para decifrar os dados.
//
// Note que o modo correto de fazer isso é usar um formato padronizado, como o PKCS#7
// (ou CMS), e usar o BouncyCastle. Só faço isto para exemplificar as APIs do Java.
//
/**
* Efetua a parte da criptografia
*/
class Cifrador {
/**
* Cifra um bloco de dados com a chave pública.
*
* @param pub
* A chave pública.
* @param textoClaro
* O bloco de dados a ser cifrado.
* @return Dois arrays de bytes, sendo que o primeiro é o bloco cifrado, e o
* segundo é a chave gerada e cifrada. Não jogue fora nenhum deles.
* @throws NoSuchAlgorithmException
* Algoritmo (AES) não disponível na sua versão do JDK.
* @throws NoSuchPaddingException
* Padding (PKCS5Padding) não disponível na sua versão do JDK.
* @throws InvalidKeyException
* Se a chave pública for inválida.
* @throws IllegalBlockSizeException
* Não deve ocorrer.
* @throws BadPaddingException
* Não deve ocorrer.
* @throws InvalidAlgorithmParameterException
* Não deve ocorrer.
*/
public byte[][] cifra(PublicKey pub, byte[] textoClaro)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, InvalidAlgorithmParameterException {
byte[] textoCifrado = null;
byte[] chaveCifrada = null;
// -- A) Gerando uma chave simétrica de 128 bits
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128);
SecretKey sk = kg.generateKey();
byte[] chave = sk.getEncoded();
// -- B) Cifrando o texto com a chave simétrica gerada
Cipher aescf = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
aescf.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(chave, "AES"),
ivspec);
textoCifrado = aescf.doFinal(textoClaro);
// -- C) Cifrando a chave com a chave pública
Cipher rsacf = Cipher.getInstance("RSA");
rsacf.init(Cipher.ENCRYPT_MODE, pub);
chaveCifrada = rsacf.doFinal(chave);
return new byte[][] { textoCifrado, chaveCifrada };
}
}
/**
* Efetua a parte da decifração
*/
class Decifrador {
/**
* Decifra o bloco de dados e a chave cifrada usando a chave privada do destinatário.
* @param pvk A chave privada.
* @param textoCifrado Os dados cifrados.
* @param chaveCifrada A chave cifrada.
* @return Os dados decifrados.
* @throws NoSuchAlgorithmException Algoritmo AES não disponível na sua versão do JDK.
* @throws NoSuchPaddingException PKCS5Padding não disponível na sua versão do JDK.
* @throws InvalidKeyException Se a chave passada for inválida.
* @throws IllegalBlockSizeException Não deve ocorrer.
* @throws BadPaddingException Se houver um erro de decifração (chave incorreta ou texto
* cifrado incorreto, por exemplo)
* @throws InvalidAlgorithmParameterException Não deve ocorrer.
*/
public byte[] decifra(PrivateKey pvk, byte[] textoCifrado,
byte[] chaveCifrada) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException {
byte[] textoDecifrado = null;
// -- A) Decifrando a chave simétrica com a chave privada
Cipher rsacf = Cipher.getInstance("RSA");
rsacf.init(Cipher.DECRYPT_MODE, pvk);
byte[] chaveDecifrada = rsacf.doFinal(chaveCifrada);
// -- B) Decifrando o texto com a chave simétrica decifrada
Cipher aescf = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivspec = new IvParameterSpec(new byte[16]);
aescf.init(Cipher.DECRYPT_MODE,
new SecretKeySpec(chaveDecifrada, "AES"), ivspec);
textoDecifrado = aescf.doFinal(textoCifrado);
return textoDecifrado;
}
}
/**
* Serve para carregar a chave pública de um arquivo. Não é o melhor jeito
* de guardar a chave (é melhor e mais seguro usar um keystore),
* mas faço isso só para simplificar.
*/
class CarregadorChavePublica {
/**
* Carrega a chave pública serializada de um arquivo.
* @param fPub O arquivo com a chave pública serializada.
* @return A chave pública
* @throws IOException Se não achar o arquivo, ou se houver algum problema
* ao efetuar a des-serialização.
* @throws ClassNotFoundException O objeto contido no arquivo é de uma classe não presente
* neste projeto.
*/
public PublicKey carregaChavePublica(File fPub) throws IOException,
ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fPub));
PublicKey ret = (PublicKey) ois.readObject();
ois.close();
return ret;
}
}
/**
* Serve para carregar a chave privada de um arquivo. Não é o melhor jeito
* de guardar a chave (é melhor e mais seguro usar um keystore),
* mas faço isso só para simplificar.
*/
class CarregadorChavePrivada {
/**
* Carrega a chave privada serializada de um arquivo.
* @param fPvk O arquivo com a chave privada serializada.
* @return A chave privada.
* @throws IOException Se não achar o arquivo, ou se houver algum problema
* @throws ClassNotFoundException O objeto contido no arquivo é de uma classe não presente
* neste projeto.
*/
public PrivateKey carregaChavePrivada(File fPvk) throws IOException,
ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fPvk));
PrivateKey ret = (PrivateKey) ois.readObject();
ois.close();
return ret;
}
}
/**
* Gera um par de chaves e as guarda em formato serializado.
* (não é o formato mais seguro - seria melhor usar um keystore, que pode ser protegido
* por senha), mas faço isto para simplificar a compreensão.
*/
class GeradorParChaves {
private static final int RSAKEYSIZE = 1024;
/**
* Gera um par de chaves e as guarda em formato serializado em arquivos.
* @param fPub O arquivo que irá conter a chave pública.
* @param fPvk O arquivo que irá conter a chave privada.
* @throws IOException Problemas de acesso/gravação do arquivo.
* @throws NoSuchAlgorithmException RSA não disponível nesta versão do JDK.
* @throws InvalidAlgorithmParameterException Não deve ocorrer.
* @throws CertificateException Não deve ocorrer.
* @throws KeyStoreException Não deve ocorrer.
*/
public void geraParChaves(File fPub, File fPvk) throws IOException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException,
CertificateException, KeyStoreException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(new RSAKeyGenParameterSpec(RSAKEYSIZE,
RSAKeyGenParameterSpec.F4));
KeyPair kpr = kpg.generateKeyPair();
PrivateKey priv = kpr.getPrivate();
PublicKey pub = kpr.getPublic();
// -- Gravando a chave pública em formato serializado
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
fPub));
oos.writeObject(pub);
oos.close();
// -- Gravando a chave privada em formato serializado
// -- Não é a melhor forma (deveria ser guardada em um keystore, e
// protegida por senha),
// -- mas isto é só um exemplo
oos = new ObjectOutputStream(new FileOutputStream(fPvk));
oos.writeObject(priv);
oos.close();
}
}
/**
* A classe a seguir cifra a decifra a string "Hello, world!".
*/
class ExemploCriptografia {
/**
* Mostra um array de bytes como um "dump".
* @param b O array de bytes que deve ser mostrado em formato dump.
*/
static void printHex(byte[] b) {
if (b == null) {
System.out.println("(null)");
} else {
for (int i = 0; i < b.length; ++i) {
if (i % 16 == 0) {
System.out.print(Integer
.toHexString((i & 0xFFFF) | 0x10000)
.substring(1, 5)
+ " - ");
}
System.out.print(Integer.toHexString((b[i] & 0xFF) | 0x100)
.substring(1, 3)
+ " ");
if (i % 16 == 15 || i == b.length - 1) {
int j;
for (j = 16 - i % 16; j > 1; --j)
System.out.print(" ");
System.out.print(" - ");
int start = (i / 16) * 16;
int end = (b.length < i + 1) ? b.length : (i + 1);
for (j = start; j < end; ++j)
if (b[j] >= 32 && b[j] <= 126)
System.out.print((char) b[j]);
else
System.out.print(".");
System.out.println();
}
}
System.out.println();
}
}
/**
* O programa principal
* @param args Os argumentos são ignorados.
* @throws Exception Lanço uma Exception para não poluir muito o código de demonstração
* com tratamento de exceções. Em um programa "de verdade" você deveria
* tratar corretamente as exceções.
*/
public static void main(String[] args) throws Exception {
// -- Gera o par de chaves, em dois arquivos (chave.publica e
// chave.privada)
GeradorParChaves gpc = new GeradorParChaves();
gpc.geraParChaves(new File("chave.publica"), new File("chave.privada"));
// -- Cifrando a mensagem "Hello, world!"
byte[] textoClaro = "Hello, world!".getBytes("ISO-8859-1");
CarregadorChavePublica ccp = new CarregadorChavePublica();
PublicKey pub = ccp.carregaChavePublica(new File("chave.publica"));
Cifrador cf = new Cifrador();
byte[][] cifrado = cf.cifra(pub, textoClaro);
printHex(cifrado[0]);
printHex(cifrado[1]);
// -- Decifrando a mensagem
CarregadorChavePrivada ccpv = new CarregadorChavePrivada();
PrivateKey pvk = ccpv.carregaChavePrivada(new File("chave.privada"));
Decifrador dcf = new Decifrador();
byte[] decifrado = dcf.decifra(pvk, cifrado[0], cifrado[1]);
// System.out.println (new String (textoClaro, "ISO-8859-1"));
printHex(decifrado);
}
}
Tuesday, May 27, 2008
Formatação de Números
Se você deseja converter um double ou um BigDecimal de/para uma string em formato monetário - R$ 12.345.678,90 por exemplo - então use java.util.NumberFormat , método getCurrencyInstance. Isso economiza um monte de tempo tentando tirar e pôr pontos, vírgulas e outras coisas ineficientes (e que podem estar erradas também).
Exemplo:
Exemplo:
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
class NumberFormatComMoeda {
public static void main(String[] args) {
NumberFormat nf = NumberFormat.getCurrencyInstance (new Locale ("pt", "BR"));
double d = 12345678.90;
String s = nf.format (d);
System.out.println (s); // deve imprimir "R$ 12.345.678,90"
// Para os que preferem double:
try {
double d1 = nf.parse (s).doubleValue();
System.out.println (d1); // imprime "1.23456789E7"
} catch (ParseException ex) {
ex.printStackTrace();
}
// Para os que preferem BigDecimal:
((DecimalFormat) nf).setParseBigDecimal(true);
try {
BigDecimal bd = (BigDecimal) (nf.parse(s));
System.out.println (bd); // imprime "12345678.90"
} catch (ParseException ex) {
ex.printStackTrace();
}
}
}
Wednesday, April 23, 2008
Cliff Click's NonBlocking HashTable
Cliff Click has made available his new brainchild, the NonBlocking HashTable.
He claims that it is faster than java.util.concurrent.ConcurrentHashMap.
Check: http://blogs.azulsystems.com/cliff/2007/04/nonblocking_has.html
http://sourceforge.net/projects/high-scale-lib
He claims that it is faster than java.util.concurrent.ConcurrentHashMap.
Check: http://blogs.azulsystems.com/cliff/2007/04/nonblocking_has.html
http://sourceforge.net/projects/high-scale-lib
DecimalFormat, SimpleDateFormat - Threading Issues
The classes java.text.DecimalFormat and java.text.SimpleDateFormat are very useful, but Sun's implementation is not thread-safe. It means that if you want to have a static instance of any of these classes in your EJB, Servlet or JSP page, you can have sporadic problems with incorrect formatting.
You'll need to use a ThreadLocal, or use a local object.
ThreadLocal sample usage:
...
You'll need to use an additional "get".
You'll need to use a ThreadLocal, or use a local object.
ThreadLocal sample usage:
private static ThreadLocaldefaultCurrencyFormat = new ThreadLocal () {
protected synchronized Format initialValue() {
return new DecimalFormat ("0.00",
new DecimalFormatSymbols (new Locale ("pt", "BR")));
}
};
...
String s = defaultCurrencyFormat.get().format (123.45);
You'll need to use an additional "get".
Wednesday, March 22, 2006
Recommended reading for Bouncy Castle users
If you're interested in using the Bouncy Castle JCE Library, you'll need to check this book:
Beginning Cryptography with Java, by David Hook.
You can download the examples of the book, if you just want to have a test suite for Bouncy Castle.
Beginning Cryptography with Java, by David Hook.
You can download the examples of the book, if you just want to have a test suite for Bouncy Castle.
Subscribe to:
Posts (Atom)