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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions