fjordtools / Security / Sign & Verify
Sign & Verify
Sign and verify digital signatures in your browser: RSA PKCS#1 v1.5, RSA-PSS, ECDSA and Ed25519, Base64 or hex. Keys never leave this tab and are not stored.
For testing and learning: use a throwaway key, not one that protects anything real. The key stays in this tab and is neither sent nor stored; the page makes no requests after it loads (check it yourself). That does not cover your browser extensions, your clipboard or anyone watching your screen, so keep real keys on the machine that uses them. Verifying needs only a public key and is safe with real keys.
Verifying needs only a public key. Your key, message and signature stay in this tab: nothing is sent or stored, and the page makes no requests after it loads (check it yourself).
About Sign & Verify
How do I sign a message or a file with a private key?
Open Sign, paste a private key (PEM, JWK or an OpenSSH key file), type a message or open a file, and press Sign. The page signs the exact bytes and shows the signature as Base64, Base64url or hex, with a .sig download and the OpenSSL command that does the same. The key decides the algorithm: RSA gives PKCS#1 v1.5 or PSS, an EC key gives ECDSA, an Ed25519 key gives Ed25519.
The same signature with OpenSSL 3, from key generation to the check:
openssl genpkey -algorithm ED25519 -out private.pem openssl pkey -in private.pem -pubout -out public.pem printf %s 'Hello, fjordtools' > message.txt openssl pkeyutl -sign -rawin -inkey private.pem -in message.txt -out sig.bin openssl pkeyutl -verify -rawin -pubin -inkey public.pem -in message.txt -sigfile sig.bin
The last command prints Signature Verified Successfully. Write the message with printf %s: echo adds a newline, which is a different message, and then OpenSSL prints Signature Verification Failure. A text message is signed as UTF-8 exactly as typed; for anything else, open the file. Use a test key (the Key Generator makes one): real secrets do not belong in any web page.
How do I verify a signature with a public key, in OpenSSL, Node or a JWT?
Open Verify, paste the public key, the message and the signature (Base64, Base64url, hex or a binary .sig file). The result updates as you type. If it is not valid, the page checks the usual mistakes (another hash, the other RSA padding, a message that differs only in line ends) and says which one fits.
For RSA, OpenSSL takes the padding as options, and Node's crypto does the same:
openssl dgst -sha256 -sign private.pem -out sig.bin message.txt openssl dgst -sha256 -verify public.pem -signature sig.bin message.txt openssl dgst -sha256 -sign private.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -out sig.bin message.txt openssl dgst -sha256 -verify public.pem -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -signature sig.bin message.txt
import { sign, verify, constants } from "node:crypto";
const opts = { key: privatePem, padding: constants.RSA_PKCS1_PSS_PADDING, saltLength: 32 };
const sig = sign("sha256", Buffer.from("hello"), opts);
verify("sha256", Buffer.from("hello"), { ...opts, key: publicPem }, sig); // true
A JWT is a signature too. The message is the first two parts joined by a dot (header.payload) and the signature is the third part in Base64url. RS256 is RSA PKCS#1 v1.5 with SHA-256, PS256 is RSA-PSS with SHA-256 and a 32-byte salt, ES256 is ECDSA P-256 with SHA-256 in raw form, and EdDSA is Ed25519. The JWT decoder shows the header and claims; a valid signature here says the key signed it, not that the claims are true.
Why does a signature fail to verify? RSA-PSS salt, ECDSA raw and DER
A signature is valid only for the same key, the same message bytes and the same settings. The usual causes: a final newline or CRLF line ends in one copy of the message, a different hash, PKCS#1 v1.5 against PSS, or a different PSS salt length. OpenSSL signs with a salt as long as the hash, Node with the largest that fits, and WebCrypto needs the number; with Detect the page reads it from the signature.
ECDSA has two forms. WebCrypto, JWT and JWS use raw r||s, always 64 bytes for P-256, 96 for P-384 and 132 for P-521. OpenSSL, Java and X.509 use DER, about 70 to 72 bytes for P-256. Pick Raw or DER when signing; when verifying, Detect tells them apart. OpenSSL can only read the DER form.
Ed25519 needs a browser with WebCrypto support (Chrome 137, Firefox 129 or Safari 17 or newer); RSA and ECDSA work everywhere. A file is read into memory to be signed, so the limit is 64 MB: for something bigger, sign its checksum from the hash generator. Keys are read in this tab and are not sent or stored.
Related tools
- Key InspectorRead a PEM, JWK or SSH key, see its fingerprints, convert it and check a key pair.
- Key GeneratorGenerate an SSH, RSA, ECDSA or Ed25519 key pair and download it as OpenSSH, PEM or JWK.
- JWT DecoderRead the header, payload and expiry of a JSON Web Token.
- Hash & HMACMD5, SHA-1, SHA-256, SHA-384, SHA-512 and HMAC for text and files.
- Certificate DecoderDecode an X.509 certificate, a chain or a CSR: names, SANs, expiry, public key and fingerprints.
- CSR GeneratorCreate a certificate signing request (PKCS#10) with a new or existing key: names, subject, RSA, ECDSA, Ed25519, and the openssl command.