上下文:
我目前正在为 Windows 平板电脑创建 WPF 应用程序。此应用程序应取代书面工作。目前的纸质表格是一张纸,一名军人在上面写下他在工作上花费的时间、他使用的材料以及他在那里所做的事情。工作完成后,他将这张表格展示给客户,客户在上面签字。该公司希望将其数字化,我一直在编写用于捕获签名的用户控件。我以本教程为例http://msdn.microsoft.com/en-us/library/aa480678.aspx。虽然它是在 vb 中,但我在 C# 中使用了相同的加密机制,美国政府(虽然我来自荷兰,适用相同的规则,所以我想我会参考英文的内容)关于数字签名的内容如下: http://usgovinfo.about.com/library/bills/bldigitalsigs.htm 存储客户签名的所有数据,存储在序列化数据类中(使用 MVVM 模式,因此很容易将所有数据放在一起)
我已经放弃了一些关于数字签名的研究,但我读的越多,我就越害怕我无法让它成为我自己。
问题: 我如何获得有效证书以确保签名有效。
这实际上是第二个部分问题..我如何使用该签名加密我的数据,所以没有关于该数据被签名的问题
一些代码: 加密方法:
public void Encrypt(InkSecureSignatureData signatureData)
{
try
{
// Block sizes and buffer for stream operations.
const int SMALLEST = 86;
const int BLOCK = 128;
byte[] Buffer = new byte[SMALLEST];
// Variables for Ink data.
StrokeCollection CopyOfSourceInk = new StrokeCollection();
StrokeCollection WashedInk = new StrokeCollection();
// Serialized signature objects.
System.IO.MemoryStream SourceStream = new System.IO.MemoryStream();
System.IO.MemoryStream EncryptedStream = new System.IO.MemoryStream();
signatureData.BiometricEncryptionSubmittedOn = DateTime.Now;
// Store the machine name in the HardwareInfo property.
signatureData.HardwareInfo = Environment.MachineName;
// Create a working copy of the SignatureData's ink.
using (MemoryStream ms = new MemoryStream(signatureData.InkSecureSignature))
{
CopyOfSourceInk = new System.Windows.Ink.StrokeCollection(ms);
ms.Close();
}
//CopyOfSourceInk.Load(signatureData.InkSecureSignature);
// Wash each Stroke by using GetFlattenedBezierPoints
// to remove all pressure information.
foreach (Stroke Stroke in CopyOfSourceInk)
{
//WashedInk.CreateStroke(Stroke.GetFlattenedBezierPoints(500));
WashedInk.Add(new Stroke(Stroke.GetBezierStylusPoints()));
}
//signatureData.InkWashedSignature = WashedInk.Save(PersistenceFormat.InkSerializedFormat, CompressionMode.Default);
byte[] signature;
using (MemoryStream ms = new MemoryStream())
{
WashedInk.Save(ms);
signature = ms.ToArray();
}
signatureData.InkWashedSignature = signature;
// Create a key and establish RSAKeyInfo.
byte[] PublicKey = {//some huge as byte array which i'm not gonna add here}
byte[] Exponent = {
1,
0,
1
};
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = PublicKey;
RSAKeyInfo.Exponent = Exponent;
System.Security.Cryptography.RSACryptoServiceProvider RSA = new System.Security.Cryptography.RSACryptoServiceProvider();
RSA.ImportParameters(RSAKeyInfo);
// Serialize the signature.
System.Xml.Serialization.XmlSerializer Serializer = new System.Xml.Serialization.XmlSerializer(typeof(InkSecureSignatureData));
Serializer.Serialize(SourceStream, signatureData);
// Cycle through the in-memory stream and encrypt it.
SourceStream.Position = 0;
while ((SourceStream.Read(Buffer, 0, SMALLEST) >= SMALLEST))
{
if ((SourceStream.Position < SourceStream.Length))
{
EncryptedStream.Write(RSA.Encrypt(Buffer, true), 0, BLOCK);
}
}
// Handle the remaining bytes in the stream.
long Amount = SourceStream.Length % SMALLEST;
byte[] Remaining = new byte[Amount];
Array.Copy(Buffer, Remaining, Amount);
EncryptedStream.Write(RSA.Encrypt(Remaining, true), 0, BLOCK);
// Place the encrypted data in the InkSecureSignatureData object.
signatureData.EncryptedBiometricData = EncryptedStream.ToArray();
signatureData.BiometricEncryptionCompletedOn = DateTime.Now;
// Blank out the original signature to prevent expropriation.
signatureData.InkSecureSignature = null;
}
catch (Exception ex)
{
throw ex;
}
}
}
我收集签名的部分类:
public partial class DrawSignatureScreen : Window
{
// Locks the signature.
private bool signatureReadOnly;
// The caption for the signer's name.
private string signersNameCaptionValue = "Signer's Name: ";
/// <summary>
/// Occurs when the signature has been fully signed.
/// </summary>
/// <param name="sender">The source InkSecureSignature object for this event.</param>
/// <param name="e">The EventArgs object that contains the event data.</param>
public event SignedEventHandler Signed;
public delegate void SignedEventHandler(object sender, EventArgs e);
public DrawSignatureScreen()
{
InitializeComponent();
signatureInkCanvas.StrokeCollected += signatureInkOverlay_Stroke;
}
/// <summary>
/// Gets or sets the override to the default "Signer's Name:" label caption.
/// </summary>
public string SignersNameCaption
{
get
{
return signersNameCaptionValue;
}
set
{
signersNameCaptionValue = value;
signersNameLabel.Content = signersNameCaptionValue;
}
}
/// <summary>
/// Gets or sets whether the signature has been completed.
/// </summary>
/// <remarks>
/// After the signature is accepted, this property is true, and
/// it cannot be changed back to false. This would enable the
/// modification of the signature after acceptance.
/// </remarks>
public bool SignatureComplete
{
get
{
return signatureReadOnly;
}
set
{
// If the signature is already accepted, then exit.
if ((signatureReadOnly == true) | (value == signatureReadOnly))
{
return;
}
// Because we got this far, Value is True,
// so lock all controls and disable Ink collection.
acceptButton.Visibility = Visibility.Hidden;
if ((signatureInkCanvas != null))
{
signatureInkCanvas.IsEnabled = false;
}
signersNameTextBox.Visibility = Visibility.Hidden;
// Set the signer's name label control to the current caption for
// the signer's name plus the actual signer's name.
signersNameLabel.Content = signersNameCaptionValue + (this.DataContext as InkSecureSignatureData).SignersName;
// Set the read-only property value.
signatureReadOnly = value;
cancelButton.Content = "OK";
}
}
//
// SetDefaultDrawingAttributes
//
// Set the default drawing attributes for ink collection.
//
// Parameters:
// color - The desired ink color.
//
private void SetDefaultDrawingAttributes(System.Drawing.Color color)
{
var _with1 = signatureInkCanvas.DefaultDrawingAttributes;
// Color.
_with1.Color = System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B);
// Smooth.
//_with1.AntiAliased = true;
_with1.FitToCurve = true;
// Set to not round (modify) the Stroke.
//_with1.PenTip = PenTip.Ball;
_with1.StylusTip = StylusTip.Ellipse;
// Ball Point.
_with1.Width = 2;
// Size.
}
// signatureInkOverlay_Stroke
//
// On the first Stroke, set the timestamp and button state.
//
// Parameters:
// sender - The source InkOverlay object for this event.
// e - The InkCollectorStrokeEventArgs object that contains the event data.
//
private void signatureInkOverlay_Stroke(object sender, InkCanvasStrokeCollectedEventArgs e)
{
// First, check to ensure that this is the first Stroke, otherwise exit.
if (signatureInkCanvas.Strokes.Count > 1)
{
return;
}
// Set the Acquired Signature Start On to Now.
(this.DataContext as InkSecureSignatureData).AcquiredSignatureStartOn = DateTime.Now;
// Enable the clear button.
clearButton.IsEnabled = true;
// Call ValidateData to see if all of the required
// criteria has been met to "accept" the signature.
ValidateData();
}
//
// signersNameTextBox_TextChanged
//
// Occurs when the Text property value changes.
//
// Parameters:
// sender - The source TextBox object for this event.
// e - The EventArgs object that contains the event data.
//
private void signersNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
// Assign the signer's name from the text box to the underlying data object.
(this.DataContext as InkSecureSignatureData).SignersName = signersNameTextBox.Text;
ValidateData();
}
//
// clearButton_Click
//
// Occurs when the Button is clicked to clear the signature in process.
//
// Parameters:
// sender - The source Button object for this event.
// e - The EventArgs object that contains the event data.
//
private void clearButton_Click(object sender, RoutedEventArgs e)
{
SignatureComplete = false;
signatureReadOnly = false;
// Delete the Strokes collection.
signatureInkCanvas.Strokes.Clear();
// Disable the clear button.
clearButton.IsEnabled = false;
signersNameTextBox.Text = "";
signersNameLabel.Content = "Signer's name:";
signatureInkCanvas.IsEnabled = true;
SetDefaultDrawingAttributes(Color.Black);
// Revalidate the data.
ValidateData();
}
/// <summary>
/// Prints the signature.
/// </summary>
/// <param name="graphics">The Graphics context to print to.</param>
/// <param name="topLeftPoint">The top left corner of the print area.</param>
public void Print(Graphics graphics, System.Drawing.Point topLeftPoint)
{
// Starting locations.
int Indentation = 5;
int BottomLineY = 17;
int VerticalLocation = (int)topLeftPoint.Y;
// Specify a bordered print area slightly smaller than the control.
Rectangle ThisRect = new Rectangle(topLeftPoint.X, topLeftPoint.Y, 800, 281);
Color BorderColor = Color.FromArgb(255, 0, 45, 150);
Microsoft.Ink.Renderer Renderer = new Microsoft.Ink.Renderer();
var _with2 = graphics;
_with2.FillRectangle(Brushes.White, ThisRect);
_with2.DrawRectangle(new Pen(BorderColor), ThisRect);
// Draw the bottom line.
_with2.DrawLine(Pens.Black, Indentation, ThisRect.Height - BottomLineY, ThisRect.Width - (2 * Indentation), ThisRect.Height - BottomLineY);
if (SignatureComplete == false)
{
// Draw a blank signature line.
_with2.DrawString("Signed: ", new Font(new System.Drawing.FontFamily("arial"),10f), new SolidBrush(Color.Black), ThisRect.Left + Indentation, ThisRect.Height - BottomLineY + 1);
}
else
{
// Draw header text and washed Ink.
_with2.DrawString("RSA Encrypted Digital Biometric Signature", new Font(new System.Drawing.FontFamily("arial"), 10f), new SolidBrush(Color.Blue), ThisRect.Left + 3, VerticalLocation + 3);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.CompositingMode = CompositingMode.SourceOver;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
StrokeCollection sc = signatureInkCanvas.Strokes;
byte[] inkData = null;
using (MemoryStream inkMemStream = new MemoryStream())
{
sc.Save(inkMemStream);
inkData = inkMemStream.ToArray();
}
Ink ink = new Ink();
ink.Load(inkData);
Microsoft.Ink.DrawingAttributes da = new Microsoft.Ink.DrawingAttributes(Color.Black);
da.AntiAliased = true;
da.FitToCurve = false;
da.RasterOperation = RasterOperation.Black;
foreach (Microsoft.Ink.Stroke Stroke in ink.Strokes)
{
Renderer.Draw(graphics, Stroke, da);
}
_with2.DrawString("Signed By: " + (this.DataContext as InkSecureSignatureData).SignersName.ToString() + " on " + (this.DataContext as InkSecureSignatureData).SignerAcceptedOn.ToString(), new Font(new System.Drawing.FontFamily("arial"), 10f), new SolidBrush(Color.Blue), ThisRect.Left + Indentation, ThisRect.Height - BottomLineY + 1);
}
}
//public void Reset()
//{
// (this.DataContext as Collection).Signatures = "<InkSecureSignatureData/>";
//}
//
// acceptButton_Click
//
// Occurs when the Button is clicked to lock the signature.
//
// Parameters:
// sender - The source Button object for this event.
// e - The EventArgs object that contains the event data.
//
private void acceptButton_Click(System.Object sender, System.EventArgs e)
{
var _with3 = (this.DataContext as InkSecureSignatureData);
// Save the serialized Ink to the SignatureData.InkSecureSignature property
// for encryption by the Biometric Encryption Provider for Ink.
MemoryStream ms = new MemoryStream();
signatureInkCanvas.Strokes.Save(ms);
_with3.InkSecureSignature = ms.ToArray();
_with3.SignerAcceptedOn = DateTime.Now;
BiometricEncryptionProviderForInk BiometricEncryptionProvider = new BiometricEncryptionProviderForInk();
try
{
// Wash and encrypt the signature data.
BiometricEncryptionProvider.Encrypt((this.DataContext as InkSecureSignatureData));
// Stop collecting Ink and show the washed Ink.
this.SignatureComplete = true;
//CreateNewInkCanvas();
SetDefaultDrawingAttributes(Color.Black);
ms = new MemoryStream((this.DataContext as InkSecureSignatureData).InkWashedSignature);
signatureInkCanvas.Strokes = new StrokeCollection(ms);
System.Drawing.Bitmap signatureBitmap = new System.Drawing.Bitmap(803, 284, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
//Create a graphics context from that bitmap image.
Graphics graphics = System.Drawing.Graphics.FromImage(signatureBitmap);
// Print the InkSecureSignature to the bitmap.
Print(graphics, new System.Drawing.Point(1, 1));
ImageConverter converter = new ImageConverter();
_with3.Signature = (byte[])converter.ConvertTo(signatureBitmap, typeof(byte[]));
// Clean up.
graphics.Dispose();
//TODO remove when done with testing
signatureBitmap.Save("test.Jpeg", ImageFormat.Jpeg);
// Tell the calling form that the control is done processing.
if (Signed != null)
{
Signed(this, new EventArgs());
}
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
}
//
// ValidateData
//
// Sets the Accept button's state depending on the presence of required inputs.
//
private void ValidateData()
{
acceptButton.IsEnabled = (signatureInkCanvas.Strokes.Count > 0) && (signersNameTextBox.Text.Length > 0);
if (!acceptButton.IsEnabled)
{
acceptButton.Visibility = Visibility.Visible;
signersNameTextBox.Visibility = Visibility.Visible;
}
}
}
InkSecureSignatureData
我用来存储从签名中收集的所有(加密)数据的类中的字段:
private System.DateTime mAcquiredSignatureStartOn;
private System.DateTime mBiometricEncryptionSubmittedOn;
private System.DateTime mBiometricEncryptionCompletedOn;
private byte[] mEncryptedBiometricData;
private string mHardwareInfo;
private byte[] mInkWashedSignature;
private byte[] mInkSignature;
private System.DateTime mSignerAcceptedOn;
private byte[] signatureBitmap;
private string mSignersName;
最后,我有一个名为“Collection”的类,它存储所有输入的数据,它还存储还包含该类的类InkSecureSignatureData
。
我希望我已经提供了足够的信息,如果有不清楚的地方请询问,我会尽力回答。