DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

デジタル署名を作成、検証する

ここでは、「デジタル署名」を行う方法を紹介します。デジタル署名は、公開鍵暗号方式を使って電子署名を実現する方法で、デジタル署名を使うことにより、そのデータを作成したのが本人であることを証明できたり、データが改ざんされていないことを確認できたりします。デジタル署名について詳しくは、「デジタル署名:ITpro」などをご覧ください。

.NET Frameworkにはデジタル署名を行うためのクラスとして、DSACryptoServiceProviderクラス、RSACryptoServiceProviderクラス、ECDsaCngクラス(.NET Framework 3.5以降)が用意されています。DSACryptoServiceProviderクラスはDSAを、RSACryptoServiceProviderクラスはRSAを、ECDsaCngクラスはECDSAを使用します。ここでは、RSACryptoServiceProviderクラスを使った例を紹介します。

サンプルコード

早速ですが、デジタル署名を作成、検証するメソッドの例を以下に示します。ここでは文字列のデータ(メッセージ)に付けるデジタル署名を作成するためのメソッドと、検証するためのメソッドを作成しています。

VB.NET
コードを隠すコードを選択
''' <summary>
''' デジタル署名を作成する
''' </summary>
''' <param name="message">署名を付けるメッセージ</param>
''' <param name="privateKey">署名に使用する秘密鍵</param>
''' <returns>作成された署名</returns>
Public Shared Function CreateDigitalSignature(ByVal message As String, _
                                              ByVal privateKey As String) As String
    'メッセージをバイト型配列にして、SHA1ハッシュ値を計算
    Dim msgData As Byte() = System.Text.Encoding.UTF8.GetBytes(message)
    Dim sha As New System.Security.Cryptography.SHA1Managed()
    Dim hashData As Byte() = sha.ComputeHash(msgData)

    'RSACryptoServiceProviderオブジェクトの作成
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider()
    '秘密鍵を使って初期化
    rsa.FromXmlString(privateKey)

    'RSAPKCS1SignatureFormatterオブジェクトを作成
    Dim rsaFormatter As New  _
        System.Security.Cryptography.RSAPKCS1SignatureFormatter(rsa)
    '署名の作成に使用するハッシュアルゴリズムを指定
    rsaFormatter.SetHashAlgorithm("SHA1")

    '署名を作成
    Dim signedValue As Byte() = rsaFormatter.CreateSignature(hashData)

    'バイト型配列を文字列に変換して返す
    Return System.Convert.ToBase64String(signedValue)
End Function

''' <summary>
''' デジタル署名を検証する
''' </summary>
''' <param name="message">署名の付いたメッセージ</param>
''' <param name="signature">署名</param>
''' <param name="publicKey">送信者の公開鍵</param>
''' <returns>認証に成功した時はTrue。失敗した時はFalse。</returns>
Public Shared Function VerifyDigitalSignature(ByVal message As String, _
                                              ByVal signature As String, _
                                              ByVal publicKey As String) As Boolean
    'メッセージをバイト型配列にして、SHA1ハッシュ値を計算
    Dim msgData As Byte() = System.Text.Encoding.UTF8.GetBytes(message)
    Dim sha As New System.Security.Cryptography.SHA1Managed()
    Dim hashData As Byte() = sha.ComputeHash(msgData)

    'RSACryptoServiceProviderオブジェクトの作成
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider()
    '公開鍵を使って初期化
    rsa.FromXmlString(publicKey)

    Dim rsaDeformatter As New  _
        System.Security.Cryptography.RSAPKCS1SignatureDeformatter(rsa)
    '署名の検証に使用するハッシュアルゴリズムを指定
    rsaDeformatter.SetHashAlgorithm("SHA1")

    '署名を検証し、結果を返す
    Return rsaDeformatter.VerifySignature( _
        hashData, System.Convert.FromBase64String(signature))
End Function

''' <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オブジェクトの作成
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider()

    '公開鍵をXML形式で取得
    publicKey = rsa.ToXmlString(False)
    '秘密鍵をXML形式で取得
    privateKey = rsa.ToXmlString(True)
