Groups | Blog | Home
all groups > dotnet security > july 2006 >

dotnet security : Digitally sign files from within a web application


Joao Maia
7/26/2006 2:42:32 AM
Hi there,

I am a newbie to the security framework of .net and to digital
signatures, and I need some help regarding signing files in .net.

Here's my problem: I have a web application developed in asp.net (with
..net framework 1.1). This web app has a support database and can access
files that are stored on a physical drive in some server (pretty much
any kind of file, although the most accessed are .doc, .xls, .ppt,
..pdf, .tif). What I want to do now is to add some digital signature
functionality to that app. The users must be able to choose a file and
digitally sign it with a certificate stored locally (either on their
machine or in a smartcard, for example). And the signature must be
valid outside my app, for example if any user opens a doc file in Word
directly from its location on the physical disk, Word must still
recognize that the file is signed.

[quoted text, click to view]

- I need to pass the file from server to client in order to sign it,
since the private key is stored on the certificate, which is on the
client. Since the files can be big, and for performance issues, I think
it's better to pass a hash of the file and sign the hash. Is this
correct ? Does the .net framework provide any way of building a hash
for this purpose ? I've been looking at the HashAlgorithm class and
ComputeHash method. Is this it ?

- I need to sign the file with the certificate on the client side. Here
I'm completely at loss, since I haven't yet found any methods to sign
files or a stream or a byte array with a certificate. I've been
browsing through X509Certificate class, but I've got no clues...

- Finally, after the hash is signed, I need to pass it back to the
server and somehow, to store it with the original file, so that the
signature is recongnized outside my app. Here I'm also at a loss. I
don't even know if there's a way to do this.

So I was wondering if any of you can help me with this problem. I'm not
quite sure if the approach I mentioned is the best one or not. Maybe
there's some easier way to do it, but as I told you, this is new
terrain for me, I've never had to deal with certificates and signatures
before, so I really don't know where to start. Any help on this will be
greatly appreciated.

Thanks in advance.

Cheers from Portugal,

Joao Maia
Joe Kaplan (MVP - ADSI)
7/26/2006 9:16:34 AM
The first thing you'll need to figure out is how you are going to execute
code on the user's machine. Will you use an ActiveX control or a
downloadable .NET WinForms control? You could also install something on
their machine or use ClickOnce deployment if you can use .NET 2.0. However,
you can't do this only in server-side code.

If you can, I'd recommend using the SignCms Class in .NET 2.0 for actually
packaging up the signed data into a standard PKCS7 signed data structure,
but you could probably use the RSA classes directly. This all presupposes
that your deployment model of your client side code will allow you to use
..NET in the first place.

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]

Joao Maia
7/26/2006 10:26:05 AM
Hi, thanks for your reply.

Regarding the execution of code on the client site, I was thinking of
using an ActiveX control. Eventually I thought about using a .NET
Winforms control, but the issue here is that I'm not able to use .NET
2.0 because we can't migrate the code we have now from .NET 1.1.

Joao Maia


[quoted text, click to view]
Joe Kaplan (MVP - ADSI)
7/26/2006 1:49:52 PM
In an ActiveX control, you'd probably want to use CAPICOM to do the signing,
but you'd have to make sure that's installed in order to use it. You could
also call the cryptoapi directly.

With a .NET control in 1.1, you don't have the CMS support by you can sign
data. There is a SignHash method on RSACryptoServiceProvider, so if you can
pass the hash of the data to the client, you could potentially use that.

Best of luck,

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]

Eugene Mayevski
7/26/2006 10:31:31 PM
Hello!
You wrote on 26 Jul 2006 02:42:32 -0700:

JM> machine or in a smartcard, for example). And the signature must be
JM> valid outside my app, for example if any user opens a doc file in Word
JM> directly from its location on the physical disk, Word must still
JM> recognize that the file is signed.

