Skip to content

Failed to generate a correct signature #484

@hsan8

Description

@hsan8

Can someone help me to generate same signature like this in nodejs using xml-crypto:
I have two keys :

  • privatekey.pem
  • publickey.pem
<Req
    xmlns="http://www.abc.com/abc/schema/">
    <Header requestId="f359718a-b759-4617-aebb-1260d98fef3e" timestamp="2025-01-19 15:26:28.552" ver="1.0"/>
    <Signature
        xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
            <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <Reference URI="">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <DigestValue>RX4gMUYcjhYtNPANxPJ2aCmxXzc=</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>fptL80Jl6614.....</SignatureValue>
        <KeyInfo>
            <KeyValue>
                <RSAKeyValue>
                    <Modulus>hPN+z6tI/WSPV1sKDDfw2.....</Modulus>
                    <Exponent>AQAB</Exponent>
                </RSAKeyValue>
            </KeyValue>
        </KeyInfo>
    </Signature>
</Req>

This is my try in js

const crypto = require('crypto');
const SignedXml = require('xml-crypto').SignedXml;
const fs = require('fs').promises;

class XmlSigner {
  constructor() {
    this.XML_TEMPLATE = `<Req
    xmlns="http://www.abc.com/abc/schema/">
    <Header requestId="f359718a-b759-4617-aebb-1260d98fef3e" timestamp="2025-01-19 15:26:28.552" ver="1.0"/></Req>`;
  }

  async loadPrivateKey(pemContent) {
    return pemContent.replace('-----BEGIN PRIVATE KEY-----', '').replace('-----END PRIVATE KEY-----', '').replace(/\s/g, '');
  }

  async loadPublicKey(pemContent) {
    return pemContent.replace('-----BEGIN PUBLIC KEY-----', '').replace('-----END PUBLIC KEY-----', '').replace(/\s/g, '');
  }

  extractModulusAndExponent(publicKeyPem) {
    // Create a public key object
    const publicKey = crypto.createPublicKey({
      key: `-----BEGIN PUBLIC KEY-----\n${publicKeyPem}\n-----END PUBLIC KEY-----`,
      format: 'pem'
    });

    // Export as DER format
    const derKey = publicKey.export({ type: 'spki', format: 'der' });

    // Parse the DER structure to extract modulus and exponent
    // Skip the ASN.1 header to get to the key parts
    let offset = 24; // Typical offset for RSA public key in SPKI format

    // Read modulus length
    const modulusLength = derKey[offset];
    offset++;

    // Extract modulus
    const modulus = derKey.slice(offset, offset + modulusLength);
    offset += modulusLength;

    // Skip header byte
    offset++;

    // Read exponent length
    const exponentLength = derKey[offset];
    offset++;

    // Extract exponent
    const exponent = derKey.slice(offset, offset + exponentLength);

    return {
      modulus: modulus.toString('base64'),
      exponent: exponent.toString('base64')
    };
  }

  async signXml(privateKeyPem, publicKeyPem) {
    const { modulus, exponent } = this.extractModulusAndExponent(publicKeyPem);

    const sig = new SignedXml();

    sig.addReference({
      xpath: "/*",
      digestAlgorithm: 'http://www.w3.org/2000/09/xmldsig#sha1',
      transforms: ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'],
      isEmptyUri: false,
    });

    sig.canonicalizationAlgorithm = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
    sig.signatureAlgorithm = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';

    sig.privateKey = Buffer.from(privateKeyPem, 'base64');
    sig.keyInfo = `<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><KeyValue><RSAKeyValue><Modulus>${modulus}</Modulus><Exponent>${exponent}</Exponent></RSAKeyValue></KeyValue></KeyInfo>`;

    console.log(this.XML_TEMPLATE);

    sig.computeSignature(xml, signature);
    return sig.getSignedXml();
  }
}

async function main() {
  try {
    const signer = new XmlSigner();

    // Load keys from files
    const privateKeyPem = await fs.readFile('private_key.pem', 'utf8');
    const publicKeyPem = await fs.readFile('public_key.pem', 'utf8');

    // Load and process the keys
    const privateKey = await signer.loadPrivateKey(privateKeyPem);

    const publicKey = await signer.loadPublicKey(publicKeyPem);

    // Sign the XML
    const signedXml = await signer.signXml(privateKey, publicKey);
    console.log(signedXml);
  } catch (error) {
    console.error('Error:', error);
    console.error('Stack:', error.stack);
  }
}

main();

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions