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

公開鍵暗号方法で暗号化する

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

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

.NET Frameworkでは公開キー暗号化アルゴリズムを実装するクラスとして、DSACryptoServiceProviderクラスRSACryptoServiceProviderクラスECDiffieHellmanCngクラス(.NET Framework 3.5以降)、ECDsaCngクラス(.NET Framework 3.5以降)が用意されていますが、暗号化に使用できるのはRSACryptoServiceProviderクラスのみです。

暗号化にはRSACryptoServiceProvider.Encryptメソッドを使用しますが、この時パラメータとして渡す暗号化されるデータにはサイズの制限があります。OAEPパディング、Direct Encryption、ともにサポートなしのそれぞれで暗号化されるデータの最大長の計算法が異なり、このことはMSDNの「RSACryptoServiceProvider.Encrypt メソッド」で説明されています。例えばDirect Encryption (PKCS#1 v1.5) の場合、RSACryptoServiceProvider.KeySize プロパティで取得できるキーサイズが1024ビットだとすると、暗号化されるデータの最大長は、 1024/8-11=117バイト となります。下のコードでは暗号化されるデータの最大長のチェックは全く行っていませんので、ご注意ください。

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

VB.NET
コードを隠すコードを選択
''' <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

''' <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 rsa As New System.Security.Cryptography.RSACryptoServiceProvider()

    '公開鍵を指定
    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 rsa As New System.Security.Cryptography.RSACryptoServiceProvider()

    '秘密鍵を指定
    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
C#
コードを隠すコードを選択
/// <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);
}

/// <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.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider();

    //公開鍵を指定
    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.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider();

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

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

    //結果を文字列に変換
    return System.Text.Encoding.UTF8.GetString(decryptedData);
}
注意:「マイクロソフト サポート技術情報 - 322371」によると、Web Service、ASP Page、COM+の場合は、上記コードの「RSACryptoServiceProviderオブジェクトの作成」の部分でエラーが発生することがあります。そのような場合は、この部分を以下のようにしてください。なお.NET Framework 1.1からは、RSACryptoServiceProvider.UseMachineKeyStoreプロパティを使うこともできます。
VB.NET
コードを隠すコードを選択
'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)
C#
コードを隠すコードを選択
//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);

秘密鍵をCSPキーコンテナに保存する

秘密鍵を永続的に記憶する場合、暗号化せずにそのまま(平文で)保存するのは危険です。安全かつ簡単に秘密鍵を永続的に保存する方法として、暗号サービスプロバイダ(Crypto Service Provider、CSP)のキーコンテナに格納する方法があります。

RSACryptoServiceProviderクラスでキーコンテナを使う方法は簡単で、キーコンテナ名を指定するだけです。キーコンテナ名を指定してRSACryptoServiceProviderオブジェクトを作成すると、もしその名前のキーコンテナが存在しなければ、新たにキーコンテナを作成して鍵を保存します。もしその名前のキーコンテナが存在していれば、キーコンテナに保存されている鍵を読み込みます。つまり、同じキーコンテナ名で作成したRSACryptoServiceProviderオブジェクトは、そのキーコンテナを削除しない限り、同じ鍵になります。

補足:CSPのRSAキーコンテナに保存した秘密鍵は、「%AppData%\Microsoft\Crypto\RSA\S-1-5-21...」フォルダ内にファイルとして保存されるようです。ただし、CspProviderFlags.UseMachineKeyStoreを指定した場合は、「%ALLUSERSPROFILE%\Application Data\Microsoft\Crypto\RSA\MachineKeys」フォルダに保存されるようです。また、Windows 2000以前では、レジストリに保存されるようです。

前のコードを書き換えて、秘密鍵をキーコンテナに保存する例を示します。前のコードとの違いは、秘密鍵をやり取りする代わりに、キーコンテナ名を使用している点です。また、指定したキーコンテナを削除するメソッドも加えました。

VB.NET
コードを隠すコードを選択
''' <summary>
''' 公開鍵と秘密鍵を作成し、キーコンテナに格納する
''' </summary>
''' <param name="containerName">キーコンテナ名</param>
''' <returns>作成された公開鍵(XML形式)</returns>
Public Shared Function CreateKeys(ByVal containerName As String) As String
    'CspParametersオブジェクトの作成
    Dim cp As New System.Security.Cryptography.CspParameters()
    'キーコンテナ名を指定する
    cp.KeyContainerName = containerName
    'CspParametersを指定してRSACryptoServiceProviderオブジェクトを作成
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider(cp)

    '公開鍵をXML形式で取得して返す
    Return rsa.ToXmlString(False)
End Function

''' <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
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider()
    rsa.FromXmlString(publicKey)
    Dim data As Byte() = System.Text.Encoding.UTF8.GetBytes(str)
    Dim encryptedData As Byte() = rsa.Encrypt(data, False)
    Return System.Convert.ToBase64String(encryptedData)