This is not possible: most file formats don't include support for digital
signatures and if you sign the file, you will have one of the following:
either a detached signature (kept in a separate file) or a single file in
some format which is not readable by the applications that are intended to
read the document. For example, you can sign using PKCS#7, but then you end
up with one of the variants that I've mentioned.

With best regards,
Eugene Mayevski
http://www.SecureBlackbox.com - the comprehensive component suite for
network security
Eugene Mayevski
7/27/2006 12:00:00 AM
Hello!
You wrote on 27 Jul 2006 02:11:27 -0700:

JM> I understand that, but what about file formats that do include support
JM> for digital signatures, such as Word or PDF ? To be honest, the only
JM> file formats I'm interested in to support this functionality are PDF,
JM> Word, Excel, Powerpoint. For the rest of the file formats, I really am
JM> not worried whether the signature is recognized outside my app, or not.

MS formats are closed, and, moreover, they are different between versions.

JM> Now with those that do include support for signatures, my question is
JM> if it is possible to create a hash of this file, sign it, and then
JM> somehow merge the signed hash back into the original file so that the
JM> application dealing with the file recognizes that it is signed.

As for PDF, this will be possible with SecureBlackbox 5.

JM> I've noticed that, for example, the Word API has something like
JM> Word.ActiveDocument.Signatures collection and that you can actually add
JM> items to that collection, but I don't know what kind of objects I can
JM> add to that collection and even if I did know I figure that I need to
JM> use the same method Word uses to sign files (same algorithm for the
JM> hash, same way of signing), otherwise the signature would not be
JM> recognized. Is this correct ? Or am I still missing something here ?

With MS tools the situation is quite complex: as the formats are closed,
it's hard to get any information about how to use the security with MS
documents.

With best regards,
Eugene Mayevski
http://www.SecureBlackbox.com - the comprehensive component suite for
network security
Joao Maia
7/27/2006 2:11:27 AM

[quoted text, click to view]

Hi,

Thanks for your reply.

I understand that, but what about file formats that do include support
for digital signatures, such as Word or PDF ? To be honest, the only
file formats I'm interested in to support this functionality are PDF,
Word, Excel, Powerpoint. For the rest of the file formats, I really am
not worried whether the signature is recognized outside my app, or not.

Now with those that do include support for signatures, my question is
if it is possible to create a hash of this file, sign it, and then
somehow merge the signed hash back into the original file so that the
application dealing with the file recognizes that it is signed. I've
noticed that, for example, the Word API has something like
Word.ActiveDocument.Signatures collection and that you can actually add
items to that collection, but I don't know what kind of objects I can
add to that collection and even if I did know I figure that I need to
use the same method Word uses to sign files (same algorithm for the
hash, same way of signing), otherwise the signature would not be
recognized. Is this correct ? Or am I still missing something here ?

Thanks once again,

Joao Maia
Joao Maia
7/27/2006 8:04:24 AM
Hi again,

What if I sign the files themselves and not just a hash ? Is this
possible ? Can you sign a FileStream that's actually a doc document
(for example) with a certificate from a certificate store ? Will word
still recognize it as a signed file ? Or the FileStream will just be
encrypted in a way that the app (in this case word) won't recognize it
anymore as a word file ?

Anyway, what's this thing about PKCS#7 signed data ? I understand that
it is a RSA standard and tried reading the specification but I'm afraid
I did not understand it very well and how it can be used on a practical
situation. Correct me if I'm wrong but, from what I understood, after
signing you're supposed to have two files, one which is the original
and one with the signed digest. In this scenery, I can validate that
the file hasn't been messed with in my application by doing the hash of
the original file, decrypting the signed hash with the certificate
public key and comparing both hashes. But how can the original
application from which the file was originally created (Word in our
example), be able to recognize the signature ?

Once again thank you both for helping me and sorry for bothering you
with this.

Joao Maia

[quoted text, click to view]
Joe Kaplan (MVP - ADSI)
7/27/2006 9:42:17 AM
Note that I had sort of assumed you were going to do some sort of detached
signature thing or use PKCS7 signed data in order to accomplish this. I
agree with Eugene that there is no clean way to provide general purpose
signing of arbitrary files without doing that.

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]

