Node.js Verify Reference
Verify Object
The Verify class is part of Node.js's crypto
module.
It provides a way to verify cryptographic digital signatures.
Verify instances are created using the crypto.createVerify()
method.
Verify is used in conjunction with the Sign class to validate that a message was signed by a known sender and was not altered in transit.
Import Crypto Module
// Import the crypto module
const crypto = require('crypto');
// Create a Verify object
const verify = crypto.createVerify('RSA-SHA256');
Run example »
Verify Methods
Method | Description |
---|---|
verify.update(data[, inputEncoding]) | Updates the Verify content with the given data . If inputEncoding is provided, data is a string using the specified encoding; otherwise, data is a Buffer, TypedArray, or DataView. This method can be called multiple times with new data. |
verify.verify(object, signature[, signatureEncoding]) | Verifies the provided data using the given object and signature . object is a string containing a PEM-encoded public key, a KeyObject of type 'public', or an X.509 certificate. If signatureEncoding is provided, signature is a string using the specified encoding; otherwise it is a Buffer, TypedArray, or DataView. Returns true if the signature is valid, false otherwise. |
Basic Verify Example
The following example demonstrates how to verify a digital signature of a message:
const crypto = require('crypto');
const fs = require('fs');
// Load the message, signature, and public key
// In a real application, these would typically come from files or network
// For this example, we'll try to load from the files created in the Sign example
let message, signature, publicKey;
try {
message = fs.readFileSync('message.txt', 'utf8');
signature = fs.readFileSync('signature.hex', 'utf8');
publicKey = fs.readFileSync('public_key.pem', 'utf8');
} catch (error) {
// If files don't exist, create example data
const { privateKey, publicKey: newPublicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
message = 'This is a message to be verified';
publicKey = newPublicKey;
// Create a signature for the example
const sign = crypto.createSign('SHA256');
sign.update(message);
signature = sign.sign(privateKey, 'hex');
}
// Create a Verify object
const verify = crypto.createVerify('SHA256');
// Update with the message
verify.update(message);
// Verify the signature with the public key
const isValid = verify.verify(publicKey, signature, 'hex');
console.log('Message:', message);
console.log('Signature:', signature);
console.log('Is signature valid?', isValid);
Run example »
Verifying with Different Algorithms
The Verify class supports various signature algorithms:
const crypto = require('crypto');
// Generate key pairs for different algorithms
function generateRSAKeyPair() {
return crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
}
function generateECKeyPair() {
return crypto.generateKeyPairSync('ec', {
namedCurve: 'prime256v1',
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'sec1',
format: 'pem'
}
});
}
// Generate different key pairs
const rsaKeys = generateRSAKeyPair();
const ecKeys = generateECKeyPair();
// Message to sign and verify
const message = 'Message to verify with different algorithms';
// Function to sign and verify with a specific algorithm
function testSignatureVerification(algorithm, privateKey, publicKey, message) {
try {
// Sign the message
const sign = crypto.createSign(algorithm);
sign.update(message);
const signature = sign.sign(privateKey, 'hex');
// Verify the signature
const verify = crypto.createVerify(algorithm);
verify.update(message);
const isValid = verify.verify(publicKey, signature, 'hex');
// Try to verify with a tampered message
const tamperedVerify = crypto.createVerify(algorithm);
tamperedVerify.update(message + ' (tampered)');
const isTamperedValid = tamperedVerify.verify(publicKey, signature, 'hex');
return {
algorithm,
isValid,
isTamperedValid
};
} catch (error) {
return {
algorithm,
error: error.message
};
}
}
// Test various signature algorithms
console.log(`Message: "${message}"`);
console.log('-----------------------------------------------');
// RSA signatures with different hash algorithms
console.log('RSA Signatures:');
['SHA256', 'SHA384', 'SHA512'].forEach(hash => {
console.log(testSignatureVerification(hash, rsaKeys.privateKey, rsaKeys.publicKey, message));
});
console.log('-----------------------------------------------');
// ECDSA signatures
console.log('ECDSA Signatures:');
['SHA256', 'SHA384'].forEach(hash => {
console.log(testSignatureVerification(hash, ecKeys.privateKey, ecKeys.publicKey, message));
});
Run example »
Verifying with Multiple Updates
You can update a Verify object with multiple pieces of data:
const crypto = require('crypto');
// Generate a keypair
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Create a signature with multiple updates
const sign = crypto.createSign('SHA256');
sign.update('First part of the message. ');
sign.update('Second part of the message. ');
sign.update('Third part of the message.');
const signature = sign.sign(privateKey, 'hex');
console.log('Signature created with multiple updates');
// Create a Verify object
const verify = crypto.createVerify('SHA256');
// Verify with multiple updates matching the original
verify.update('First part of the message. ');
verify.update('Second part of the message. ');
verify.update('Third part of the message.');
const isValidMultiple = verify.verify(publicKey, signature, 'hex');
console.log('Verification with matching multiple updates:', isValidMultiple);
// Verify with a single update containing the same data
const verifySingle = crypto.createVerify('SHA256');
verifySingle.update('First part of the message. Second part of the message. Third part of the message.');
const isValidSingle = verifySingle.verify(publicKey, signature, 'hex');
console.log('Verification with single update of same data:', isValidSingle);
// Try to verify with different updates
const verifyDifferent = crypto.createVerify('SHA256');
verifyDifferent.update('First part of the message. ');
verifyDifferent.update('Modified second part. ');
verifyDifferent.update('Third part of the message.');
const isValidDifferent = verifyDifferent.verify(publicKey, signature, 'hex');
console.log('Verification with different updates:', isValidDifferent);
Run example »
Verifying File Signatures
This example demonstrates verifying a digital signature for a file:
const crypto = require('crypto');
const fs = require('fs');
// Function to verify a file's signature
function verifyFile(filePath, signaturePath, publicKey, algorithm = 'SHA256') {
return new Promise((resolve, reject) => {
try {
// Read the signature
const signature = fs.readFileSync(signaturePath, 'utf8');
// Create Verify object
const verify = crypto.createVerify(algorithm);
// Create read stream for the file
const readStream = fs.createReadStream(filePath);
// Handle stream events
readStream.on('data', (data) => {
verify.update(data);
});
readStream.on('end', () => {
// Verify the signature
const isValid = verify.verify(publicKey, signature, 'hex');
resolve(isValid);
});
readStream.on('error', (error) => {
reject(error);
});
} catch (error) {
reject(error);
}
});
}
// For this example, create a file, sign it, and verify it
const filePath = 'example_to_verify.txt';
const signaturePath = `${filePath}.sig`;
const publicKeyPath = 'verify_public_key.pem';
// Create a test environment if files don't exist
if (!fs.existsSync(filePath) || !fs.existsSync(signaturePath) || !fs.existsSync(publicKeyPath)) {
console.log('Creating test environment...');
// Generate a keypair
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Save the public key
fs.writeFileSync(publicKeyPath, publicKey);
// Create a test file
fs.writeFileSync(filePath, 'This is a test file for signature verification.\n'.repeat(100));
// Sign the file
const sign = crypto.createSign('SHA256');
const fileContent = fs.readFileSync(filePath);
sign.update(fileContent);
const signature = sign.sign(privateKey, 'hex');
// Save the signature
fs.writeFileSync(signaturePath, signature);
console.log('Test environment created');
}
// Load the public key
const publicKey = fs.readFileSync(publicKeyPath, 'utf8');
// Verify the file signature
verifyFile(filePath, signaturePath, publicKey)
.then(isValid => {
console.log(`File: ${filePath}`);
console.log(`Signature: ${signaturePath}`);
console.log(`Verification result: ${isValid ? 'Valid signature' : 'Invalid signature'}`);
// Demonstrate a tampered file
if (isValid) {
const tamperedFilePath = `${filePath}.tampered`;
fs.copyFileSync(filePath, tamperedFilePath);
// Make a small change to the file
const content = fs.readFileSync(tamperedFilePath, 'utf8');
fs.writeFileSync(tamperedFilePath, content.replace('verification', 'TAMPERED'));
// Verify the tampered file with the original signature
return verifyFile(tamperedFilePath, signaturePath, publicKey)
.then(isTamperedValid => {
console.log(`\nTampered file: ${tamperedFilePath}`);
console.log(`Verification result: ${isTamperedValid ? 'Valid signature (unexpected!)' : 'Invalid signature (expected)'}`);
});
}
})
.catch(error => {
console.error('Error verifying file:', error.message);
});
Run example »
Verifying with Different Key Types
The Verify class can work with different formats of public keys:
const crypto = require('crypto');
const fs = require('fs');
// Message to sign and verify
const message = 'Message to verify with different key formats';
// Function to sign and verify with different key formats
function verifyWithKeyFormat(publicKey, keyFormat, algorithm = 'SHA256') {
try {
// Generate keypair for test
const { privateKey, publicKey: generatedPublicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Sign the message with private key
const sign = crypto.createSign(algorithm);
sign.update(message);
const signature = sign.sign(privateKey, 'hex');
// Verify with the provided public key format
const verify = crypto.createVerify(algorithm);
verify.update(message);
return {
format: keyFormat,
isValid: verify.verify(publicKey, signature, 'hex')
};
} catch (error) {
return {
format: keyFormat,
error: error.message
};
}
}
// Generate an RSA key pair
const { privateKey, publicKey: pemPublicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Sign the message for verification tests
const sign = crypto.createSign('SHA256');
sign.update(message);
const signature = sign.sign(privateKey, 'hex');
// Function to verify with different key formats
function testVerifyWithKey(publicKey, keyFormat) {
try {
const verify = crypto.createVerify('SHA256');
verify.update(message);
return {
format: keyFormat,
isValid: verify.verify(publicKey, signature, 'hex')
};
} catch (error) {
return {
format: keyFormat,
error: error.message
};
}
}
console.log(`Message: "${message}"`);
console.log('Signature:', signature.substring(0, 32) + '...');
// 1. Verify with PEM-encoded public key (string)
console.log('\n1. PEM-encoded public key (string):');
console.log(testVerifyWithKey(pemPublicKey, 'PEM string'));
// 2. Verify with PEM-encoded public key (buffer)
console.log('\n2. PEM-encoded public key (buffer):');
console.log(testVerifyWithKey(Buffer.from(pemPublicKey), 'PEM buffer'));
// 3. Verify with KeyObject
console.log('\n3. KeyObject:');
const keyObject = crypto.createPublicKey(pemPublicKey);
console.log(testVerifyWithKey(keyObject, 'KeyObject'));
// 4. Try to verify with X.509 certificate
console.log('\n4. X.509 Certificate (simulated):');
console.log({
format: 'X.509 Certificate',
note: 'In a real scenario, you would load an X.509 certificate containing the public key'
});
// 5. Try to verify with JWK (requires conversion)
console.log('\n5. JWK (requires conversion):');
console.log({
format: 'JWK',
note: 'JWK needs to be converted to PEM or KeyObject first'
});
Run example »
Verifying with Advanced Options
Verifying signatures with specific OpenSSL options:
const crypto = require('crypto');
// Generate RSA key pair
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// Message to sign
const message = 'Message to verify with different options';
// Function to sign with specific options
function signWithOptions(algorithm, message, privateKey, options = {}) {
// Create private key with options
const keyWithOptions = {
key: privateKey,
...options
};
// Sign the message
const sign = crypto.createSign(algorithm);
sign.update(message);
return sign.sign(keyWithOptions, 'hex');
}
// Function to verify with specific options
function verifyWithOptions(algorithm, message, publicKey, signature, options = {}) {
try {
// Create public key with options
const keyWithOptions = {
key: publicKey,
...options
};
// Verify the signature
const verify = crypto.createVerify(algorithm);
verify.update(message);
return verify.verify(keyWithOptions, signature, 'hex');
} catch (error) {
return `Error: ${error.message}`;
}
}
console.log(`Message: "${message}"`);
// 1. Sign and verify with standard PKCS#1 v1.5 padding (default)
const sig1 = signWithOptions('SHA256', message, privateKey);
console.log('\n1. Standard PKCS#1 v1.5 padding:');
console.log('Signature:', sig1.substring(0, 32) + '...');
console.log('Verification result:', verifyWithOptions('SHA256', message, publicKey, sig1));
// 2. Sign and verify with PSS padding
const pssOptions = {
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: 32
};
const sig2 = signWithOptions('SHA256', message, privateKey, pssOptions);
console.log('\n2. PSS padding:');
console.log('Signature:', sig2.substring(0, 32) + '...');
console.log('Verification result (matching options):',
verifyWithOptions('SHA256', message, publicKey, sig2, pssOptions));
console.log('Verification result (default options):',
verifyWithOptions('SHA256', message, publicKey, sig2));
// 3. Verify with PSS padding and different salt lengths
console.log('\n3. PSS padding with different salt lengths:');
[20, 32, 48].forEach(saltLength => {
const sigSalt = signWithOptions('SHA256', message, privateKey, {
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength
});
console.log(`Salt length ${saltLength}:`);
// Try to verify with correct salt length
console.log(` - Verify with correct salt length (${saltLength}):`,
verifyWithOptions('SHA256', message, publicKey, sigSalt, {
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength
}));
// Try to verify with wrong salt length
const wrongSaltLength = saltLength + 10;
console.log(` - Verify with wrong salt length (${wrongSaltLength}):`,
verifyWithOptions('SHA256', message, publicKey, sigSalt, {
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: wrongSaltLength
}));
});
Run example »
Certificate-Based Verification
Verifying signatures using X.509 certificates:
const crypto = require('crypto');
const fs = require('fs');
// Function to simulate a certificate-based verification
function demonstrateCertificateVerification() {
console.log('Certificate-Based Verification Demonstration');
console.log('-------------------------------------------');
console.log('In a real application, you would:');
console.log('1. Obtain an X.509 certificate containing the signer\'s public key');
console.log('2. Verify the certificate\'s trust chain');
console.log('3. Extract the public key from the certificate');
console.log('4. Use that public key to verify the signature');
console.log('\nSimplified example:');
// Generate a key pair
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});
// In a real app, you'd have a certificate with the public key
const mockCertificate = `-----BEGIN CERTIFICATE-----
(This would be a real X.509 certificate containing the public key)
-----END CERTIFICATE-----`;
// Message to sign
const message = 'Message signed with a certificate-backed key';
// Sign the message
const sign = crypto.createSign('SHA256');
sign.update(message);
const signature = sign.sign(privateKey, 'hex');
console.log(`Message: "${message}"`);
console.log(`Signature: ${signature.substring(0, 32)}...`);
console.log('\nVerification steps:');
console.log('1. Extract public key from certificate (simulated)');
// In a real scenario, you'd extract the public key from the certificate
// For this example, we'll use our generated public key directly
console.log('2. Verify the signature using the extracted public key');
const verify = crypto.createVerify('SHA256');
verify.update(message);
const isValid = verify.verify(publicKey, signature, 'hex');
console.log(`Verification result: ${isValid ? 'Valid signature' : 'Invalid signature'}`);
}
// Run the demonstration
demonstrateCertificateVerification();
Run example »
Security Best Practices
When verifying digital signatures, consider these security best practices:
- Trust management: Validate the source of the public key used for verification. Don't trust a public key unless it comes from a trusted source.
- Certificate validation: When using certificates, verify the entire certificate chain and check certificate revocation status.
- Matching signing algorithm: Ensure the verification algorithm matches the signing algorithm, including any options like padding or salt length.
- Input validation: Validate and sanitize any data before verification to prevent injection attacks.
- Fail securely: Always default to rejecting signatures that fail verification for any reason.
- Keep verification code simple: Complexity increases the risk of verification bypass vulnerabilities.
- Consider timing attacks: Signature verification may be vulnerable to timing attacks in some implementations.
Common Use Cases for Signature Verification
- Software Updates: Verifying the authenticity of updates before installation.
- Document Verification: Ensuring digitally signed documents haven't been modified.
- API Authentication: Verifying the identity of API requests.
- JWT Validation: Verifying JSON Web Token signatures.
- Certificate Chain Validation: Verifying signatures in a certificate chain.
- Secure Communication: Authenticating messages in secure protocols.