рджреГрд╢реНрдпрдкрдЯрд▓ рдореЗрдВ рдХрдЬрд╛рдХрд┐рд╕реНрддрд╛рди рдЧрдгрд░рд╛рдЬреНрдп рдХреА рдЗрд▓реЗрдХреНрдЯреНрд░реЙрдирд┐рдХ рд╕рд░рдХрд╛рд░ рдХреЗ рджрд╕реНрддрд╛рд╡реЗрдЬреЛрдВ рдореЗрдВ рдХреНрдпреВрдЖрд░ рдХреЛрдб рдХреА рд╕рд╛рдордЧреНрд░реА рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг

рдореИрдВ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реВрдБ рдХрд┐ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдореЗрдВ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЕрдзрд┐рдХрд╛рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЖрдк рдХрдЬрд╛рдЦрд╕реНрддрд╛рди рдЧрдгрд░рд╛рдЬреНрдп рдХреЗ рдЗрд▓реЗрдХреНрдЯреНрд░реЙрдирд┐рдХ рд╕рд░рдХрд╛рд░реА рдкреЛрд░реНрдЯрд▓реЛрдВ рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрдиреНрди рджрд╕реНрддрд╛рд╡реЗрдЬреЛрдВ рдореЗрдВ рдирд┐рд╣рд┐рдд рдХреНрдпреВрдЖрд░ рдХреЛрдб рд╕реЗ рдбреЗрдЯрд╛ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдФрд░ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, https://egov.kz )ред


рдЗрд▓реЗрдХреНрдЯреНрд░реЙрдирд┐рдХ рджрд╕реНрддрд╛рд╡реЗрдЬреЛрдВ рдореЗрдВ, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╢рдмреНрджрд╛рдВрдХрди рдореМрдЬреВрдж рд╣реИ:


* рдмрд╛рд░рдХреЛрдб рдореЗрдВ рдЬреАрдбреАрдмреА рдкреАрдПрдЪ рдХреА рд╕реВрдЪрдирд╛ рдкреНрд░рдгрд╛рд▓реА рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдбреЗрдЯрд╛ рд╣реЛрддрд╛ рд╣реИ рдФрд░ рд╕реНрдЯреЗрдЯ рдХреЙрд░рдкреЛрд░реЗрд╢рди рдХреА рд╢рд╛рдЦрд╛ тАЬрдирд╛рдЧрд░рд┐рдХ рдХреЗ рд▓рд┐рдП рд╕рд░рдХрд╛рд░тАЭ NAO рдХреЗ рдПрдХ рдЗрд▓реЗрдХреНрдЯреНрд░реЙрдирд┐рдХ рдбрд┐рдЬрд┐рдЯрд▓ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдХреЗ рд╕рд╛рде рд╣рд╕реНрддрд╛рдХреНрд╖рд░рд┐рдд рд╣реЛрддрд╛ рд╣реИред

рдЬрд╣рд╛рдБ рддрдХ рдореБрдЭреЗ рдкрддрд╛ рд╣реИ, рдХреНрдпреВрдЖрд░ рдХреЛрдб рдореЗрдВ рдбреЗрдЯрд╛ рдирд┐рдХрд╛рд▓рдиреЗ рдФрд░ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХреЗ рд▓рд┐рдП рдХреЛрдИ рддреИрдпрд╛рд░ рдЙрдкрдХрд░рдг рдирд╣реАрдВ рд╣реИрдВред


