JavaScript Python PHP
async function executar(values = {}) {
const encoder = new TextEncoder();
function toBase64(bytes) {
let binary = '';
for (let i = 0; i < bytes.length; i += 8192)
binary += String.fromCharCode(...bytes.subarray(i, i + 8192));
return btoa(binary);
}
function fromBase64(value) {
const clean = value.replace(/\s/g, '');
if (!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(clean))
throw new Error('Base64 inválido. Confira caracteres e padding (=).');
const bytes = Uint8Array.from(atob(clean), c => c.charCodeAt(0));
if (toBase64(bytes) !== clean)
throw new Error('Base64 não canônico.');
return bytes;
}
function fromHex(value) {
if (!/^(?:[a-fA-F0-9]{2})+$/.test(value))
throw new Error('Hexadecimal inválido: use pares de caracteres, sem espaços.');
return Uint8Array.from(value.match(/../g), byte => parseInt(byte, 16));
}
function publicKeyBytes(value) {
const match = /^\s*-----BEGIN PUBLIC KEY-----\s+([A-Za-z0-9+/=\s]+)-----END PUBLIC KEY-----\s*$/.exec(value);
if (!match)
throw new Error('Informe uma chave pública SPKI em PEM (BEGIN PUBLIC KEY).');
return fromBase64(match[1]);
}
function integer(values, key, fallback, min, max) {
const raw = values[key] ?? String(fallback);
const value = Number(raw);
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(value) || value < min || value > max)
throw new Error(`${key}: informe um inteiro entre ${min} e ${max}.`);
return value;
}
function option(values, key, options, fallback) {
const value = values[key] ?? fallback;
if (!options.includes(value))
throw new Error(`Opção inválida: ${key}.`);
return value;
}
function required(values, key) {
if (!values[key])
throw new Error(`Preencha o campo ${key}.`);
return values[key];
}
async function verifyDigitalSignature(values) {
const algorithm = option(values, 'algorithm', ['RSASSA-PKCS1-v1_5', 'RSA-PSS', 'ECDSA'], 'RSASSA-PKCS1-v1_5');
const hash = option(values, 'hash', ['SHA-256', 'SHA-384', 'SHA-512'], 'SHA-256');
if (algorithm === 'ECDSA' && hash !== 'SHA-256')
throw new Error('ECDSA nesta ferramenta usa P-256 com SHA-256.');
const signature = values.signatureFormat === 'hex' ? fromHex(required(values, 'signature')) : fromBase64(required(values, 'signature'));
if (algorithm === 'ECDSA' && signature.length !== 64)
throw new Error('Assinatura ECDSA deve ter 64 bytes no formato bruto IEEE P1363 (r || s), não DER.');
const key = await crypto.subtle.importKey('spki', publicKeyBytes(required(values, 'publicKey')).buffer, algorithm === 'ECDSA' ? { name: 'ECDSA', namedCurve: 'P-256' } : { name: algorithm, hash }, false, ['verify']);
const params = algorithm === 'RSA-PSS' ? { name: algorithm, saltLength: integer(values, 'saltLength', 32, 0, 64) } : algorithm === 'ECDSA' ? { name: algorithm, hash } : { name: algorithm };
const message = values.messageFormat === 'base64' ? fromBase64(values.message ?? '') : encoder.encode(values.message ?? '');
return crypto.subtle.verify(params, key, signature.buffer, message.buffer);
}
async function runSecurityTool(kind, values) {
if (!crypto.subtle)
throw new Error('Esta ferramenta requer Web Crypto em HTTPS ou localhost.');
if (Object.values(values).some(value => value.length > 1000000))
throw new Error('Limite de entrada: 1 milhão de caracteres por campo.');
{
{
const valid = await verifyDigitalSignature(values);
return { text: valid ? 'Assinatura válida para esta mensagem e chave pública.' : 'Assinatura inválida para esta mensagem e chave pública.', valid };
}
}
}
return await runSecurityTool("signature", values);
}
import base64
import re
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import padding, ec
from cryptography.hazmat.primitives.asymmetric.utils import encode_dss_signature
from cryptography.exceptions import InvalidSignature
def executar(values=None):
values = {} if values is None else values
if not isinstance(values, dict) or any(not isinstance(v,str) or len(v.encode('utf-16-le',errors='surrogatepass')) // 2 > 1000000 for v in values.values()): raise ValueError('Informe campos textuais de até 1 milhão de caracteres')
def integer(key):
raw = values.get(key,'')
if not re.fullmatch(r'[0-9]+',raw): raise ValueError('Informe inteiro válido: ' + key)
return int(raw)
algorithm = values.get('algorithm','RSASSA-PKCS1-v1_5')
hash_name = values.get('hash','SHA-256')
if hash_name not in ('SHA-256','SHA-384','SHA-512') or algorithm not in ('RSASSA-PKCS1-v1_5','RSA-PSS','ECDSA'): raise ValueError('Algoritmo inválido')
digest = {'SHA-256':hashes.SHA256,'SHA-384':hashes.SHA384,'SHA-512':hashes.SHA512}[hash_name]()
signature = bytes.fromhex(values['signature']) if values.get('signatureFormat') == 'hex' else base64.b64decode(re.sub(r'\s','',values['signature']),validate=True)
message = base64.b64decode(re.sub(r'\s','',values.get('message','')),validate=True) if values.get('messageFormat') == 'base64' else values.get('message','').encode()
key = serialization.load_pem_public_key(values['publicKey'].encode())
if algorithm == 'ECDSA':
if hash_name != 'SHA-256' or len(signature) != 64 or not isinstance(key.curve,ec.SECP256R1): raise ValueError('ECDSA exige P-256/SHA-256 e assinatura P1363 de 64 bytes')
signature = encode_dss_signature(int.from_bytes(signature[:32],'big'), int.from_bytes(signature[32:],'big'))
params = (ec.ECDSA(digest),)
else:
length = int(values.get('saltLength','32'))
if not 0 <= length <= 64: raise ValueError('Salt PSS fora dos limites')
params = (padding.PSS(mgf=padding.MGF1(digest),salt_length=length) if algorithm == 'RSA-PSS' else padding.PKCS1v15(), digest)
try: key.verify(signature,message,*params); valid = True
except InvalidSignature: valid = False
return {'text':'Assinatura válida para esta mensagem e chave pública.' if valid else 'Assinatura inválida para esta mensagem e chave pública.', 'valid':valid}
<?php
require 'vendor/autoload.php';
use phpseclib3\Crypt\PublicKeyLoader;
use phpseclib3\Crypt\RSA;
function executar($values = []) {
foreach ($values as $value) if (!is_string($value) || strlen($value)>1000000) throw new InvalidArgumentException("Informe campos textuais de até 1 milhão de bytes");
$integer = function ($key) use ($values) { $raw = $values[$key] ?? ""; if (!preg_match("/^[0-9]+$/D",$raw)) throw new InvalidArgumentException("Inteiro inválido: " . $key); return (int)$raw; };
$algorithm = $values['algorithm'] ?? 'RSASSA-PKCS1-v1_5'; $hash = $values['hash'] ?? 'SHA-256';
if (!in_array($algorithm,['RSASSA-PKCS1-v1_5','RSA-PSS','ECDSA'],true) || !in_array($hash,['SHA-256','SHA-384','SHA-512'],true)) throw new InvalidArgumentException('Algoritmo inválido');
$decode = function ($value) { $clean = preg_replace('/\s/','',$value); $bytes = base64_decode($clean,true); if ($bytes === false || base64_encode($bytes)!==$clean) throw new InvalidArgumentException('Base64 inválido'); return $bytes; };
$raw = $values['signature'] ?? '';
if (($values['signatureFormat'] ?? 'base64') === 'hex') { if (!preg_match('/^(?:[0-9a-f]{2})+$/iD',$raw)) throw new InvalidArgumentException('Hex inválido'); $signature = hex2bin($raw); } else $signature = $decode($raw);
$message = ($values['messageFormat'] ?? 'utf8') === 'base64' ? $decode($values['message'] ?? '') : ($values['message'] ?? '');
$pem = $values['publicKey'] ?? '';
if (!preg_match('/^\s*-----BEGIN PUBLIC KEY-----\s+[A-Za-z0-9+\/=\s]+-----END PUBLIC KEY-----\s*$/D',$pem)) throw new InvalidArgumentException('Exige SPKI PEM');
if ($algorithm === 'ECDSA') {
if ($hash !== 'SHA-256' || strlen($signature)!==64) throw new InvalidArgumentException('ECDSA exige P-256/SHA-256 e assinatura P1363 de 64 bytes');
$key = openssl_pkey_get_public($pem); $details = $key ? openssl_pkey_get_details($key) : false;
if (!$details || ($details['ec']['curve_name'] ?? '') !== 'prime256v1') throw new InvalidArgumentException('Chave P-256 inválida');
$integerDer = function ($bytes) { $bytes = ltrim($bytes,"\x00"); if ($bytes === '') $bytes="\x00"; if (ord($bytes[0]) & 128) $bytes="\x00".$bytes; return "\x02".chr(strlen($bytes)).$bytes; };
$body = $integerDer(substr($signature,0,32)).$integerDer(substr($signature,32)); $signature="\x30".chr(strlen($body)).$body;
$status = openssl_verify($message,$signature,$key,OPENSSL_ALGO_SHA256);
if ($status === -1) throw new RuntimeException('Falha técnica de OpenSSL');
$valid = $status === 1;
} else {
$key = PublicKeyLoader::load($pem)->withHash(strtolower(str_replace('-','',$hash)));
if ($algorithm === 'RSA-PSS') { $length = $integer('saltLength'); if ($length>64) throw new InvalidArgumentException('Salt PSS fora dos limites'); $key=$key->withPadding(RSA::SIGNATURE_PSS)->withMGFHash(strtolower(str_replace('-','',$hash)))->withSaltLength($length); }
else $key=$key->withPadding(RSA::SIGNATURE_PKCS1);
$valid = $key->verify($message,$signature);
}
return ['text'=>$valid ? 'Assinatura válida para esta mensagem e chave pública.' : 'Assinatura inválida para esta mensagem e chave pública.', 'valid'=>$valid];
}