End Sub
C#
コードを隠すコードを選択
/// <summary>
/// デジタル署名を作成する
/// </summary>
/// <param name="message">署名を付けるメッセージ</param>
/// <param name="privateKey">署名に使用する秘密鍵</param>
/// <returns>作成された署名</returns>
public static string CreateDigitalSignature(
    string message, string privateKey)
{
    //メッセージをバイト型配列にして、SHA1ハッシュ値を計算
    byte[] msgData = System.Text.Encoding.UTF8.GetBytes(message);
    System.Security.Cryptography.SHA1Managed sha =
        new System.Security.Cryptography.SHA1Managed();
    byte[] hashData = sha.ComputeHash(msgData);

    //RSACryptoServiceProviderオブジェクトの作成
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider();
    //秘密鍵を使って初期化
    rsa.FromXmlString(privateKey);

    //RSAPKCS1SignatureFormatterオブジェクトを作成
    System.Security.Cryptography.RSAPKCS1SignatureFormatter rsaFormatter =
        new System.Security.Cryptography.RSAPKCS1SignatureFormatter(rsa);
    //署名の作成に使用するハッシュアルゴリズムを指定
    rsaFormatter.SetHashAlgorithm("SHA1");

    //署名を作成
    byte[] signedValue = rsaFormatter.CreateSignature(hashData);

    //バイト型配列を文字列に変換して返す
    return System.Convert.ToBase64String(signedValue);
}

/// <summary>
/// デジタル署名を検証する
/// </summary>
/// <param name="message">署名の付いたメッセージ</param>
/// <param name="signature">署名</param>
/// <param name="publicKey">送信者の公開鍵</param>
/// <returns>認証に成功した時はTrue。失敗した時はFalse。</returns>
public static bool VerifyDigitalSignature(
    string message, string signature, string publicKey)
{
    //メッセージをバイト型配列にして、SHA1ハッシュ値を計算
    byte[] msgData = System.Text.Encoding.UTF8.GetBytes(message);
    System.Security.Cryptography.SHA1Managed sha =
        new System.Security.Cryptography.SHA1Managed();
    byte[] hashData = sha.ComputeHash(msgData);

    //RSACryptoServiceProviderオブジェクトの作成
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider();
    //公開鍵を使って初期化
    rsa.FromXmlString(publicKey);

    System.Security.Cryptography.RSAPKCS1SignatureDeformatter rsaDeformatter =
        new System.Security.Cryptography.RSAPKCS1SignatureDeformatter(rsa);
    //署名の検証に使用するハッシュアルゴリズムを指定
    rsaDeformatter.SetHashAlgorithm("SHA1");

    //署名を検証し、結果を返す
    return rsaDeformatter.VerifySignature(hashData,
        System.Convert.FromBase64String(signature));
}

