Sin firma, una línea en el archivo hosts de un servidor basta para responder «licencia válida» en nuestro lugar. Cada respuesta lleva una firma Ed25519 sobre la forma canónica de los datos: claves ordenadas, JSON compacto. Verifícala antes de fiarte de nada. La biblioteca lo hace por ti, incluso sobre la copia en caché, que a su vez está vinculada a su host para que no pueda copiarse de un sitio a otro.
Solo hace falta si llamas a la API directamente. Reconstruye la forma canónica a partir del objeto analizado, nunca de los bytes en crudo recibidos, que pueden diferir, y después verifica la firma Ed25519 separada con nuestra clave pública.
Ed25519, 32 bytes en crudo en base64. Es la misma para todos los autores y todos los productos.
/PTkKQpweo9XCLOS0+b0vgrecx7Ly/SsAuwTiA737Tw=
La firma cubre el objeto donnees serializado con estas cuatro reglas, y nada más. Falla una y todas las firmas parecerán inválidas.
Para la respuesta de arriba, los bytes firmados exactamente:
{"domaine":"exemple.fr","emis":1790687453,"essai":false,"hote":"boutique.exemple.fr","motif":"","ok":true,"quand":"","revalider":1790773853,"sursis":2592000}
function signature_valide(array $donnees, $signature, $publique)
{
ksort($donnees);
$corps = json_encode($donnees, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
return sodium_crypto_sign_verify_detached(
base64_decode($signature, true),
$corps,
base64_decode($publique, true)
);
}
import { createPublicKey, verify } from 'node:crypto';
const publique = createPublicKey({
key: Buffer.concat([Buffer.from('302a300506032b6570032100', 'hex'),
Buffer.from('/PTkKQpweo9XCLOS0+b0vgrecx7Ly/SsAuwTiA737Tw=', 'base64')]),
format: 'der',
type: 'spki',
});
const canonique = (d) =>
JSON.stringify(Object.fromEntries(Object.keys(d).sort().map((k) => [k, d[k]])));
const valide = verify(null, Buffer.from(canonique(rep.donnees), 'utf8'),
publique, Buffer.from(rep.signature, 'base64'));
import base64, json
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
publique = Ed25519PublicKey.from_public_bytes(base64.b64decode('/PTkKQpweo9XCLOS0+b0vgrecx7Ly/SsAuwTiA737Tw='))
canonique = json.dumps(rep['donnees'], sort_keys=True,
separators=(',', ':'), ensure_ascii=False)
publique.verify(base64.b64decode(rep['signature']), canonique.encode('utf-8'))
Escríbenos desde tu área de autor. Una pregunta que hubo que hacer suele significar que a esta página le falta un párrafo.