Hi All!
My managed C# app has to sign edited assemblies after finishing its work. I
have tried to use .NET Framework unamnaged API function
StrongNameSignatureGeneration() which apparently works (success as return
result) but not with all scenarios described in MSDN
documentation and moreover the signing result seems to be incorrect!
Scenario 1.
I can not reproduce scenario where passing null pointer to ppbSignatureBlob
causes function to sign assembly file instead of returning
signature blob in ppbSignatureBlob.
Scenario 2. (with signature blob retrieval)
The test for signing result is as follows:
1. PEFile with StrongName signature is created (valid assembly which can be
loaded and executed by CLR).
2. PEFile is parsed and StrongNameSignature pointed by CLIHead is zeroed -
exactly 128 bytes at FilePointer calculated from Signature RVA.
3. StrongNameSignatureGeneration function is called on modified PEFile (the
step 2 could be ommited to my knowledge as signature area is
not hashed during signing - code analysis of strongname.cpp of SSCLI).
4. After retrieving signature blob in ppbSignatureBlob it is written into
PEFile at FilePointer address of original signature.
Assembly signed in that way does not pass verification with sn tool.
When this same is done with sn tool -R (resign) option on this same assembly
different signature blob is obtained than with API call
(signature blob generated by StrongNameSignatureGeneration and sn tools are
not simple start - end reversal)!
This can be easily traced with any hex dumps of file content. Furthermore
file signed with sn -R passes sn verification test.
My questions:
1. How can I force StrongNameSignatureGeneration to sign file?
2. What's wrong with signature generated by StrongNameSignatureGeneration
function when retrieving it via blob?
My code follows.
Thanks for any hints to the problem.
Jacek
DllImport prototype declaration is as follows:
[DllImport("mscoree.dll", CharSet=CharSet.Unicode, SetLastError=true)]
public static extern bool StrongNameSignatureGeneration(
[In, MarshalAs(UnmanagedType.LPWStr)] String wszFilePath,
[In, MarshalAs(UnmanagedType.LPWStr)] String wszKeyContainer,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=3)] byte[]
pbKeyBlob,)
[In,MarshalAs(UnmanagedType.U4 )] uint cbKeyBlob,
[Out] out IntPtr ppbSignatureBlob,
[Out,MarshalAs(UnmanagedType.U4 )] out uint pcbSignatureBlob);
Function is called in the following way (it should be sufficient to comment
out buff = Marshal.AllocHGlobal(128) to reproduce file signing
scenario but it does not work - signature is returned in buff instead of
being written to assembly file);:
public static byte[] SignatureGeneration(String AssemblyFilePath, String
KeyFile)
{
byte[] retVal = null;
IntPtr buff = IntPtr.Zero;
uint kl;
FileStream fs = null;
try
{
fs = new FileStream(KeyFile, FileMode.Open);
byte[] keypair = new byte[fs.Length];
int read = fs.Read(keypair, 0, (int)fs.Length);
if ((long) read != fs.Length) throw new
System.IO.IOException("Error while reading file.");
buff = Marshal.AllocHGlobal(128);
if (!Interop.StrongNameSignatureGeneration(
AssemblyFilePath, String.Empty, keypair, (uint) keypair.Length,
out buff, out kl))
{
int result = Marshal.GetHRForLastWin32Error();
Marshal.ThrowExceptionForHR(result);
}
retVal = new byte[kl];
if (buff != IntPtr.Zero) Marshal.Copy(buff, retVal, 0,
retVal.Length);
}
finally
{
if (buff != IntPtr.Zero) Marshal.FreeHGlobal(buff);
if (fs != null) fs.Close();
}
return retVal;
}
Jacek - 10 Jun 2004 16:39 GMT
Finally I have found solution and thought it could be of interest to others.
Unfortunately I was unable to use StrongNameSignatureGeneration to sign
anything so I have tested recently released libraries of Mono Beta 2 (Ximian
CLR, CLI implemetation comprising large parts of .NET as well with numerous
very useful extensions) and found Mono.Security.StrongName class which can
be used for signing and verification of assemblies.
This works perfectly!!! Assemblies signed pass verification by sn.exe tools
of both Mono and .NET distributions.
Shame on MS for not being able to provide appriopriate support for such a
basic part of infrastructure.
Jacek
Code of method employing Mono.StrongName class to sign assemblies follows:
internal void ResignAssemblyMono()
{
FileStream fs = null;
try
{
fs = new FileStream("C:\KeyPairFile", FileMode.Open);
byte[] keypair = new byte[fs.Length];
int result = fs.Read(keypair, 0, (int) fs.Length);
if (result != keypair.Length) throw new System.IO.IOException("Error
while reading file.");
StrongName sn = new StrongName(keypair);
if (!sn.Sign(this.file)) throw new Exception ("Failed to sign file: " +
Path.GetFileName(this.file));
}
finally
{
if (fs != null) fs.Close();
}
}
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
[quoted text clipped - 102 lines]
> =cu1V
> -----END PGP SIGNATURE-----
Jacek - 11 Jun 2004 10:44 GMT
Hi Again!
I have to withdraw my complaints about MSFT, as there exist working solution
for using StrongNameSignatureGeneration() from C#.
Please check for SDK sample TlbGen in Technologies\Interop folder.
StrongName class declares imports of different API functions and
ResignAssembly method implements assembly resigning.
Tests are passed so I am positive about this solution. However, due to
P/Invoke independant nature of Mono implemenation I prefer to use that one.
Jacek
PS P/Invoke declarations are made in a weird way which is surely not
recommended by MS in all P/Invoke documentation I have read and it was a lot
on that subject. i.e. pointers to C style byte arrays are declared as int
instead of IntPtr, btw they are not used at all.
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
[quoted text clipped - 102 lines]
> =cu1V
> -----END PGP SIGNATURE-----