/// <summary>
/// 公開鍵と秘密鍵を作成して返す
/// </summary>
/// <param name="publicKey">作成された公開鍵(XML形式)</param>
/// <param name="privateKey">作成された秘密鍵(XML形式)</param>
public static void CreateKeys(out string publicKey, out string privateKey)
{
    //RSACryptoServiceProviderオブジェクトの作成
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider();

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

上記メソッドの使用例

次に、上記のメソッドを使う例を示します。まず、CreateKeysメソッドで公開鍵と秘密鍵を作成します。署名を作成するには、CreateDigitalSignatureメソッドに署名を付けるメッセージと秘密鍵を指定します。署名を検証するには、VerifyDigitalSignatureメソッドにメッセージと署名と公開鍵を指定します。

VB.NET
コードを隠すコードを選択
'デジタル署名をつけるメッセージ
Dim message As String = "あいうえお"

'公開鍵と秘密鍵を取得
Dim privateKey As String = ""
Dim publicKey As String = ""
CreateKeys(publicKey, privateKey)

'署名を作成する
Dim signature As String = CreateDigitalSignature(message, privateKey)

'署名を検証する
If VerifyDigitalSignature(message, signature, publicKey) Then
    Console.WriteLine("検証成功")
Else
    Console.WriteLine("検証失敗")
End If
C#
コードを隠すコードを選択
//デジタル署名をつけるメッセージ
string message = "あいうえお";

//公開鍵と秘密鍵を取得
string privateKey;
string publicKey;
CreateKeys(out publicKey , out privateKey);

//署名を作成する
string signature = CreateDigitalSignature(message, privateKey);

//署名を検証する
if (VerifyDigitalSignature(message, signature, publicKey))
{
    Console.WriteLine("検証成功");
}
else
{
    Console.WriteLine("検証失敗");
}

デジタル署名を作成する手順

ここからは、上記のコードの説明をします。なお、RSACryptoServiceProviderクラスについて詳しくは「公開鍵暗号方法で暗号化する」で説明していますので、参考にしてください(CreateKeysメソッドはそちらで紹介しているメソッドをそのまま使っています)。

CreateDigitalSignatureメソッドでは、署名を作成しています。デジタル署名を作成する手順は以下の通りです。

  1. デジタル署名を付けるデータのハッシュ値を計算する。ここではSHA1で計算しているが、MD5でもよい。.NET Framework 2.0以降であれば、SHA256、SHA384、SHA512でもうまくいくようだ。ハッシュ値を計算する方法は、「MD5やSHA1などでハッシュ値を計算する」で詳しく説明している。もしファイルのデジタル署名を作成するならば、「ファイルのMD5やSHA1などでハッシュ値を計算する」により、ファイルのハッシュ値を計算する。
  2. RSACryptoServiceProviderオブジェクトを作成する。ここではFromXmlStringメソッドを使って、秘密鍵で初期化を行っている。ちなみに、秘密鍵を平文ではなくCSPキーコンテナに保存しておく方法は、「秘密鍵をCSPキーコンテナに保存する」で説明している。
  3. このRSACryptoServiceProviderオブジェクトを使用して、RSAPKCS1SignatureFormatterオブジェクトを作成する。
  4. RSAPKCS1SignatureFormatter.SetHashAlgorithmメソッドで、1.の計算で使用したハッシュアルゴリズム名を指定する。ここでは"SHA1"だが、別のアルゴリズムで計算したのであれば、その名前("MD5"、"SHA256"、"SHA384"、"SHA512"など)を指定する。.NET Framework 2.0以降であれば、ハッシュ値を計算する際に使用したクラスの完全限定名を指定しても大丈夫のようだ。この時は、上記の例で言えば、「sha.GetType().FullName」のようになる。
  5. RSAPKCS1SignatureFormatter.CreateSignatureメソッドで署名を作成する。このとき、1.で計算したハッシュ値を指定する。
  6. 上記の例では、作成された署名をBase64で文字列に変換している。

デジタル署名を検証する手順

上記のVerifyDigitalSignatureメソッドでは、デジタル署名の検証(正当か、不当か調べる)を行っています。デジタル署名を検証する手順は、次のようになります。

  1. 検証を行うデータのハッシュ値を計算する。デジタル署名を作成したときと同じハッシュアルゴリズムで計算する。
  2. RSACryptoServiceProviderオブジェクトを作成する。さらに、FromXmlStringメソッドを使って、公開鍵で初期化を行う。
  3. このRSACryptoServiceProviderオブジェクトを使用して、RSAPKCS1SignatureDeformatterオブジェクトを作成する。
  4. RSAPKCS1SignatureDeformatter.SetHashAlgorithmメソッドで、ハッシュアルゴリズム名を指定する。デジタル署名を作成したときと同じハッシュアルゴリズムを指定する。
  5. RSAPKCS1SignatureDeformatter.VerifySignatureメソッドで、署名の検証を行う。正当と判断されれば、Trueを返す。

DSACryptoServiceProviderクラスを使う場合

RSACryptoServiceProviderクラスの代わりにDSACryptoServiceProviderクラスを使用する場合は、RSAPKCS1SignatureFormatterクラスの代わりにDSASignatureFormatterクラスを、RSAPKCS1SignatureDeformatterクラスの代わりにDSASignatureDeformatterクラスを使用します。それ以外は、同じです。

  • 履歴:
  • 2010/10/10 .NET Framework 2.0以降に関する説明を補足。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。