рдореЗрд░рд╛ рдЕрдВрддрд┐рдо рд▓рдХреНрд╖реНрдп рд╣рд╕реНрддрд╛рдХреНрд╖рд░рд┐рдд рдбреЗрдЯрд╛ рдФрд░ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдирд┐рдХрд╛рд▓рдирд╛ рд╣реИ, рд╣рд╕реНрддрд╛рдХреНрд╖рд░рд┐рдд рдбреЗрдЯрд╛ рдХреА рдЕрдЦрдВрдбрддрд╛ рдХреЛ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рдирд╛ред рдЗрд╕ рдиреЛрдЯ рдореЗрдВ рдХреЗрд╡рд▓ рдПрдХ рд╣реИрд╢ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рддреЗ рд╣реБрдП, рдбрд┐рдЬрд┐рдЯрд▓ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдХреЛ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреА рдХреЛрдИ рдмрд╛рдд рдирд╣реАрдВ рд╣реЛрдЧреАред рдпрджрд┐ рдЗрд╕ рд╡рд┐рд╖рдп рдореЗрдВ рдЬрдирддрд╛ рд░реБрдЪрд┐ рджрд┐рдЦрд╛рддреА рд╣реИ, рддреЛ рднрд╡рд┐рд╖реНрдп рдореЗрдВ рдбрд┐рдЬрд┐рдЯрд▓ рд╣рд╕реНрддрд╛рдХреНрд╖рд░реЛрдВ рдХреЗ рд╕рддреНрдпрд╛рдкрди рдХрд╛ рд╡рд┐рд╡рд░рдг рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред


рдорд╣рддреНрд╡рдкреВрд░реНрдг: рдпрд╣ рдиреЛрдЯ рд╣реИрдХрд┐рдВрдЧ рддрдХрдиреАрдХреЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдбреЗрдЯрд╛ рддрдХ рдЕрдирдзрд┐рдХреГрдд рдкрд╣реБрдВрдЪ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ; рд╣рдо рдбреЗрдЯрд╛ рдХреЛ рдПрдХ рджреГрд╢реНрдп рд╕реЗ рджреВрд╕рд░реЗ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗред


рдореИрдВ рджрд┐рдЦрд╛рдКрдВрдЧрд╛ рдХрд┐ рдореВрд▓ рдкреАрдбреАрдПрдл рдлрд╛рдЗрд▓реЛрдВ рдХреЛ рдХреИрд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬреЛ рдХрдЬрд╛рдХрд┐рд╕реНрддрд╛рди рдЧрдгрд░рд╛рдЬреНрдп рдХреА рдЗрд▓реЗрдХреНрдЯреНрд░реЙрдирд┐рдХ рд╕рд░рдХрд╛рд░ рдХреЗ рдбрд╛рдЙрдирд▓реЛрдб рдкреЛрд░реНрдЯрд▓реНрд╕ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред рдЗрди рдкреАрдбреАрдПрдл рдлрд╛рдЗрд▓реЛрдВ рдореЗрдВ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдПрдореНрдмреЗрдбреЗрдб рдЫрд╡рд┐рдпреЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рдХреНрдпреВрдЖрд░ рдХреЛрдб рд╣реЛрддреЗ рд╣реИрдВред


рдореИрдВ рдмрд┐рдирд╛ рдХрд┐рд╕реА рдЖрдкрд░рд╛рдзрд┐рдХ рд░рд┐рдХреЙрд░реНрдб рдХреЗ рдкреНрд░рдорд╛рдг рдкрддреНрд░ рдкрд░ рдкреНрд░рдпреЛрдЧ рдХрд░реВрдВрдЧрд╛ред


рдореИрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реВрдВрдЧрд╛:


  • PDF.js рдПрдХ рдкреАрдбреАрдПрдл рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рд╕реЗ рдЫрд╡рд┐рдпреЛрдВ рдХреЛ рдирд┐рдХрд╛рд▓рдиреЗ рдХреЗ рд▓рд┐рдП;
  • QR рдХреЛрдб рдбрд┐рдХреЛрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП jsQR ;
  • рдЬрд╝рд┐рдк рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рдЕрдирдкреИрдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП JSZip ;
  • XML рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд▓рд┐рдП XMLDSIGjs ;
  • WebCrypto GOST (gostCrypto) рдХрдВрдкреНрдпреВрдЯрд┐рдВрдЧ рд╣реИрд╢ рдФрд░ рдПрдиреНрдХреЛрдбрд┐рдВрдЧ / рдбрд┐рдХреЛрдбрд┐рдВрдЧ рдбреЗрдЯрд╛ рдХреЗ рд▓рд┐рдПред

0. ArrayBuffer рдореЗрдВ рдкреАрдбреАрдПрдл рдлрд╛рдЗрд▓ рдкрдврд╝реЗрдВ