End Function

''' <summary>
''' キーコンテナに格納された秘密鍵を使って、文字列を復号化する
''' </summary>
''' <param name="str">Encryptメソッドにより暗号化された文字列</param>
''' <param name="containerName">キーコンテナ名</param>
''' <returns>復号化された文字列</returns>
Public Shared Function Decrypt(ByVal str As String, _
                               ByVal containerName As String) As String
    'CspParametersオブジェクトの作成
    Dim cp As New System.Security.Cryptography.CspParameters()
    'キーコンテナ名を指定する
    cp.KeyContainerName = containerName
    'CspParametersを指定してRSACryptoServiceProviderオブジェクトを作成
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider(cp)

    '復号化する
    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

''' <summary>
''' 指定されたキーコンテナを削除する
''' </summary>
''' <param name="containerName">キーコンテナ名</param>
Public Shared Sub DeleteKeys(ByVal containerName As String)
    'CspParametersオブジェクトの作成
    Dim cp As New System.Security.Cryptography.CspParameters()
    'キーコンテナ名を指定する
    cp.KeyContainerName = containerName
    'CspParametersを指定してRSACryptoServiceProviderオブジェクトを作成
    Dim rsa As New System.Security.Cryptography.RSACryptoServiceProvider(cp)

    'キーコンテナを削除
    rsa.PersistKeyInCsp = False
    rsa.Clear()
End Sub
C#
コードを隠すコードを選択
/// <summary>
/// 公開鍵と秘密鍵を作成し、キーコンテナに格納する
/// </summary>
/// <param name="containerName">キーコンテナ名</param>
/// <returns>作成された公開鍵(XML形式)</returns>
public static string CreateKeys(string containerName)
{
    //CspParametersオブジェクトの作成
    System.Security.Cryptography.CspParameters cp =
        new System.Security.Cryptography.CspParameters();
    //キーコンテナ名を指定する
    cp.KeyContainerName = containerName;
    //CspParametersを指定してRSACryptoServiceProviderオブジェクトを作成
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider(cp);

    //公開鍵をXML形式で取得して返す
    return rsa.ToXmlString(false);
}

/// <summary>
/// 公開鍵を使って文字列を暗号化する
/// </summary>
/// <param name="str">暗号化する文字列</param>
/// <param name="publicKey">暗号化に使用する公開鍵(XML形式)</param>
/// <returns>暗号化された文字列</returns>
public static string Encrypt(string str, string publicKey)
{
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider();
    rsa.FromXmlString(publicKey);
    byte[] data = System.Text.Encoding.UTF8.GetBytes(str);
    byte[] encryptedData = rsa.Encrypt(data, false);
    return System.Convert.ToBase64String(encryptedData);
}

/// <summary>
/// キーコンテナに格納された秘密鍵を使って、文字列を復号化する
/// </summary>
/// <param name="str">Encryptメソッドにより暗号化された文字列</param>
/// <param name="containerName">キーコンテナ名</param>
/// <returns>復号化された文字列</returns>
public static string Decrypt(string str, string containerName)
{
    //CspParametersオブジェクトの作成
    System.Security.Cryptography.CspParameters cp =
        new System.Security.Cryptography.CspParameters();
    //キーコンテナ名を指定する
    cp.KeyContainerName = containerName;
    //CspParametersを指定してRSACryptoServiceProviderオブジェクトを作成
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider(cp);

    //復号化する
    byte[] data = System.Convert.FromBase64String(str);
    byte[] decryptedData = rsa.Decrypt(data, false);
    return System.Text.Encoding.UTF8.GetString(decryptedData);
}

/// <summary>
/// 指定されたキーコンテナを削除する
/// </summary>
/// <param name="containerName">キーコンテナ名</param>
public static void DeleteKeys(string containerName)
{
    //CspParametersオブジェクトの作成
    System.Security.Cryptography.CspParameters cp =
        new System.Security.Cryptography.CspParameters();
    //キーコンテナ名を指定する
    cp.KeyContainerName = containerName;
    //CspParametersを指定してRSACryptoServiceProviderオブジェクトを作成
    System.Security.Cryptography.RSACryptoServiceProvider rsa =
        new System.Security.Cryptography.RSACryptoServiceProvider(cp);

    //キーコンテナを削除
    rsa.PersistKeyInCsp = false;
    rsa.Clear();
}
  • 履歴:
  • 2007/9/28 UseMachineKeyStoreを使用するコードを注意コラム内に移動。
  • 2010/10/10 「秘密鍵をCSPキーコンテナに保存する」を追加。

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

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