Joao Maia
7/27/2006 9:59:30 AM
Thanks, I think I got the basic idea behind PKCS#7 now. Do you have any
idea regarding what kind of clients support this standard ?

Joao Maia


[quoted text, click to view]
Joe Kaplan (MVP - ADSI)
7/27/2006 10:52:35 AM
PKCS7 is a standard developed by RSA that is useful for providing encryption
and signing capabilities to arbitrary data, primary using certificates and
RSA encryption/decryption.

Essentially, it uses ASN.1 and defined schema for specifying how to "wrap"
content with signature and encryption info. The enveloped data structure
solves the problem with encryption of how to specify the symmetric algorithm
used, where to store the encrypted symmetric key, who the message was
encrypted for, etc. With signed data, it specifies the certs used to sign
the data, the message hash, the data that was signed, etc. This saves you
from having to invent your own file format for doing all the same stuff and
allows clients that understand PKCS7 (like SMIME compatible email clients)
to exchange signed and encrypted content.

I actually don't know anything about how to encorporate signatures into file
formats that have built-in support for signatures like Word and PDF. Eugene
seems to have some code that supports this type of thing for PDF though.

Also, the way the SignData method on RSACryptoServiceProvider works is that
it does not change the underlying data being signed. It simply gives you a
binary blob of data with a signature based on the data you wanted signed and
the parameters you wanted to sign it with. This is essentially just a
detached signature. There seemed to be some misunderstanding on how it
worked based on your first paragraph.

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]

Joe Kaplan (MVP - ADSI)
7/27/2006 12:31:37 PM
Well, lots of email clients do. Outlook and Outlook Express come to mind.
I'm sure many others do as well.

I'm not sure if PKCS7 will work for you or not as I don't really understand
what it is that you are trying to do with the digital signatures on this
data.

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]
Joao Maia
7/28/2006 9:19:05 AM
Basically, I'm part of a development team of a Document Management
Product. We want to add digital signature functionality to the
documents managed by our product. The purpose we are trying to achieve
is the usual for digital signatures: guaranteeing that the document is
not tampered with since the time it is signed, and if it is, invalidate
the signature. Since we deal with several types of documents, we need
to be able to sign at least the most used ones, from within our
application.

[quoted text, click to view]
such as pdf are PKCS#7 compliant, so I'm guessing that's the way to go.

jm

[quoted text, click to view]
Joe Kaplan (MVP - ADSI)
7/28/2006 12:41:10 PM
If you used detached signatures, you could definitely use those signatures
in the context of the web application to show their signed status and verify
if the signatures were still valid, since you would presumably have control
over all this stuff. That would allow you to sign arbitrary content as
well. The downside would be that the signatures in your system would not be
stored directly in the actual files, but that could be a feature. :)

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]
moises.cid NO[at]SPAM ideit.es
8/1/2006 8:15:24 AM

Joao Maia ha escrito:

[quoted text, click to view]

Hi,

Joao here you have an example for signing a pdf file:

http://article.gmane.org/gmane.comp.windows.dotnet.itextsharp.general/12

In this sample you can sign the file from a a certificate stored in a
device like a hdd o smartcard, but it doesn't work if you have an
asp.net application.
I've the same problem that you, how can a client of an asp.net
application sign a pdf document using the certificate stored in the
"personal store"???

I've seen something about CAPICOM but I'm lost, can anybody help me?

Thanks
Joao Maia
8/3/2006 6:31:37 AM
Hi,

What I'm going to do is write a .NET Windows Form component (which I'm
going to host on Internet Explorer) to get a certificate from the
user's certificate store to sign my document.

To access certificate stores and X509 Certificates, and to digitally
sign data, there are several approaches:

1 - Use the support on .NET Framework 1.1 in
System.Security.Cryptography and
System.Security.Cryptography.X509Certificates namespaces.