рдЯреИрдЧ <input type="file">рдФрд░ рдЗрд╕рдХреА рд╡рд┐рд╢реЗрд╖рддрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдорд╛рдирдХ HTML рдЯреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдкреАрдбреАрдПрдл рдлрд╛рдЗрд▓ рддрдХ рдкрд╣реБрдВрдЪ рд╕рдВрднрд╡ рд╣реИ filesред


рдЖрдзреБрдирд┐рдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░реЛрдВ рдореЗрдВ, рдЖрдк рдПрдХ рдлрд╝рд╛рдЗрд▓ рдХреА рд╕рд╛рдордЧреНрд░реА ArrayBufferрдЗрд╕ рдкреНрд░рдХрд╛рд░ рдкреНрд░рд╛рдкреНрдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ :


const fileContents = await fileInput.files[0].arrayBuffer();

1. рдПрдХ рдкреАрдбреАрдПрдл рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рд╕реЗ рдЪрд┐рддреНрд░ рдирд┐рдХрд╛рд▓реЗрдВ


PDF.js , https://mozilla.imtqy.com/pdf.js/examples/index.html#interactive-examples


const pdfjsLib = window['pdfjs-dist/build/pdf'];
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.js';

PDF.js . , :


const ops = [
  pdfjsLib.OPS.paintJpegXObject,
  pdfjsLib.OPS.paintImageXObject,
];

PDF :


const loadingTask = pdfjsLib.getDocument(fileContents);
const pdf = await loadingTask.promise;

const objIDs = [];
const images = [];

await (async function () {
  for (let pageIndex = 1; pageIndex <= pdf.numPages; pageIndex += 1) {
    const page = await pdf.getPage(pageIndex);

    //    ,   .
    const operators = await page.getOperatorList();
    for (let i = 0; i < operators.fnArray.length; i++) {
      const fn = operators.fnArray[i];

      if (ops.indexOf(fn) !== -1) {
        //       ,   -  .
        const objID = operators.argsArray[i][0];

        //          ,   .
        if (objIDs.indexOf(objID) === -1) {
          objIDs.push(objID);

          //       .
          try {
            const imageInfo = page.objs.get(objID);
            images.push(imageInfo);
          } catch (err) {
            console.log(err);
          }
        }
      }
    }
  }
})()

2. QR


jsQR RGBA PDF RGB, RGB RGBA:


function extractRGBAData(image) {
  if (image.kind === 3) { // ImageKind.RGBA_32BPP  https://github.com/mozilla/pdf.js/blob/master/src/shared/util.js
    return image.data;
  }

  if (image.kind !== 2) { // ImageKind.RGB_24BPP  https://github.com/mozilla/pdf.js/blob/master/src/shared/util.js
    throw new Error(`Image kind "${image.kind}" is not supported.`);
  }

  const data = new Uint8ClampedArray(image.width * image.height * 4);

  let destPosition = 0;
  for (let srcPosition = 0; srcPosition < image.data.length;) {
    data[destPosition++] = image.data[srcPosition++];
    data[destPosition++] = image.data[srcPosition++];
    data[destPosition++] = image.data[srcPosition++];
    data[destPosition++] = 255;
  }

  return data;
}

:


const qrCodes = [];
images.forEach((image) => {
  if (image.data) {
    const data = extractRGBAData(image);

    try {
      const code = jsQR(data, image.width, image.height);
      console.log(code);
      qrCodes.push(code);
    } catch (err) {
      console.log(err);
    }
  }
});

7 тАФ QR . URL тАФ QR , . 6 XML ( ):


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<BarcodeElement xmlns="http://barcodes.pdf.shep.nitec.kz/">
  <creationDate>...</creationDate>
  <elementData>...</elementData>
  <elementNumber>1</elementNumber>
  <elementsAmount>6</elementsAmount>
  <FavorID>...</FavorID>
</BarcodeElement>

:


  • <elementData>...</elementData> тАФ
  • <elementNumber>1</elementNumber> тАФ
  • <elementsAmount>6</elementsAmount> тАФ

3.


:


const qrCodesBlocks = [];

