ここでは公開鍵暗号 (非対称暗号化方式、公開キー暗号方式)により暗号化、復号化する方法を紹介します。公開鍵暗号とは、公開鍵と秘密鍵(個人鍵)の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メソッドにより行います。
''' <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
/// <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プロパティを使うこともできます。
'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)
//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);
秘密鍵を永続的に記憶する場合、暗号化せずにそのまま(平文で)保存するのは危険です。安全かつ簡単に秘密鍵を永続的に保存する方法として、暗号サービスプロバイダ(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以前では、レジストリに保存されるようです。
前のコードを書き換えて、秘密鍵をキーコンテナに保存する例を示します。前のコードとの違いは、秘密鍵をやり取りする代わりに、キーコンテナ名を使用している点です。また、指定したキーコンテナを削除するメソッドも加えました。
''' <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
/// <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(); }
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。