node.jssql-servercryptographyx509certificate

How to verify MSSQL signbycert in NodeJS


I'm not familiar with cryptography or the PKCS protocol, but what i need is to crypto.verify succesfully in NodeJS using singature generated in MSSQL signbycert.

by using this as reference: https://stackoverflow.com/a/77519242/19991312, with the following value:

DECLARE @PlainText as varchar(max) = 'Hello World';
DECLARE @Signature as varbinary(max); 
SET @Signature = SignByCert(Cert_ID('MyTestCertificate'), @PlainText);

SELECT @Signature as SIG, CERTENCODED(Cert_ID('MyTestCertificate')) as PBK;

plain text Hello World

Signature 01000502040000002B0DFBF8B0EB048B18BA30A7A9AC3C63C18D8F932DC974178C61F810758A3FB81F14247AA338E6DBABAE265A8D8077C689901E33B016379685F7A0D27429DCB78F438201F5C04827F2F2B816BF5EFD569FA2AD7EEECC69621AF5F7C39893F0001FC22EAA74DB0F5026CDB38F5BFF3A3D208196C75E7A7A9556050459C826194E460D4C5AB1F30ACFF8594E4E99BC8FD5C8675E79EC54AC9EB017FA112C1759BF387C248869C461677E1CF711860618BFCAC1332C879C01D88C0EBFD584BCF4B358D5D47618A7367DE3A1943AB18454D1B8C423437661120EEE2225A35F763002E9AC80B23B5F03F1EA4C16422BEA8C51032AA0C16B73C7AC5583496EDFD6AA21

public key 3082036B30820253A00302010202146472932AE654353AF660E02361C2062FC040FEE6300D06092A864886F70D01010B05003045310B30090603550406130241553113301106035504080C0A536F6D652D53746174653121301F060355040A0C18496E7465726E6574205769646769747320507479204C7464301E170D3233313132303137303835375A170D3234313131393137303835375A3045310B30090603550406130241553113301106035504080C0A536F6D652D53746174653121301F060355040A0C18496E7465726E6574205769646769747320507479204C746430820122300D06092A864886F70D01010105000382010F003082010A0282010100CF9CCF5AF133484FC7DC8FF41C1B9010FC7821B373D0352A57E76A0FA0456AE1F35FDCED60343551594FC0E636727AF868EC237978BBBAE8C366B955A507CB942A3380755F6565F1811BF4476948008F18F3BEBE852540C660A781B362DAD21E70567CE70363EB4DE0F281DB239381308C2A8664B066F7F3331038E62465FC837AF170346AB63D1D18CA0400A6FC41EEE87FEFFB80C01D2FF7B86A1E48F299FA4BECA495C8B33CCB3A9603731AC9CE6B6F55EA1EB91DD919DCCBB17833CB1FB67C97891AF9FAC34EFDB2E7777DF5DFD7E4172638ED65ECBAE08AB5E17EB7EA81937DAAA79F9B041BE3C05488423BB3EF1B26C91D91CF7FD9D7A42E8E5582DA5F0203010001A3533051301D0603551D0E041604145F7A25691BA68CF1D6B59AD50E8F2CA6D0403018301F0603551D230418301680145F7A25691BA68CF1D6B59AD50E8F2CA6D0403018300F0603551D130101FF040530030101FF300D06092A864886F70D01010B0500038201010064E1E3CB35A81CB136B599134D0F536A4D1776CE26B2822B33C415C8E65EE4A8A5E185668455DDF326A4219CAB87D12ED59E85BDA31521201998165F41979CA0DB6608AF2A5D5A3A7566082B6E7B1204666233312E0FFABFF427DDB8E37415559CD8B5405F13425078ACDC1C02AECD3DE372FDC9DE0DCE9A74F3716AF2D0D2ACD06F6070AB620A5E6A5575CA760AED728AAD8549B047345D4F286EA51CB005719B2396F5C0340FB94162EE9E7EAC247CE402169FC754EACAC818C08DAD0A796D336F4437D665CD7ABAB1ED9E30D6213ED2328859709884C35BE1143861429A84FA2CE530F95434554B33131AA66EC6ABC22091E91072242B466877ED6D82C137


Based on my rough understanding after reading this: https://stackoverflow.com/a/77519242/19991312, is that MSSQL signature is different from the standard PKCS protocol and cannot be directly used in crypto.verify

so i did some rough transformation on the signature value, but still could not verify the signature, possibly due to my misunderstand or lack of knowledge regarding cryptography. My current verification in nodeJS as follow:

const plainText    = 'Hello World';
const textDigest   = crypto.hash('sha256', plainText, 'hex');
const sqlSignature = '...';
const pbk          = '...';
const x509Cert     = new crypto.X509Certificate(Buffer.from(pbk, 'hex'))

const signatureTransform = Buffer.from(sqlSignature.slice(16), 'hex').reverse()

const isSignatureValid = crypto.verify(null, Buffer.from(textDigest, 'hex'), x509Cert.publicKey, signatureTransform)

console.log('isSignatureValid', isSignatureValid ) // false Q^Q

Solution

  • As already described in the linked post, signatureTransform is not compatible with the PKCS#1 v1.5 signature scheme (more precisely with the RSASSA-PKCS1-v1_5 signature scheme), because the applied encoding differs from EMSA-PKCS1-v1_5 in that only the hashed data and not the DER encoding of the DigestInfo value was used.
    For this reason, crypto.verify() must not be applied, as this checks whether the decrypted data strictly conforms to the EMSA-PKCS1-v1_5 encoding and, in particular, whether the hash portion matches the hashed message.
    Instead, crypto.publicDecrypt() should be used. This implementation performs a pure decryption with the public key and an unpadding of the static part (0x0001FF...FF00) without further checking of the resulting data, and therefore returns the message hash here.
    Thus, verification simply consists of checking that the hashes are identical:

    ...
    const decrypted = crypto.publicDecrypt(x509Cert.publicKey, signatureTransform).toString('hex')
    const verified = (decrypted === textDigest) 
    console.log("Verified:" , verified) // Verified: true