1С: Явное указание даты в запросе

Очень бы хотелось в запросе указывать явную дату как во многих SQL, что-то типа:

И ДатаЗапуска='2025.07.16 12:13:14'

Но,ой. К сожалению 1С так делать не позволяет. Поэтому чтобы указать дату явно, не параметром запроса, приходится городить такую конструкцию:

справа="ДатаВремя("+Формат(Год(справа),"ЧГ=0")+","+Месяц(справа)+","+День(справа)+")";	
....
sql=sql+"и ДатаЗапуска>"+справа

Javascript: удаление элемента массива

В JavaScript как мне кажется не очень удобная реализация работы с массива. Наверное слишком низкоуровневая по сравнению с другими интерпретируемыми языками программирования. Возможно это даёт большую гибкость, но лично мне не удобно, что для простое удаление элемента массива по виду:

mass=[1,2,3,4];
delete mass[2];

Приводит на самом деле не к удалению элемента массива, а пометке, что элемент пустой. Многие пользуются командой splice:

mass=[1,2,3,4];
mass.splice(2,1)

Или даже filter:

brisks_mass=[1,2,3,4];
brisks_mass=brisks_mass.filter((number)=>number!==3) 

Но как говорится «Мне не зашло» (c), ибо есть с чем сравнивать удобство (Python, PHP и т.д.)

JavaScript: декодирование строки в Base64

Вообще штатно есть функции для кодирования/декодирования: atob / btoa. Но один нюанс: если строчку вида «Hello world!» они кодирую-декодируют легко, то с бинарными файлами выйдет ой. Не, они конечно тоже что-то сделают, но с «тихой ошибкой» — т.е. результат будет, но не верен. Попался так, когда с сервера передавал файл в base64 на клиент javascript, декодировал его примерно так:

let a = document.createElement("a");
let file = new Blob([atob(data.result)], {type: 'application/xlsx'});
a.href = URL.createObjectURL(file);
a.download = "template_0.xlsx";
a.click();

А в результате, с виду вроде бы валидный файл, экселем открываться отказался. А всё потому, что atob не работает со строками в Uicode. Потому обычно используют следующие обертки для кодирования-декодирования:

function base64ToBytes(base64) {
  const binString = atob(base64);
  return Uint8Array.from(binString, (m) => m.codePointAt(0));
}
function bytesToBase64(bytes) {
  const binString = String.fromCodePoint(...bytes);
  return btoa(binString);
}

Сброс пароля Bitrix

Для того чтобы сбросить пароль у известного пользователя и известным ID, достаточно в корне сайта разместить файл:

r.php

<?php
$USER_ID=111;
$pass="123";
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
$user = new CUser;
$fields = Array("PASSWORD" => "$pass", "CONFIRM_PASSWORD" => "$pass");
$user->Update($USER_ID, $fields);
if($user->LAST_ERROR) {
echo $user->LAST_ERROR;
} else {
echo "Password successfully changed!";
}
?>

Вместо 111 — вставить ID пользователя из БД. Вместо 123 — свой пароль. Далее открыть этот файл через браузер. После смены пароля естественно файл нужно удалить

ГИС ЖКХ — отправка и подпись запросов часть 3

В продолжение предыдущей статьи. На основе того кода, так и не удалось довести до рабочего состояния. Ошибка так и осталась. Посему было решено..опять всё переписать с нуля.. С учётом полученных новых вводных, а именно:

  • XML подписи оказывается должен быть минимизирован (не должно быть форматирования тегов)
  • подписываться должен хэш данных, а не сами данные
  • при работе с XML как текстом, были опасения про неверную каннизацию

Потому переписал код на отностительно честную работу с XML через DOMDocument. С одним нюансом — так и не научился вставлять один XML документ в другой посредством его. Поэтому присутствует трюк с переводом XML в текст, склейкой двух XML и обратный перевод в DOMDocument. XML шаблоны удалось сократить до двух:

fish.xml:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:base="http://dom.gosuslugi.ru/schema/integration/base/" xmlns:drs="http://dom.gosuslugi.ru/schema/integration/drs/" xmlns:xd="http://www.w3.org/2000/09/xmldsig#">
   <soapenv:Header>
      <base:RequestHeader>
         <base:Date>2025-03-19T11:26:00+03:00</base:Date>
          <base:MessageGUID>${=java.util.UUID.randomUUID()}</base:MessageGUID>
	  <base:orgPPAGUID>c8913980-8cbb-4a46-bdac-7fecd98c7ff8</base:orgPPAGUID>
      </base:RequestHeader>
   </soapenv:Header>
