0

I'm trying to sign XML in Delphi with certificate, but all I'm getting are some unrecognized characters. I'm using this function

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

but all I get is:

'舰餁आ蘪虈'#$0DF7'܁ꀂƂりƂʆāัర'#$0806'蘪虈'#$0DF7'Ԃ'#5'ରआ蘪虈'#$0DF7'܁'#$3101'ƂぢƂɞā㠰 〰
'#$0B31'र̆ѕጆ匂ㅉ『،唃'#$0A04'ԓ佐呓ㅁ】؏唃'#$0B04#$0813'佐呓牁䅃Ђ䤾Eర'#$0806'蘪虈'#$0DF7'Ԃ'#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'

and there is alot more data that need to be in signed XML. In C# this was solved like this

    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;                

Thanks for help in advance!

Tomek
  • 557
  • 2
  • 7
  • 24
  • Please format the code better so that we don't need to use the horizontal scroll bar. Please also make clear what output you expect to see and why the actual output is not what you expect. For instance, it seems that you are interpreting 8 bit text as UTF-16. Is that intentional? – David Heffernan Jan 13 '14 at 11:38
  • No, this was just copied from other site, where do you mean I have 8 bit text as utf-16? Output has to look like this(I deleted certificate values) http://en.textsave.org/XAN – Tomek Jan 13 '14 at 11:59
  • All that Chinese code is indicative of treating 8-bit ANSI text as UTF-16. – David Heffernan Jan 13 '14 at 12:40
  • What Delphi version? That info determines the answer when it's a Unicode issue. – Jan Doggen Jan 13 '14 at 12:45
  • Delphi® 2010 Version 14.0.3593.25826. How can I change that unicode? – Tomek Jan 13 '14 at 12:50

1 Answers1

2

Look at this code:

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

I'm just guessing, but Result is probably of type string. That's a UTF-16 encoded string. But you copy in just cbSignedBlob bytes, which fills only half of the buffer. I suspect that you have either ANSI or UTF-8 encoded text, but it is a little hard to tell.

If the text is UTF-8 encoded, then this is what you do:

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

If the text is ANSI encoded then you would do this:

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

There's probably more going on here but given the amount of detail presented in the question, this is as much as I can see.

For a start I am very suspicious of your lack of error checking. You call these API functions but ignore their return values. I'll wager the functions fail and your code continues regardless. Next step is to add error checking.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • You won't get an answer that way. You need to be precise. Start off with some error checking as per my answer. – David Heffernan Jan 13 '14 at 14:29
  • I don't really know what else to tell. I get XML from a service which returns it as a string, which I load with XMLDoc := LoadXMLData(document); and I pass XMLDoc.XML.Text to this SignXML(AXml: AnsiString): string; function The thing I noticed just now is that only signature part is wrong encoded, other text in XML is fine – Tomek Jan 14 '14 at 09:45