Как правильно подписать XML в Delphi

Я пытаюсь подписать XML в Delphi сертификатом, но все, что я получаю, это какие-то нераспознанные символы. я использую эту функцию

var
   xmlData, signature: PByte; data: array[0..0] of PByte;
   msgCert: array[0..0] of PCCERT_CONTEXT;
   dwDataSizeArray: array[0..0] of DWORD;
   sigParams: CRYPT_SIGN_MESSAGE_PARA;
   cbSignedBlob: DWORD;
begin
   if PCertContext = nil then
    Exit;

   GetMem(xmlData, Length(AXml));
   try
      system.Move(Pointer(AXml)^, xmlData^, Length(AXml));
      ZeroMemory(@sigParams, SizeOf(CRYPT_SIGN_MESSAGE_PARA));
      sigParams.cbSize := SizeOf(CRYPT_SIGN_MESSAGE_PARA);
      sigParams.dwMsgEncodingType := (X509_ASN_ENCODING or PKCS_7_ASN_ENCODING);

      sigParams.pSigningCert := PCertContext;
      sigParams.HashAlgorithm.pszObjId := szOID_RSA_MD5;
      //SigParams.cAuthAttr := 0;
      //SigParams.dwInnerContentType := 0;
      //SigParams.cMsgCrl := 0;
      //SigParams.cUnauthAttr := 0;
      //SigParams.dwFlags := 0;
      //SigParams.pvHashAuxInfo := nil;
      //SigParams.rgAuthAttr := nil;  }
      data[0] := xmlData;
      dwDataSizeArray[0] := Length(AXml);
      cbSignedBlob := 0;

      CryptSignMessage(@sigParams, True, 1, @data[0], @dwDataSizeArray[0], nil, @cbSignedBlob);

      GetMem(signature, cbSignedBlob);
      try
         CryptSignMessage(@sigParams, True, 1, @data[0], @dwDataSizeArray[0], signature, @cbSignedBlob);
         SetLength(Result, cbSignedBlob);
         system.Move(signature^, Pointer(Result)^, cbSignedBlob);
      finally
         FreeMem(signature);
      end;
   finally
      FreeMem(xmlData);
   end;
end

но все, что я получаю, это:

'舰餁आ蘪虈'#$0DF7'paid りƂʆāัర'#$0806'蘪虈'#$0DF7'Ԃ'#5'ରआ蘪虈'#$0DF7' 聪#$3101'ƂぢƂɞā㠰 〰
'#$0B31'र̆ѕጆ匂ㅉ『،唃'#$0A04'ԓ佐呓ㅁ】؏唃'#$0B04#$0813'佐呓牁䅃Ђ䤾Eర'#$0806'蘪虈'#$0 DF7'Ԃ'#5'രआ蘪虈'#$0DF7'āԁЀƂ堀묥䮡悕㈒古虺̐昇⠹꺶힊覬왧䮽㕺⢂꺇宬䝄'#$07B3'䲠'#$D868'㶙㌌ 㟜'#$DAF2'#$2B83#$1C'ity-1.0.xsd" wsu:Id="Timestamp-3c7c79b4-2afa-4184-9d2c-8f5e721c8421">2014-01-13T11:26:52Z2014-01-13T11 :31:52Z'#0' OTRRC.PTT'

