#title(.NETプログラミング研究 第18号)

#navi(.NET プログラミング研究)

#contents

*.NETプログラミング研究 第18号 [#a310e0df]

**.NET質問箱 [#t6390304]

***公開鍵暗号による暗号化 [#re786320]

#column(注意){{
この記事の最新版は「[[公開鍵暗号方法で暗号化する>http://dobon.net/vb/dotnet/string/rsaencryption.html]]」で公開しています。
}}

前回は「秘密鍵暗号方式による暗号化」について説明しましたが、今回は公開鍵暗号(非対称暗号化方式、公開キー暗号方式)により暗号化、復号化する方法を紹介します。

公開鍵暗号とは、公開鍵と秘密鍵(個人鍵)の2つの鍵を使って暗号化、復号化する方法です。公開鍵方式に関して「アスキーデジタル用語辞典」では次のように説明されています。

「暗号化専用の鍵(公開鍵)と解読専用の鍵(個人鍵)を使って、暗号化と解読を行なう形式。受信側で事前に公開鍵と個人鍵のペアを用意し、暗号文の送信側に公開鍵のほうを配布する。送信側は平文を公開鍵で暗号文に変換できるが、解読はできない。受信側は、個人鍵で平文に復元可能。公開鍵だけでは解読できないという利点がある。」

-[[アスキーデジタル用語辞典>http://yougo.ascii24.com/]]

.NETでは公開キー暗号化アルゴリズムを実装するクラスとして、DSACryptoServiceProvider(DSA)とRSACryptoServiceProvider(RSA)の2つが用意されていますが、ここではRSACryptoServiceProviderを使った例のみを示します。なおDSA、RSAとは何かということ関しては次のURLが参考になります。

-[[通信用語の基礎知識 - DSA>http://www.wdic.org/?word=DSA]]
-[[通信用語の基礎知識 - RSA>http://www.wdic.org/?word=RSA]]

次に示すサンプルは、公開鍵暗号により文字列を暗号化、復号化するため3つの静的メソッドからなります。まずCreateKeysメソッドで公開鍵と秘密鍵のペアを作成し、その公開鍵を使ってEncryptメソッドにより暗号化します。復号化は秘密鍵を指定してDecryptメソッドにより行います。

暗号化にはRSACryptoServiceProvider.Encryptメソッドを使用しますが、この時渡す暗号化されるデータにはサイズの制限があります。OAEPパディング、Direct Encryption、ともにサポートなしのそれぞれのケースで暗号化されるデータの最大長の計算法が異なり、このことはヘルプ(「RSACryptoServiceProvider.Encrypt メソッド」)で説明されています。

-[[RSACryptoServiceProvider.Encrypt メソッド>http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemsecuritycryptographyrsacryptoserviceproviderclassencrypttopic.asp]]

例えばDirect Encryption(高度暗号化パックがインストールされている Microsoft Windows 2000 以降)の場合、RSACryptoServiceProvider.KeySize プロパティから取得できるキーサイズが1024ビットだとすると、暗号化されるデータの最大長は、 1024/8-11=117バイト となります。

下のコードでは暗号化されるデータの最大長のチェックは全く行っていませんので、ご注意ください。

なお下の例の「RSACryptoServiceProviderオブジェクトの作成」の部分についてですが、詳しくは「マイクロソフト サポート技術情報 - 322371」をご覧ください。

-[[マイクロソフト サポート技術情報 - 322371>http://support.microsoft.com/default.aspx?scid=kb;en-us;Q322371]]

#code(csharp){{
/// <summary>
/// 公開鍵と秘密鍵を作成して返す
/// </summary>
/// <param name="publicKey">作成された公開鍵(XML形式)</param>
/// <param name="privateKey">作成された秘密鍵(XML形式)</param>
public static void CreateKeys(out string publicKey, out string privateKey)
{
    //RSACryptoServiceProviderオブジェクトの作成
    //(Web Service,ASP Page,COM+の時は次のように
    //UseMachineKeyStoreを指定する)
    System.Security.Cryptography.CspParameters CSPParam =
        new System.Security.Cryptography.CspParameters();
    CSPParam.Flags =
        System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore;
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider(CSPParam);

    //公開鍵をXML形式で取得
    publicKey = rsa.ToXmlString(false);
    //秘密鍵をXML形式で取得
    privateKey = rsa.ToXmlString(true);
}

/// <summary>
/// 公開鍵を使って文字列を暗号化する
/// </summary>
/// <param name="str">暗号化する文字列</param>
/// <param name="publicKey">暗号化に使用する公開鍵(XML形式)</param>
/// <returns>暗号化された文字列</returns>
public static string Encrypt(string str, string publicKey)
{
    //RSACryptoServiceProviderオブジェクトの作成
    System.Security.Cryptography.CspParameters CSPParam =
        new System.Security.Cryptography.CspParameters();
    CSPParam.Flags =
        System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore;
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider(CSPParam);

    //公開鍵を指定
    rsa.FromXmlString(publicKey);

    //暗号化する文字列をバイト配列に
    byte[] data = System.Text.Encoding.UTF8.GetBytes(str);
    //暗号化する
    //(XP以降の場合のみ2項目にTrueを指定し、OAEPパディングを使用できる)
    byte[] encryptedData = rsa.Encrypt(data, false);

    //Base64で結果を文字列に変換
    return System.Convert.ToBase64String(encryptedData);
}

/// <summary>
/// 秘密鍵を使って文字列を復号化する
/// </summary>
/// <param name="str">Encryptメソッドにより暗号化された文字列</param>
/// <param name="privateKey">復号化に必要な秘密鍵(XML形式)</param>
/// <returns>復号化された文字列</returns>
public static string Decrypt(string str, string privateKey)
{
    //RSACryptoServiceProviderオブジェクトの作成
    System.Security.Cryptography.CspParameters CSPParam =
        new System.Security.Cryptography.CspParameters();
    CSPParam.Flags =
        System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore;
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider(CSPParam);

    //秘密鍵を指定
    rsa.FromXmlString(privateKey);

    //復号化する文字列をバイト配列に
    byte[] data = System.Convert.FromBase64String(str);
    //復号化する
    byte[] decryptedData = rsa.Decrypt(data, false);

    //結果を文字列に変換
    return System.Text.Encoding.UTF8.GetString(decryptedData);
}
}}

#code(vbnet){{
'C#のコードを'C# to VB.NET Translator'で変換し、修正したコードです
'http://www.aspalliance.com/aldotnet/examples/translate.aspx
'
'/ <summary>
'/ 公開鍵と秘密鍵を作成して返す
'/ </summary>
'/ <param name="publicKey">作成された公開鍵(XML形式)</param>
'/ <param name="privateKey">作成された秘密鍵(XML形式)</param>
Public Shared Sub CreateKeys(ByRef publicKey As String, _
                            ByRef privateKey As String)
    'RSACryptoServiceProviderオブジェクトの作成
    '(Web Service,ASP Page,COM+の時は次のように
    'UseMachineKeyStoreを指定する)
    Dim CSPParam As New System.Security.Cryptography.CspParameters
    CSPParam.Flags = _
        System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore
    Dim rsa As New _
        System.Security.Cryptography.RSACryptoServiceProvider(CSPParam)

    '公開鍵をXML形式で取得
    publicKey = rsa.ToXmlString(False)
    '秘密鍵をXML形式で取得
    privateKey = rsa.ToXmlString(True)
End Sub

'/ <summary>
'/ 公開鍵を使って文字列を暗号化する
'/ </summary>
'/ <param name="str">暗号化する文字列</param>
'/ <param name="publicKey">暗号化に使用する公開鍵(XML形式)</param>
'/ <returns>暗号化された文字列</returns>
Public Shared Function Encrypt(ByVal str As String, _
                            ByVal publicKey As String) As String
    'RSACryptoServiceProviderオブジェクトの作成
    Dim CSPParam As New System.Security.Cryptography.CspParameters
    CSPParam.Flags = _
        System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore
    Dim rsa As New _
        System.Security.Cryptography.RSACryptoServiceProvider(CSPParam)

    '公開鍵を指定
    rsa.FromXmlString(publicKey)

    '暗号化する文字列をバイト配列に
    Dim data As Byte() = System.Text.Encoding.UTF8.GetBytes(str)
    '暗号化する
    '(XP以降の場合のみ2項目にTrueを指定し、OAEPパディングを使用できる)
    Dim encryptedData As Byte() = rsa.Encrypt(data, False)

    'Base64で結果を文字列に変換
    Return System.Convert.ToBase64String(encryptedData)
End Function

'/ <summary>
'/ 秘密鍵を使って文字列を復号化する
'/ </summary>
'/ <param name="str">Encryptメソッドにより暗号化された文字列</param>
'/ <param name="privateKey">復号化に必要な秘密鍵(XML形式)</param>
'/ <returns>復号化された文字列</returns>
Public Shared Function Decrypt(ByVal str As String, _
                            ByVal privateKey As String) As String
    'RSACryptoServiceProviderオブジェクトの作成
    Dim CSPParam As New System.Security.Cryptography.CspParameters
    CSPParam.Flags = _
        System.Security.Cryptography.CspProviderFlags.UseMachineKeyStore
    Dim rsa As New _
        System.Security.Cryptography.RSACryptoServiceProvider(CSPParam)

    '秘密鍵を指定
    rsa.FromXmlString(privateKey)

    '復号化する文字列をバイト配列に
    Dim data As Byte() = System.Convert.FromBase64String(str)
    '復号化する
    Dim decryptedData As Byte() = rsa.Decrypt(data, False)

    '結果を文字列に変換
    Return System.Text.Encoding.UTF8.GetString(decryptedData)
End Function
}}

-[[公開鍵暗号による暗号化>http://dobon.net/vb/dotnet/string/rsaencryption.html]]

**.NET質問箱 [#af039df6]

***フォームのイメージを印刷するには? [#o885893c]

#column(注意){{
この記事の最新版は「[[フォームのイメージを印刷する>http://dobon.net/vb/dotnet/vb6/printform.html]]」で公開しています。
}}

''質問:''

Visual Basic 6.0ではPrintFormというメソッドがあり、これによりフォームのイメージを印刷することが出来ましたが、同じようなことを.NETで行うにはどのようにすればよいのでしょうか?

''答え:''

VB6のPrintFormメソッドに代わるものは.NETにはありません。ヘルプによると、

「Visual Basic 6.0 では、フォームのイメージをプリンタに送るとき、フォームの PrintForm メソッドが使用されていました。PrintForm メソッドは Visual Basic .NET ではサポートされていません。PrintForm メソッドの結果は、画面の解像度およびプリンタの解像度に依存するところが大きく、印刷の方法としては推奨されていませんでした。PrintForm メソッドの機能を複製する必要がある場合は、サードパーティ製のグラフィックス ツールに用意されているスクリーン キャプチャ機能を自動化してフォームのイメージをキャプチャおよび印刷できます。」

とあります。

-[[Visual Basic .NET における印刷の変更点>http://www.microsoft.com/japan/msdn/library/ja/Vbcon/html/vbconprintingchangesinvisualbasic70.asp]]

それではサードパーティ製のスクリーンキャプチャ機能を使うしかないかというとそうではなく、Win32 APIでスクリーンキャプチャを行う方法があり、しかもそれは現在ではヘルプに載っています。

ただし、VB6のPrintFormメソッドがフォームの一部が画面に表示されていない場合でもフォーム全体が印刷されるのに対して、この方法では表示されていない部分は印刷されません。

-[[Code: Printing a Windows Form (Visual Basic)>http://msdn.microsoft.com/library/en-us/dv_vbCode/html/vbtskCodeExamplePrintingForm.asp]]
-[[Code: Displaying Print Preview for a Windows Form (Visual Basic)>http://msdn.microsoft.com/library/en-us/dv_vbCode/html/vbtskCodeExamplePrintPreviewingForm.asp]]
-[[MSDN:コード : Windows フォームを印刷する (Visual Basic)]]
-[[MSDN:コード : Windows フォームの印刷プレビューを表示する (Visual Basic)]]

以下にこのサンプルコードを書き直した例を示します。使い方としては、自分自身のフォームを印刷する時、C#では

 PrintForm(this);

VB.NETでは

 PrintForm(Me)

のようにしてください。

#code(csharp){{
/// <summary>
/// フォームのイメージを印刷する
/// </summary>
/// <param name="frm">イメージを印刷するフォーム</param>
public void PrintForm(Form frm)
{
     //フォームのイメージを取得する
     CaptureScreen(frm);
     //フォームのイメージを印刷する
     System.Drawing.Printing.PrintDocument PrintDocument1 = 
          new System.Drawing.Printing.PrintDocument();
     PrintDocument1.PrintPage += 
          new System.Drawing.Printing.PrintPageEventHandler(
          PrintDocument1_PrintPage);
     PrintDocument1.Print();
}

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest, 
     int nXDest, int nYDest, int nWidth, int nHeight, 
     IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);

//フォームのイメージを保存する変数
private Bitmap memoryImage;

//フォームのイメージを取得する
private void CaptureScreen(Form frm)
{
     Graphics mygraphics = frm.CreateGraphics();
     Size s = frm.Size;
     memoryImage = new Bitmap(s.Width, s.Height, mygraphics);
     Graphics memoryGraphics = Graphics.FromImage(memoryImage);
     IntPtr dc1 = mygraphics.GetHdc();
     IntPtr dc2 = memoryGraphics.GetHdc();
     BitBlt(dc2, 0, 0, frm.ClientRectangle.Width, 
          frm.ClientRectangle.Height, dc1, 0, 0, 13369376);
     mygraphics.ReleaseHdc(dc1);
     memoryGraphics.ReleaseHdc(dc2);
}

//PrintDocument1のPrintPageイベントハンドラ
private void PrintDocument1_PrintPage(object sender, 
     System.Drawing.Printing.PrintPageEventArgs e)
{
     e.Graphics.DrawImage(memoryImage, 0, 0);
}
}}

#code(vbnet){{
'/ <summary>
'/ フォームのイメージを印刷する
'/ </summary>
'/ <param name="frm">イメージを印刷するフォーム</param>
Public Sub PrintForm(ByVal frm As Form)
    'フォームのイメージを取得する
    CaptureScreen(frm)
    'フォームのイメージを印刷する
    Dim PrintDocument1 As New System.Drawing.Printing.PrintDocument
    AddHandler PrintDocument1.PrintPage, _
        AddressOf PrintDocument1_PrintPage
    PrintDocument1.Print()
End Sub

<System.Runtime.InteropServices.DllImport("gdi32.dll")> _
Private Shared Function BitBlt(ByVal hdcDest As IntPtr, _
    ByVal nXDest As Integer, ByVal nYDest As Integer, _
    ByVal nWidth As Integer, ByVal nHeight As Integer, _
    ByVal hdcSrc As IntPtr, _
    ByVal nXSrc As Integer, ByVal nYSrc As Integer, _
    ByVal dwRop As Integer) As Boolean
End Function

'フォームのイメージを保存する変数
Private memoryImage As Bitmap

'フォームのイメージを取得する
Private Sub CaptureScreen(ByVal frm As Form)
    Dim mygraphics As Graphics = frm.CreateGraphics()
    Dim s As Size = frm.Size
    memoryImage = New Bitmap(s.Width, s.Height, mygraphics)
    Dim memoryGraphics As Graphics = Graphics.FromImage(memoryImage)
    Dim dc1 As IntPtr = mygraphics.GetHdc()
    Dim dc2 As IntPtr = memoryGraphics.GetHdc()
    BitBlt(dc2, 0, 0, _
        frm.ClientRectangle.Width, frm.ClientRectangle.Height, _
        dc1, 0, 0, 13369376)
    mygraphics.ReleaseHdc(dc1)
    memoryGraphics.ReleaseHdc(dc2)
End Sub

'PrintDocument1のPrintPageイベントハンドラ
Private Sub PrintDocument1_PrintPage(ByVal sender As Object, _
        ByVal e As System.Drawing.Printing.PrintPageEventArgs)
    e.Graphics.DrawImage(memoryImage, 0, 0)
End Sub
}}

***GotFocusとLostFocusイベントはどこにいったか? [#j415734e]

#column(注意){{
この記事の最新版は「[[GotFocusとLostFocusイベントはどこに行った?>http://dobon.net/vb/dotnet/vb6/gotfocusevent.html]]」で公開しています。
}}

''質問:''

VB6ではコントロールがフォーカスを受け取った時にGotFocusイベントが、フォーカスを失った時にLostFocusイベントが発生しましたが、.NETではこれらのイベントが見つかりません。.NETではどのようにすればよいのでしょうか?

''答え:''

.NETでもVB6と同様にGotFocusイベント及びLostFocusイベントは存在しています。しかしVisual Studio .NETでは、VB.NETの場合はコードエディタ上の「メソッド名ボックス」に、C#の場合はプロパティウィンドウの「イベント」にGotFocusとLostFocusが表示されません。

GotFocusイベントとLostFocusイベントについて、ヘルプには次のように書いてあります。

「GotFocus イベントおよび LostFocus イベントは、WM_KILLFOCUS Windows メッセージおよび WM_SETFOCUS Windows メッセージに結び付けられた、低水準のフォーカス イベントです。一般的に、 GotFocus イベントおよび LostFocus イベントは、 UICues を更新するときにだけ使用されます。 Activated イベントおよび Deactivate イベントを使用する Form クラス以外のすべてのコントロールに対して、 Enter イベントおよび Leave イベントを使用する必要があります。」

このことから推測すると、GotFocus及びLostFocusイベントの代わりに、Enter及びLeaveイベントを使うべきという意味でこれらが表示されないのかもしれません。

-[[GotFocus イベント>http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemwindowsformscontrolclassgotfocustopic.asp]]
-[[LostFocus イベント>http://www.microsoft.com/japan/msdn/library/ja/cpref/html/frlrfsystemwindowsformscontrolclasslostfocustopic.asp]]

また、ヘルプの「Visual Basic .NET における TextBox コントロールの変更点」では、VB6のGotFocus及びLostFocusイベントに代わるものは、.NETではEnter及びLeaveイベントとなっています。

-[[Visual Basic .NET における TextBox コントロールの変更点>http://www.microsoft.com/japan/msdn/library/ja/Vbcon/html/vxconChangesToTextBoxControlInVisualBasicNET.asp]]

以下にLostFocusイベントを使ったコードを一応書いておきます。

#code(csharp){{
private void Form1_Load(object sender, System.EventArgs e)
{
     //TextBox1のLostFocusイベントハンドラを追加する
     TextBox1.LostFocus += new EventHandler(TextBox1_LostFocus);
}

//LostFocusイベントハンドラ
private void TextBox1_LostFocus(object sender, EventArgs e)
{
     Console.WriteLine("LostFocusイベントが発生しました。");
}
}}

#code(vbnet){{
Private Sub Form1_Load(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load
    'TextBox1のLostFocusイベントハンドラを追加する
    AddHandler TextBox1.LostFocus, AddressOf TextBox1_LostFocus
End Sub

'LostFocusイベントハンドラ
Private Sub TextBox1_LostFocus(ByVal sender As Object, _
        ByVal e As EventArgs)
    Console.WriteLine("LostFocusイベントが発生しました。")
End Sub
}}

-[[どぼん!のVB道掲示板 - focusイベントについて>http://www.sky-j.com/viewlist.php?arg_forum_id=38&arg_thread_id=825&arg_message_id=825]]

**コメント [#a5639586]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]],2003-10-07 (火) 06:00:00,DOBON!,2010-03-21 (日) 00:57:14,DOBON!)
[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]