@mastersigner/sdk
v1.1.12
Published
Master Signer SDK — Integração de assinatura digital ICP-Brasil para sistemas web
Maintainers
Readme
Master Signer SDK
Assinatura digital ICP-Brasil (A1 e A3) no seu sistema — na web e dentro do seu app.
- 🔐 A1 (
.pfx/.p12) e A3 (token USB / smartcard, na web) - 📄 CAdES destacado (
.p7s) e PAdES B-B (assinatura embutida no PDF) - 🛡️ A chave privada nunca sai do dispositivo do usuário — nem para o seu servidor, nem para o nosso
- 📱 Mesmo código na web e no app: o SDK escolhe o caminho sozinho
Instalação
npm install @mastersigner/sdkSe o seu projeto é um app Capacitor (iOS/Android), rode também:
npx cap syncO plugin nativo vem dentro deste mesmo pacote — não há um segundo pacote para instalar. O
cap sync o encontra sozinho: no iOS pelo Package.swift (SPM), no Android pelo Gradle.
Um passo obrigatório no iOS
Acrescente ao ios/App/App/Info.plist do seu app:
<key>NSFaceIDUsageDescription</key>
<string>O Face ID protege seu certificado digital: é exigido para autorizar cada assinatura.</string>Sem essa chave o iOS encerra o app (SIGABRT) no instante em que o Face ID é acionado — o sintoma é cruel: importar funciona (não usa Face ID) e assinar fecha o aplicativo. É exigência do sistema, não nossa; o texto aparece para o usuário e pode ser reescrito.
No Android não há passo nenhum: a permissão de biometria entra pelo próprio plugin. O aparelho precisa ter digital ou reconhecimento facial cadastrado — é o cofre que exige isso para guardar a chave, e sem nenhum cadastrado a importação falha dizendo exatamente isso.
Requisitos: Capacitor 8, iOS 15+, Android 6+ (API 23).
Como cada plataforma assina
| | Quem assina | O que o usuário precisa | |---|---|---| | Web | Extensão Master Signer (+ agente nativo para A3) | Instalar a extensão | | App (iOS/Android) | Plugin nativo deste pacote — chave no Keychain / Android Keystore, biometria a cada assinatura | Importar o certificado A1 uma vez |
Uso
import { MasterSigner } from '@mastersigner/sdk';
// apiKey: só no app. Na web a autorização é pelo domínio cadastrado no painel.
const signer = new MasterSigner({ apiKey: 'msk_...' });
const result = await signer.signDocument(pdfBytes);
if (result.signatureFormat === 'pades') {
// PDF com a assinatura embutida — abre no Adobe e no VerificAR
salvar(result.signedPdf);
} else {
// .p7s em base64 — guarde junto do PDF original, ele não vale sozinho
salvar(result.signature);
}Junto vêm os metadados para o seu registro: thumbprint, commonName, taxId, issuer,
certificateDer, signedAt, contentHash (SHA-256 do documento original) e algorithm — não
crave esse último no seu código, ele vem de quem assinou.
O formato vem do painel, não do seu código: o dono da conta escolhe entre CAdES e PAdES em Integrações, e vale na web e no app sem você publicar nada. Quando o formato for imposto pelo documento (co-assinar um ZIP CAdES, por exemplo), force na chamada:
await signer.signDocument(pdfBytes, { signatureFormat: 'cades' });Certificados
const certs = await signer.listCertificates();
await signer.signDocument(pdfBytes, { certificateThumbprint: certs[0].thumbprint });A tela de certificados é do SDK. Você não precisa construir seletor de arquivo, campo de
senha nem lista: com mais de um certificado disponível (ou nenhum, no app), o signDocument
abre a tela sozinho, o usuário escolhe ou importa, e a assinatura continua.
await signer.manageCertificates(); // abrir por conta própria (menu do seu app)
const cert = await signer.chooseCertificate(); // só o seletor; null se o usuário fecharEla vive num shadow DOM: o CSS do seu app não entra e o dela não vaza.
As cores são opcionais — sem elas, sai a paleta do Master Signer. Para combinar com o seu app, defina as duas paletas uma vez e diga na chamada qual usar:
const signer = new MasterSigner({ apiKey, appearance: {
light: { background: '#f4f7f6', accent: '#0f766e' },
dark: { background: '#0b1220', accent: '#38bdf8' },
}});
await signer.signDocument(pdf, { theme: temaAtualDoApp }); // sem isto, abre claro
await signer.manageCertificates({ theme: 'dark' });Por que a paleta fica no construtor e o tema na chamada: a paleta é constante do seu app, o tema é estado de runtime — o usuário troca claro/escuro quando quiser, e o SDK não tem como adivinhar (ele nunca olha o tema do sistema operacional, que pode divergir do seu).
São duas cores por tema, não onze: texto, borda, superfície e a cor sobre o botão saem
derivadas por contraste do background. Fundo escuro → texto claro; botão claro → texto
escuro. É o que impede uma combinação infeliz de produzir texto ilegível numa tela de
assinatura.
A marca Master Signer aparece no rodapé em qualquer app e não é configurável — ela também segue o contraste do fundo.
Prefere a sua própria interface? new MasterSigner({ apiKey, ui: false }) — aí os mesmos casos
viram erro (no_certificate, certificate_not_chosen) para você tratar.
Se precisar das operações diretas:
await signer.importCertificate(); // só no app; abre as telas NATIVAS de arquivo e senha
await signer.removeCertificate(thumbprint);Você não recebe o arquivo nem a senha — de propósito
importCertificate() não aceita bytes nem senha: quem os coleta é o código nativo, em
telas do sistema operacional. O .p12 e a senha nunca existem no JavaScript — nem no seu
código, nem no nosso —, e não há método na ponte do Capacitor que os aceite. Para o JS sobem
só os metadados do certificado.
É uma decisão de segurança, não de conveniência: enquanto existia um importP12(bytes, senha),
bastava um log distraído ou um envio ao seu servidor para vazar o certificado de um cliente
seu. Do jeito atual, esse acidente não é possível — e você não precisa auditar nada a respeito.
A chave privada entra no Keychain (iOS) ou no Android Keystore protegida por biometria, e nunca sai: assinar acontece dentro do cofre. A senha é usada só para abrir o arquivo e não é guardada.
Erros
Todo erro é um MasterSignerError, com duas mensagens e um código:
import { MasterSignerError } from '@mastersigner/sdk';
try {
await signer.signDocument(pdfBytes);
} catch (err) {
if (err instanceof MasterSignerError) {
mostrarAoUsuario(err.message); // sempre exibível: em português, sem jargão
if (err.code === 'cancelled') return; // o usuário fechou a tela; não é falha
}
}messageé escrito para a tela do usuário final. Pode exibir sem medo.devMessageé o recado técnico (vai também para o console) — não exiba na interface.codeé o que o seu código testa. Alguns:no_certificate,certificate_not_chosen,invalid_p12,auth_cancelled,biometrics_required,key_invalidated,backend_refused,cancelled,plugin_unavailable. Códigos novos podem aparecer, então trate o desconhecido pelomessage.
Recusas do servidor (cota esgotada, versão mínima, bundle não autorizado) chegam em
backend_refused com o texto do próprio backend, que já vem escrito para o usuário — e muda
sem você publicar nada.
Chave de API (só no app)
A chave é criada junto com o app, no painel: em Integrações, cadastre o bundle ID (com.exemplo.app) e a chave dele aparece uma única vez — copie na hora, guardamos só um hash.
Cada chave vale exclusivamente para o app com que nasceu. Onde guardá-la é decisão sua: embutida no código ou buscada no seu backend. Embutida, ela é extraível do binário — e o que se perde nesse caso é cota da sua organização, por isso o painel permite gerar uma nova (↻) a qualquer momento, o que revoga a anterior.
Na web não existe chave: a autorização é pelo domínio cadastrado, e nada secreto vai para o frontend.
Atalhos de formato explícito
Continuam disponíveis, para quem prefere decidir no código:
await signer.sign({ data, certificateThumbprint }); // CAdES destacado
await signer.signPdf(pdfBytes, { thumbprint }); // PAdES B-B
await signer.signBatch({ items, certificateThumbprint }); // lote, um PIN só (web)Sem build step (só web)
<script src="https://get.mastersigner.app/sdk/v1/mastersigner.min.js"></script>
<script>
const signer = new MasterSigner();
</script>O bundle do CDN é web-only: dentro de um app, o caminho nativo exige a instalação via npm.
Documentação
- API completa: https://mastersigner.app/app/docs
- CAdES × PAdES: https://mastersigner.app/app/docs
- Verificador de assinaturas: https://mastersigner.app/verificar
Licença
Proprietária — veja LICENSE. Para uso comercial, contrate um plano.
