Validar, gerar e formatar CNPJ em Java
Classe utilitária em Java 17 para validar, gerar e formatar CNPJ numérico e alfanumérico, testes com JUnit 5 @ParameterizedTest e chamada à API.
Em sistemas Java, o CNPJ costuma estar espalhado em entidades JPA, validadores do Bean Validation e integrações com outros sistemas. Com o CNPJ alfanumérico, todo código que faz Long.parseLong ou Character.getNumericValue no CNPJ passa a quebrar ou, pior, a calcular errado. A classe abaixo usa apenas o JDK, compila em Java 17 e trata os dois formatos com a mesma conta: cada caractere vale c - 48, ou seja, o código ASCII menos 48. Dígitos continuam valendo 0 a 9; A vale 17 e Z vale 42. O guia CNPJ alfanumérico: o que muda no seu sistema lista os outros pontos de atenção.
Validar CNPJ em Java (numérico e alfanumérico)
import java.security.SecureRandom;
import java.util.Locale;
import java.util.regex.Pattern;
public final class Cnpj {
private static final int[] PESOS_1 = {5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
private static final int[] PESOS_2 = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
private static final Pattern SEPARADORES = Pattern.compile("[./\\-\\s]");
private static final Pattern FORMATO = Pattern.compile("[A-Z0-9]{12}[0-9]{2}");
private static final SecureRandom RANDOM = new SecureRandom();
private Cnpj() {}
static int calcularDigito(String base, int[] pesos) {
int soma = 0;
for (int i = 0; i < base.length(); i++) {
soma += (base.charAt(i) - 48) * pesos[i];
}
int resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
public static boolean validar(String valor) {
if (valor == null) return false;
String cnpj = SEPARADORES.matcher(valor.toUpperCase(Locale.ROOT)).replaceAll("");
if (!FORMATO.matcher(cnpj).matches()) return false;
String base = cnpj.substring(0, 12);
if (base.chars().allMatch(c -> c == base.charAt(0))) return false;
int dv1 = calcularDigito(base, PESOS_1);
int dv2 = calcularDigito(base + dv1, PESOS_2);
return cnpj.substring(12).equals("" + dv1 + dv2);
}Detalhes que fazem diferença:
toUpperCase(Locale.ROOT)evita surpresas com a localidade da JVM. Em turco, por exemplo,"i".toUpperCase()não vira"I".matches()exige que a string inteira siga o padrão: 12 caracteres deA-Z0-9e dois dígitos no fim.allMatchrecusa bases com os 12 primeiros caracteres iguais, como 00.000.000/0000-00, que fecham a conta, mas não são válidas.base.charAt(i) - 48faz aritmética direta com ochar. Não useCharacter.getNumericValue: para letras ele devolve 10 a 35, e o resultado fica errado.
Com 11.222.333/0001-81, a primeira soma é 102, o resto é 3 e o dígito é 8; a segunda soma é 120, o resto é 10 e o dígito é 1. Com 12.ABC.345/01DE-35, as somas são 459 e 424, os restos 8 e 6, e os dígitos 3 e 5. Para usar com Bean Validation, crie uma anotação @CnpjValido cujo ConstraintValidator chame Cnpj.validar.
Gerar CNPJ válido em Java
private static final String DIGITOS = "0123456789";
private static final String ALFANUMERICOS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static String gerar(boolean alfanumerico, boolean formatado) {
String alfabeto = alfanumerico ? ALFANUMERICOS : DIGITOS;
String base;
do {
StringBuilder sb = new StringBuilder(12);
for (int i = 0; i < 12; i++) {
sb.append(alfabeto.charAt(RANDOM.nextInt(alfabeto.length())));
}
base = sb.toString();
} while (base.matches("(.)\\1{11}"));
int dv1 = calcularDigito(base, PESOS_1);
int dv2 = calcularDigito(base + dv1, PESOS_2);
String cnpj = base + dv1 + dv2;
return formatado ? formatar(cnpj) : cnpj;
}SecureRandom evita sequências previsíveis. O CNPJ gerado passa na validação, mas pode coincidir com um CNPJ real por acaso; use-o só em testes e dados fictícios. Para gerar filiais de uma mesma raiz, mantenha os 8 primeiros caracteres e varie a ordem, como mostra o guia da estrutura do CNPJ. O gerador de CNPJ em lote faz isso sem código.
Formatar e limpar CNPJ em Java
public static String limpar(String valor) {
return valor == null ? "" : valor.toUpperCase(Locale.ROOT).replaceAll("[^A-Z0-9]", "");
}
public static String formatar(String valor) {
String c = limpar(valor);
if (c.length() != 14) {
throw new IllegalArgumentException("CNPJ precisa de 14 caracteres");
}
return c.substring(0, 2) + "." + c.substring(2, 5) + "." + c.substring(5, 8)
+ "/" + c.substring(8, 12) + "-" + c.substring(12);
}
}Cnpj.formatar("12abc34501de35") devolve "12.ABC.345/01DE-35", e aplicar o método de novo no resultado não muda nada. Na entidade JPA, mapeie o CNPJ como String com @Column(length = 14) e grave a versão limpa; um campo Long não comporta letras. Para validar o formato com máscara em uma anotação @Pattern, veja o guia de regex de CNPJ.
Testes
Com JUnit 5, @ParameterizedTest e @CsvSource geram um teste por linha. O artefato junit-jupiter-params precisa estar no classpath.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
class CnpjTest {
@ParameterizedTest
@CsvSource({
"11.222.333/0001-81, true",
"12.ABC.345/01DE-35, true",
"11.222.333/0001-82, false",
"00.000.000/0000-00, false",
})
void validar(String cnpj, boolean esperado) {
assertEquals(esperado, Cnpj.validar(cnpj));
}
@ParameterizedTest
@ValueSource(booleans = {false, true})
void geradosSaoValidos(boolean alfanumerico) {
for (int i = 0; i < 1000; i++) {
assertTrue(Cnpj.validar(Cnpj.gerar(alfanumerico, false)));
}
}
@Test
void formatarEhIdempotente() {
assertEquals("12.ABC.345/01DE-35", Cnpj.formatar(Cnpj.formatar("12abc34501de35")));
}
}Mais números para a tabela estão nos exemplos de CNPJ alfanumérico para teste.
Usar a API em vez de reimplementar
A API do cpf.dev.br valida e gera CNPJs por HTTP, sem chave, com limite de 60 requisições por minuto por IP. Envie o CNPJ sem máscara ou com a barra codificada como %2F. O HttpClient do JDK 11 em diante resolve sem dependências.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
public class ApiCnpj {
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
HttpRequest req = HttpRequest.newBuilder(
URI.create("https://www.cpf.dev.br/api/v1/cnpj/validar/12ABC34501DE35")).build();
String json = http.send(req, HttpResponse.BodyHandlers.ofString()).body();
System.out.println(json);
// {"valido":true,"cnpj":"12ABC34501DE35","formatado":"12.ABC.345/01DE-35",
// "formato":"alfanumerico","raiz":"12ABC345","ordem":"01DE","matriz":false}
HttpRequest lote = HttpRequest.newBuilder(URI.create(
"https://www.cpf.dev.br/api/v1/cnpj/gerar?quantidade=5&formato=alfanumerico&formatado=true")).build();
System.out.println(http.send(lote, HttpResponse.BodyHandlers.ofString()).body());
}
}Para ler o JSON em objetos, use a biblioteca que o projeto já tiver, como Jackson ou Gson. Em formulários, a classe local continua sendo a melhor opção. Para conferir um número à mão, use o validador de CNPJ.