The support for this is somewhat limited on these namespaces (it has
improved on .NET Framework 2.0, but I can't use that so that's out of
the question for me). So I tried approach number 2:

2 - Download Microsoft Web Services Enhancements (WSE) at

http://www.microsoft.com/downloads/details.aspx?familyid=1ba1f631-c3e7-420a-bc1e-ef18bab66122&displaylang=en

This has some improved functionality on certificates and certificate
stores, plus it includes many code samples on how to do basic things
such as opening a store and signing data

3 - If there's still something that you can't do with approach 2, you
must do it with CAPICOM (also downloadble from Microsoft). CAPICOM is a
COM wrapper for Microsoft's Win 32 Crypto API.

If there's still something you can't do with CAPICOM, then you'll have
to call the Crypto API directly through Platform Invoke (P/Invoke)

Check out these links, to know more about this:

http://www.c-sharpcorner.com/UploadFile/Gowri%20S%20Paramasivam/Cryptography211242005003308AM/Cryptography2.aspx?ArticleID=cf900b08-35ed-4524-aeff-6806265a4196
http://www.c-sharpcorner.com/Code/2003/Jan/DigitalCertIII.asp

The problem I'm having right now is that I can't open a dialog for the
user to select the certificate because I'm getting the following error:

"That assembly does not allow partially trusted callers"

I figure this is happening because I'm calling some assembly that needs
special permissions to access the user's certificate store from a web
page, but still haven't been able to figure out what those permissions
are.

Hope I've been of some help... Let me know if you can solve your
problem or not.

Cheers from Portugal,

jm


[quoted text, click to view]
Joao Maia
8/3/2006 7:09:49 AM
[dominick]
[quoted text, click to view]

How do I do this ? What is mscorcfg.msc ? What is CasPol ?

Thanks,

jm
Joao Maia
8/3/2006 8:04:41 AM

[quoted text, click to view]

Ok, after searching for CasPol, I used it on my assembly with the
-addfulltrust option but it still doesn't work. Oh and my assembly is
strongly named.

My assembly (the user control hosted on IE) uses
Microsoft.Web.Services2.dll assembly from Microsoft WSE, so I ran
CasPol on that assembly as well, but I still get the same error
message.

Is there any way to know what is the assembly that is causing the
problem ?

jm
Joao Maia
8/3/2006 8:46:01 AM
I did that with both my assembly and the Microsoft.Web.Services2.dll
and I still get the same message. When I try to evaluate both
assemblies on .NET Configuration, it says that I have unrestricted
permissions, but I still can't get it to work.

One other thing, my code invokes a Win32 dll (cryptui):

[DllImport("cryptui", CharSet=CharSet.Unicode, SetLastError=true)]
internal extern static IntPtr
CryptUIDlgSelectCertificateFromStore(IntPtr hCertStore, IntPtr hwnd,
string pwszTitle, string pwszDisplayString, uint dwDontUseColumn, uint
dwFlags, IntPtr pvReserved);

Can this be the reason it's still not working ? Is there anything more
I need to do ?
The code above is part of a sample I got from the Microsoft WSE
installation, so I'm figuring it should work...

Thanks for your help,

jm


[quoted text, click to view]
Joao Maia
8/3/2006 9:47:54 AM
Ok, you guys must think I'm crazy or something but:

I tried asserting every kind of permission in
System.Security.Permissions before I do the call to the method that's
causing the error and guess what... it didn't work.

So I tried what Shawn suggests on his blog, which is creating a
condition based upon the Zone or the Site. I created a condition based
on the Zone, the Site, and the URL and still I get that "partially
trusted" annoying message...

What's weird is that, when I evaluate my dll using the Evaluate
Assembly tool on the .NET configiuration tool, and try to view code
groups that grant permissions to the assembly, the code groups I just
created don't appear for my assembly...

I'm almost throwing myself out of the window... :(

jm