и есть много других данных, которые должны быть в подписанном XML. В С# это было решено так

    AsymmetricAlgorithm rsa = signCertificate.PrivateKey;

    // Read provided document, find the signature element ...
    MemoryStream documentStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(document));

    XmlDocument doc = new XmlDocument();
    doc.PreserveWhitespace = true;
    doc.Load(new XmlTextReader(documentStream));

    XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
    nsmgr.AddNamespace("ds", dsigURI);
    XmlNode sig = doc.SelectSingleNode("//*[@Id='" + signatureID + "']", nsmgr);

    XmlNode after = sig.PreviousSibling;
    XmlNode parent = sig.ParentNode;

    SignedXml signedXml = new SignedXml(doc);
    parent.RemoveChild(sig);

    Reference r = new Reference();
    r.Uri = "";
    r.AddTransform(new XmlDsigEnvelopedSignatureTransform());
    signedXml.AddReference(r);

    X509Certificate cert1 = new X509Certificate(signCertificate.GetRawCertData());

    KeyInfo ki = new KeyInfo();
    ki.AddClause(new KeyInfoX509Data(cert1));
    signedXml.KeyInfo = ki;

    signedXml.Signature.Id = signatureID;

    signedXml.SigningKey = rsa;
    signedXml.ComputeSignature();

    XmlNode signature = doc.ImportNode(signedXml.GetXml(), true);
    parent.InsertAfter(signature, after);

    return doc.OuterXml;                

Спасибо за помощь заранее!


person Tomek    schedule 13.01.2014    source источник
comment
Пожалуйста, отформатируйте код лучше, чтобы нам не нужно было использовать горизонтальную полосу прокрутки. Пожалуйста, также поясните, какой результат вы ожидаете увидеть и почему фактический результат не соответствует вашим ожиданиям. Например, кажется, что вы интерпретируете 8-битный текст как UTF-16. Это намеренно?   -  person David Heffernan    schedule 13.01.2014
comment
Нет, это было просто скопировано с другого сайта, где вы имеете в виду, что у меня есть 8-битный текст в формате utf-16? Вывод должен выглядеть так (я удалил значения сертификатов) en.textsave.org/XAN   -  person Tomek    schedule 13.01.2014
comment
Весь этот китайский код указывает на то, что 8-битный текст ANSI обрабатывается как UTF-16.   -  person David Heffernan    schedule 13.01.2014
comment
Какая версия Делфи? Эта информация определяет ответ, когда речь идет о проблеме с Unicode.   -  person Jan Doggen    schedule 13.01.2014
comment
Delphi® 2010, версия 14.0.3593.25826. Как я могу изменить этот юникод?   -  person Tomek    schedule 13.01.2014


Ответы (1)


Посмотрите на этот код:

SetLength(Result, cbSignedBlob);
system.Move(signature^, Pointer(Result)^, cbSignedBlob);

Я только предполагаю, но Result, вероятно, относится к типу string. Это строка в кодировке UTF-16. Но вы копируете всего cbSignedBlob байта, что заполняет только половину буфера. Я подозреваю, что у вас есть текст в кодировке ANSI или UTF-8, но это немного сложно сказать.

Если текст закодирован в UTF-8, то это то, что вы делаете:

var
  utf8: UTF8String;
....
SetLength(utf8, cbSignedBlob);
system.Move(signature^, Pointer(utf8)^, cbSignedBlob);
Result := string(utf8);

Если текст закодирован в ANSI, вы должны сделать это:

var
  ansi: AnsiString(1252); // or whatever the code page really is
....
SetLength(ansi, cbSignedBlob);
system.Move(signature^, Pointer(ansi)^, cbSignedBlob);
Result := string(ansi);

Здесь, вероятно, происходит больше, но, учитывая количество деталей, представленных в вопросе, это все, что я вижу.

Во-первых, я очень подозрительно отношусь к вашему отсутствию проверки ошибок. Вы вызываете эти функции API, но игнорируете их возвращаемые значения. Держу пари, что функции не работают, и ваш код продолжает работать независимо. Следующим шагом является добавление проверки ошибок.

person David Heffernan    schedule 13.01.2014
comment
Так ты не получишь ответа. Вы должны быть точны. Начните с проверки ошибок в соответствии с моим ответом. - person David Heffernan; 13.01.2014
comment
Я действительно не знаю, что еще сказать. Я получаю XML от службы, которая возвращает его в виде строки, которую я загружаю с помощью XMLDoc := LoadXMLData(document); и я передаю XMLDoc.XML.Text в этот SignXML(AXml: AnsiString): string; только что я заметил, что неправильно закодирована только часть подписи, другой текст в XML в порядке. - person Tomek; 14.01.2014