function addQRCodeBlock(code) {
  if (!code || !code.data) {
    return;
  }

  //    .
  const elementsAmountRegexp = /<elementsAmount>((.|\r|\n)+?)<\/elementsAmount>/;
  const elementsAmountResult = elementsAmountRegexp.exec(code.data);
  if (!elementsAmountResult || elementsAmountResult.length <= 2) {
    return;
  }
  const elementsAmount = +elementsAmountResult[1];
  if (!Number.isSafeInteger(elementsAmount)) {
    throw new Error('        <elementsAmount>');
  }

  //       .
  if (qrCodesBlocks.length === 0) {
    for (let i = 0; i < elementsAmount; i++) {
      qrCodesBlocks.push('');
    }
  } else {
    if (qrCodesBlocks.length !== elementsAmount) {
      throw new Error(`  QR      QR : "${qrCodesBlocks.length}"  "${elementsAmount}"`);
    }
  }

  //   .
  const elementNumberRegexp = /<elementNumber>((.|\r|\n)+?)<\/elementNumber>/;
  const elementNumberResult = elementNumberRegexp.exec(code.data);
  if (!elementNumberResult || elementNumberResult.length < 2) {
    throw new Error(` QR   "<elementNumber>"`);
  }
  const elementNumber = +elementNumberResult[1];
  if (!Number.isSafeInteger(elementNumber)) {
    throw new Error(`"<elementNumber>"  QR    `);
  }

  //    .
  if (elementNumber > elementsAmount) {
    throw new Error(` QR  "${elementNumber}"    QR  "${elementsAmount}"`);
  }

  if (qrCodesBlocks[elementNumber - 1] !== '') {
    throw new Error(` QR  "${elementNumber}"    `);
  }

  //     .
  const elementDataRegexp = /<elementData>((.|\r|\n)+?)<\/elementData>/;
  const elementDataResult = elementDataRegexp.exec(code.data);
  if (!elementDataResult || elementDataResult.length < 2) {
    throw new Error(' QR   "<elementData>"');
  }
  const elementData = elementDataResult[1];

  qrCodesBlocks[elementNumber - 1] = elementData;
}

.


qrCodes.forEach(addQRCodeBlock);

if (qrCodesBlocks.length === 0) {
  throw new Error('     QR :     QR    ');
}

const foundBlocks = qrCodesBlocks.filter((block) => !!block);
if (qrCodesBlocks.length !== foundBlocks.length) {
  throw new Error('     QR :      QR ');
}

4.


ZIP Base64.


Base64:


const zippedParts = qrCodesBlocks.map(block => new Uint8Array(gostCrypto.coding.Base64.decode(block)));

:


const totalLength = zippedParts.reduce((accumulator, part) => accumulator + part.length, 0);

const zippedData = new Uint8Array(totalLength);
let zippedDataIndex = 0;
zippedParts.forEach((part) => {
  zippedData.set(part, zippedDataIndex);
  zippedDataIndex += part.length;
});

:


const zip = await JSZip.loadAsync(zippedData, { checkCRC32: true });

one, :


const file = zip.file('one');
if (!file) {
  throw new Error('     "one"');
}

const recoveredContents = await file.async("string");

5.


тАФ XML ( ):


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p1001Response>
    <SystemInfo>
        <messageId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
        <chainId>...</chainId>
        <messageDate>...</messageDate>
        `1`
        <responseInfoRu> </responseInfoRu>
        <responseInfoKz> </responseInfoKz>
        <digiSign>...</digiSign>
    </SystemInfo>
    <ResponseData>
        <ResponseType>UNJUDGED</ResponseType>
        <Person>
            <IIN>...</IIN>
            <SurName>...</SurName>
            <Name>...</Name>
            <MiddleName>...</MiddleName>
            <BirthDate>...</BirthDate>
            <BirthPlace>
                <Country>...</Country>
                <CountryKz>...</CountryKz>
                <District>...</District>
                <DistrictKz>...</DistrictKz>
                <City>...</City>
                <CityKz>...</CityKz>
                <Locality>...</Locality>
                <LocalityKz>...</LocalityKz>
            </BirthPlace>
        </Person>
        <Untried/>
        <CheckDate>...</CheckDate>
    </ResponseData>
</p1001Response>