<soapenv:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><drs:exportDSRsRequest Id="{signed_id}" base:version="15.3.0.0"><drs:periodOfSendingRequest><base:startDate>2025-05-27</base:startDate><base:endDate>2025-06-28</base:endDate></drs:periodOfSendingRequest></drs:exportDSRsRequest></soapenv:Body></soapenv:Envelope>

xades_dom.xml:

<ds:Signature Id="xmldsig-{signature_id}" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><ds:SignatureMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256"/><ds:Reference Id="xmldsig-{signature_id}-ref0" URI="#{signed_id}"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></ds:Transforms><ds:DigestMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256"/><ds:DigestValue>{digest1}</ds:DigestValue></ds:Reference><ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-{signature_id}-signedprops"><ds:DigestMethod Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256"/><ds:DigestValue>{digest3}</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue Id="xmldsig-{signature_id}-sigvalue">{signature_value}</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>{x590_cert}</ds:X509Certificate></ds:X509Data></ds:KeyInfo><ds:Object><xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Target="#xmldsig-{signature_id}"><xades:SignedProperties Id="xmldsig-{signature_id}-signedprops"><xades:SignedSignatureProperties><xades:SigningTime>{signing_time}</xades:SigningTime><xades:SigningCertificate><xades:Cert><xades:CertDigest><DigestMethod xmlns="http://www.w3.org/2000/09/xmldsig#" Algorithm="urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34112012-256"/><DigestValue xmlns="http://www.w3.org/2000/09/xmldsig#">{digest2}</DigestValue></xades:CertDigest><xades:IssuerSerial><X509IssuerName xmlns="http://www.w3.org/2000/09/xmldsig#">{x509_issuer_name}</X509IssuerName><X509SerialNumber xmlns="http://www.w3.org/2000/09/xmldsig#">{x509_sn}</X509SerialNumber></xades:IssuerSerial></xades:Cert></xades:SigningCertificate></xades:SignedSignatureProperties></xades:SignedProperties></xades:QualifyingProperties></ds:Object></ds:Signature>    

И сам код скрипта:

<?php

$fish_file='in/fish.xml';       // xml - главный шаблон, откуда осуществляем сборку
$xades_file='in/xades_dom.xml';      // xml - шаблон подписи
$cert_file='keys/cert.key';     // Сертификат формата X590
$private_file='keys/priv.key';  // Закрытый ключ

function md_gost($text){
  file_put_contents("tmp.xml", $text);
  $res=cat tmp.xml | openssl dgst -binary -md_gost12_256 | base64  -w 0;
  $res= str_replace("\n", "", $res);
  return trim($res);
};

function md_gost_bin($text){
  file_put_contents("tmp.xml", $text);
  $res=cat tmp.xml | openssl dgst -binary -md_gost12_256>hash.bin;  
  return file_get_contents("hash.bin");
};

function get_issuer($cert_file){
  $res=openssl x509 -in $cert_file -issuer -nameopt sep_multiline,utf8 -noout;
  $res= str_replace("issuer=\n", "", $res);
  $res= str_replace("emailaddress", "1.2.840.113549.1.9.1", $res);
  $res= str_replace("inn", "1.2.643.100.4", $res);
  $res= str_replace("ogrn", "1.2.643.100.1", $res);
  $res= str_replace('"', "\\", $res);
  $res= str_replace(',', "\,", $res);
  $res= explode("\n", $res);
  $result="";
  for ($index = count($res)-1; $index > 0; $index--) {
    if (trim($res[$index])!=""){
        $prop= explode("=",$res[$index]);
        $result=$result.strtolower(trim($prop[0]))."=".trim($prop[1]);
        if ($index>1){
            $result=$result.",";
        };
    };
  };
  return trim($result);
}

function get_serial($cert_file){
    $res=openssl x509 -in $cert_file -serial -nameopt sep_multiline,utf8 -noout;
    $res= str_replace("serial=", "", $res);
    $res= str_replace("\n", "", $res);
    return base_convert(trim($res),16,10);
}

function get_signature($text, $private_file){
    file_put_contents("for_sign.xml", $text);    
    $res=cat for_sign.xml | openssl dgst -md_gost12_256 -sign $private_file -binary | base64 -w 0;
    $res= str_replace("\n", "", $res);
    return trim($res);
}

