Loading lesson path
Concept visual
Start from A
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 the crypto module const crypto = require('crypto');
// Create a Verify object const verify = crypto.createVerify('RSA-SHA256');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.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);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));
});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);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);
});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'
});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
}));
});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();When verifying digital signatures, consider these security best practices:
: Validate the source of the public key used for verification. Don't trust a public key unless it comes from a trusted source.: When using certificates, verify the entire certificate chain and check certificate revocation status.
: Ensure the verification algorithm matches the signing algorithm, including any options like padding or salt length.
: Validate and sanitize any data before verification to prevent injection attacks.
: Always default to rejecting signatures that fail verification for any reason.
: Complexity increases the risk of verification bypass vulnerabilities.
: Signature verification may be vulnerable to timing attacks in some implementations.
: Verifying the authenticity of updates before installation.
: Ensuring digitally signed documents haven't been modified.
: Verifying the identity of API requests.
: Verifying JSON Web Token signatures.