digiSign тАФ XML Base64. , .


XML:


const regexp = /<digiSign>((.|\r|\n)+?)<\/digiSign>/;
const regexpResult = regexp.exec(recoveredContents);
if (!regexpResult && regexpResult.length !== 2) {
  throw new Error(' XML  "<digiSign>"');
}

const digiSignBytes = gostCrypto.coding.Base64.decode(regexpResult[1]);
const xmlDataAndSignature = gostCrypto.coding.Chars.encode(digiSignBytes, 'utf8');

6.


XML ( ):


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ResponseData>
  <ResponseType>UNJUDGED</ResponseType>
  <Person>
    <IIN>...</IIN>
    <SurName>...</SurName>
    <Name>...</Name>
    <MiddleName>...</MiddleName>
    <BirthDate>...</BirthDate>
    <BirthPlace>
      <Country>...</Country>
      <CountryKz>...</CountryKz>
      <District>...</District>
      <DistrictKz>...</DistrictKz>
      <City>...</City>
      <CityKz>...</CityKz>
      <Locality>...</Locality>
      <LocalityKz>...</LocalityKz>
    </BirthPlace>
  </Person>
  <Untried/>
  <CheckDate>...</CheckDate>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gost34310-gost34311"/>
      <ds:Reference URI="">
        <ds:Transforms>
          <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gost34311"/>
        <ds:DigestValue>...</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>...</ds:SignatureValue>
    <ds:KeyInfo>
      <ds:X509Data>
        <ds:X509Certificate>...</ds:X509Certificate>
      </ds:X509Data>
    </ds:KeyInfo>
  </ds:Signature>
</ResponseData>

XML , , <ResponseType>UNJUDGED</ResponseType>, <Person>...</Person>.


7.


XML .


XML :


const xml = XmlDSigJs.Parse(xmlDataAndSignature);

<ds:DigestValue>...</ds:DigestValue>:


const xmlSignatures = XmlDSigJs.Select(xml, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']");
if (xmlSignatures.length === 0) {
  throw new Error(`      ( "<Signature>"): "${xmlDataAndSignature}"`);
}
if (xmlSignatures.length > 1) {
  throw new Error(`       ( "<Signature>"): "${xmlDataAndSignature}"`);
}

const hashElementsInSignature = XmlDSigJs.Select(xmlSignatures[0], "//*[local-name(.)='DigestValue']");
if (hashElementsInSignature.length === 0) {
  throw new Error(` XML    ( "<DigestValue>"): "${xmlDataAndSignature}"`);
}
if (hashElementsInSignature.length > 1) {
  throw new Error(` XML     ( "<DigestValue>"): "${xmlDataAndSignature}"`);
}
const hashInSignature = hashElementsInSignature[0].textContent;

тАФ <ds:Transforms>...</ds:Transforms> XML :


const xmlDsigEnvelopedSignatureTransform = new XmlDSigJs.XmlDsigEnvelopedSignatureTransform();
xmlDsigEnvelopedSignatureTransform.LoadInnerXml(xml.documentElement);
xmlDsigEnvelopedSignatureTransform.GetOutput();

const xmlDsigC14NWithCommentsTransform = new XmlDSigJs.XmlDsigC14NWithCommentsTransform();
xmlDsigC14NWithCommentsTransform.LoadInnerXml(xml.documentElement);
const signedDataXML = xmlDsigC14NWithCommentsTransform.GetOutput();

const dataToHash = gostCrypto.coding.Chars.decode(signedDataXML, 'utf8');

"http://www.w3.org/2001/04/xmldsig-more#gost34311", 34.311-95 GOST R 34.11-94 gostCrypto. D-TEST.


:


const hashBytes = await gostCrypto.subtle.digest({name: 'GOST R 34.11-94', version: 1994, sBox: 'D-TEST'}, dataToHash);
const signedDataXMLHash = gostCrypto.coding.Base64.encode(hashBytes);

:


if (signedDataXMLHash !== hashInSignature) {
  throw new Error(`    XML  "${signedDataXMLHash}"      "${hashInSignature}"`);
}


, . , тАФ , XML .


, : XML , digiSign Base64, HTML , . .


:



All Articles