[quoted text, click to view]
Joe Kaplan (MVP - ADSI)
8/3/2006 10:49:53 AM
Shawn Farkas has the canonical sample of this stuff on his blog somewhere.
Google should turn it up.

The big challenge with this type of thing is that granting full trust to the
assembly is not sufficient, because the app domain in IE does not have full
trust. The assembly that has full trust must also assert the permissions
that will be demanded to prevent the stack walk from getting out of the full
trust assembly.

The other way to do it is to grant full trust using a URL membership
condition, in which case the app domain WILL have full trust too. This
makes the programming easier, but less secure.

Programming for partial trust is a bit painful. Where is Nicole when we
need her? :)

HTH,

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
"Dominick Baier" <dbaier@pleasepleasenospam_leastprivilege.com> wrote in
message news:4580be63c35b8c8853787a064f3@news.microsoft.com...
[quoted text, click to view]

Joe Kaplan (MVP - ADSI)
8/3/2006 12:16:36 PM
I'm not good enough at CAS to help with the nasty problems. Hopefully
Nicole will see this and can save you (or Dominick or someone else wants to
try).

Best of luck,

Joe K.

--
Joe Kaplan-MS MVP Directory Services Programming
Co-author of "The .NET Developer's Guide to Directory Services Programming"
http://www.directoryprogramming.net
--
[quoted text, click to view]

Dominick Baier
8/3/2006 1:44:17 PM
Both CAPICOM and WSE3 demand full trust. Browser hosted controls don't get
FT by default.

The error you are seeing happens when a partially trusted client calls into
these DLLs.

You have to manually adjust the CAS Policy on every client to grant Full
Trust to your component - best by basing the policy on the strong name of
your component. You can use mscorcfg.msc or CasPol for this.

dominick

[quoted text, click to view]

Dominick Baier
8/3/2006 3:15:19 PM
You may find it more intuitive to use the GUI

Adm. Tools -> .NET Framework Configuration

Runtime Security Policy -> Machine -> All Code

Create new Code group -> use strong name membership condition and assign
full trust.

I looked for a good walkthrough online meanwhile, but couldn't find something...maybe
someone else has a link?

dominick

[quoted text, click to view]

Dominick Baier
8/3/2006 3:59:21 PM
aah yeah - i knew there was a gotcha.

Try a UrlMembershipCondition instead to get it to work.

Also have a look at blogs.msdn.com/shawnfa

dominick

[quoted text, click to view]

Joao Maia
8/4/2006 2:23:56 AM
Hi !

I found out two more things that may be important regarding this
problem:

First, the code in my user control is supposed to be .NET Framework 1.1
code (I'm using vstudio 2003), but somehow when the control gets hosted
on IE on my machine, it runs in a .NET Framework 2.0 environment. I
have no idea how this happens, but I was able to figure this out when I
looked at the error details and noticed on the "Loaded Assemblies"
part, that all the loaded assemblies, including mscorlib are version
2.0. I've got both versions of the platform installed on my machine,
but shouldn't the assemblies for version 1.1 be loaded instead of the
ones from version 2.0 ?

The funny thing is that I tried accessing my web page from a machine
with only .net 1.1 and there was a different error: "Security Error" or
something like that.

Now that I think of this, this may actually explain why the code groups
I created are having no effect at all, because they were created on the
..net framework 1.1 configuration tool and the assemblies loaded are all
2.0. My question is, how do you configure code zones on version 2.0 of
the platform ? I see no configuration tool on my administrative tools
for version 2.0... :(

The second thing that I noticed about my problem is the time when the
security exception is thrown. According to the displayed call stack, it
happens when calling

System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg,
IntPtr wparam, IntPtr lparam)

(I think this is probably called when I do a P/Invoke on
CryptUIDlgSelectCertificateFromStore)

According to the details of the error:

The action that failed was:
LinkDemand
The Zone of the assembly that failed was:
Trusted

I have no idea what this means but some of you guys might...

Thanks once again for all the help you've been giving me.

jm

[quoted text, click to view]