Classe utilitária em Java 17 sem dependências, mais a anotação @Cnpj de Bean Validation (Jakarta) para usar direto nos DTOs do Spring Boot.
0=0 … 9=9, A=17 … Z=42), os pesos vão de 2 a 9 da direita para a esquerda, soma-se tudo, divide-se por 11 e o dígito é 0 quando o resto é menor que 2, ou 11 menos o resto. Ver a conta detalhada.Código completo
package br.com.exemplo.cnpj;
import java.util.Locale;
import java.util.regex.Pattern;
public final class Cnpj {
private static final Pattern NAO_ALFANUMERICO = Pattern.compile("[^0-9A-Z]");
private static final Pattern FORMATO = Pattern.compile("[0-9A-Z]{12}\\d{2}");
private static final Pattern REPETIDO = Pattern.compile("(.)\\1{13}");
private Cnpj() {
}
/** Remove máscara e normaliza para maiúsculas. */
public static String limpar(String valor) {
if (valor == null) {
return "";
}
return NAO_ALFANUMERICO.matcher(valor.toUpperCase(Locale.ROOT)).replaceAll("");
}
/** Calcula um dígito do módulo 11 (valor do caractere = ASCII - 48). */
private static int digito(String sequencia) {
int soma = 0;
int tamanho = sequencia.length();
for (int i = 0; i < tamanho; i++) {
int valor = sequencia.charAt(i) - 48;
int peso = ((tamanho - 1 - i) % 8) + 2;
soma += valor * peso;
}
int resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
/** Devolve os dois dígitos verificadores da base de 12 caracteres. */
public static String calcularDv(String base) {
int primeiro = digito(base);
int segundo = digito(base + primeiro);
return String.valueOf(primeiro) + segundo;
}
/** Valida CNPJ numérico ou alfanumérico, com ou sem máscara. */
public static boolean valido(String valor) {
String cnpj = limpar(valor);
if (!FORMATO.matcher(cnpj).matches() || REPETIDO.matcher(cnpj).matches()) {
return false;
}
return calcularDv(cnpj.substring(0, 12)).equals(cnpj.substring(12));
}
/** Aplica a máscara 00.000.000/0000-00. */
public static String formatar(String valor) {
String cnpj = limpar(valor);
if (cnpj.length() != 14) {
return cnpj;
}
return cnpj.substring(0, 2) + "." + cnpj.substring(2, 5) + "."
+ cnpj.substring(5, 8) + "/" + cnpj.substring(8, 12)
+ "-" + cnpj.substring(12);
}
}
Testes e integração com o framework
// Anotação reutilizável nos DTOs
@Documented
@Constraint(validatedBy = CnpjValidator.class)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface CnpjValido {
String message() default "CNPJ inválido";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class CnpjValidator implements ConstraintValidator<CnpjValido, String> {
@Override
public boolean isValid(String valor, ConstraintValidatorContext contexto) {
return valor == null || valor.isBlank() || Cnpj.valido(valor);
}
}
// Teste
class CnpjTest {
@Test
void validaExemploOficial() {
assertEquals("35", Cnpj.calcularDv("12ABC34501DE"));
assertTrue(Cnpj.valido("12.ABC.345/01DE-35"));
assertTrue(Cnpj.valido("12abc34501de35"));
}
@Test
void mantemCompatibilidadeComNumerico() {
assertTrue(Cnpj.valido("11.222.333/0001-81"));
assertFalse(Cnpj.valido("11.222.333/0001-82"));
}
}
Detalhes que costumam quebrar em Java
- Em Java,
charAt(i)devolve umchar; a subtração- 48já promove paraintcom o valor ASCII correto. - Se o seu projeto usa
@CNPJdo Hibernate Validator, verifique a versão: as antigas só aceitam dígitos e vão reprovar CNPJ alfanumérico válido. - Colunas JPA precisam virar
String/VARCHAR.LongouBigIntegernão guardam letras — veja como migrar a coluna.
Casos de teste recomendados
Cubra pelo menos estes cenários — eles pegam praticamente todos os erros de implementação:
| Entrada | Esperado | O que testa |
|---|---|---|
12.ABC.345/01DE-35 | válido | exemplo oficial, com máscara |
12abc34501de35 | válido | normalização para maiúsculas |
11.222.333/0001-81 | válido | compatibilidade com o formato numérico |
11.222.333/0001-82 | inválido | dígito verificador errado |
12ABC34501DEAB | inválido | letra no dígito verificador |
12ABC34501DE3 | inválido | tamanho incorreto |
00.000.000/0000-00 | inválido | sequência repetida |
"" / null | inválido | entrada vazia sem exceção |
Precisa de mais massa de teste? O gerador produz até 500 CNPJs alfanuméricos válidos de uma vez, com opção de baixar em .txt.