function X509($cert_file){
  $crt= file_get_contents($cert_file);  
  $crt= str_replace("-----BEGIN CERTIFICATE-----", "", $crt);
  $crt= str_replace("-----END CERTIFICATE-----", "", $crt);
  $crt= str_replace("\n", "", $crt);
  $crt= str_replace("\r", "", $crt);
  return trim($crt);
}

function guidv4($data = null) {
    $data = $data ?? random_bytes(16);
    assert(strlen($data) == 16);
    $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

$dt=new DateTime();
$signing_time=$dt->format(DateTime::ATOM);
$signature_id=guidv4();  // идентификатор блока "подпись"
$signed_id=guidv4();     // идентификатор блока который подписываем

$fish_xml=file_get_contents($fish_file);
$dom = new DOMDocument('1.0', 'utf-8'); 
$dom->loadXML($fish_xml);
$root = $dom->documentElement;

$req_xml=$root->getElementsByTagName('exportDSRsRequest')->item(0);
$req_xml->setAttribute('Id',$signed_id);
$req_can=$req_xml->C14N(true);
$digest1=md_gost($req_can);
echo "req=$req_can\n";
echo "digest1=$digest1\n";
$digest2= md_gost(base64_decode(X509($cert_file)));
echo "digest2=$digest2\n";

$dom = new DOMDocument('1.0', 'utf-8'); 
$tmp_text_xml=$root->C14N(true);

// подмешиваем подпись
$xades= file_get_contents($xades_file);
$tmp_text_xml= str_replace('base:version="15.3.0.0">', 'base:version="15.3.0.0">'.$xades, $tmp_text_xml);

$tmp_text_xml= str_replace("{signing_time}", $signing_time, $tmp_text_xml);
$tmp_text_xml= str_replace("{signature_id}", $signature_id, $tmp_text_xml);
$tmp_text_xml= str_replace("{digest1}", $digest1, $tmp_text_xml);
$tmp_text_xml= str_replace("{digest2}", $digest2, $tmp_text_xml);
$tmp_text_xml= str_replace("{x509_issuer_name}", get_issuer($cert_file), $tmp_text_xml);
$tmp_text_xml= str_replace("{x509_sn}", get_serial($cert_file), $tmp_text_xml);


$dom->loadXML($tmp_text_xml);
$root = $dom->documentElement;

$digest3=md_gost($root->getElementsByTagName('SignedProperties')->item(0)->C14N(true));
echo "digest3=$digest3\n";

$dom = new DOMDocument('1.0', 'utf-8'); 
$tmp_text_xml=$root->C14N(true);
$tmp_text_xml= str_replace("{digest3}", $digest3, $tmp_text_xml);
$tmp_text_xml= str_replace("{x590_cert}", X509($cert_file), $tmp_text_xml);
$tmp_text_xml= str_replace("{signed_id}", $signed_id, $tmp_text_xml);

$dom->loadXML($tmp_text_xml);
$root = $dom->documentElement;

// подписываем
$for_sign=$root->getElementsByTagName('SignedInfo')->item(0)->C14N(true);
$hash_for_sign= md_gost_bin($for_sign);
$sign=get_signature($hash_for_sign,$private_file);
echo "sign=$sign\n";

$dom = new DOMDocument('1.0', 'utf-8'); 
$tmp_text_xml=$root->C14N(true);
$tmp_text_xml= str_replace("{signature_value}", $sign, $tmp_text_xml);

$dom->loadXML($tmp_text_xml);
$root = $dom->documentElement;


//
echo "---------------- ИТОГО ---------------\n";
file_put_contents("out.xml", $root->C14N(true));

Но увы и ах.. Результат его работы хоть с виду еще более нормальный, но ГИС ЖКХ так и продолжает возвращать ошибку:

<ns4:ErrorCode>AUT011005</ns4:ErrorCode>
<ns4:Description>Ошибка формата подписи запроса</ns4:Description>

Update 24.07.2025:

Всё получилось. Собака порылась в не верном преобразовании серийного номера в десятичный вид. Итоговый вид функции (правильный):

function get_serial($cert_file){
    $res=openssl x509 -in $cert_file -serial -nameopt sep_multiline,utf8 -noout;
    $res= str_replace("serial=", "", $res);
    $res= str_replace("\n", "", $res);
    $res = echo "ibase=16;$res"|bc;
    $res= str_replace("\n", "", $res);
    return $res;
}
1 6 7 8 9 10 300