0

AES 暗号化を使用してパスワードを暗号化する必要がある iPhone アプリケーションを作成しています。AES 暗号化のさまざまな例を見つけましたが、実装はサンプルごとに異なることがわかりました。復号化プロセスも制御していれば問題ありませんが、そうではありません。.NET コードを使用してパスワードを復号化する .NET API に暗号化されたパスワードを送信する必要があります。

以下に C# コードを含めます。誰かが私を正しい方向に向けることができますか、またはさらに良いことに、このC#コードで動作するNSStringを暗号化するためのObjective-Cコードを提供できますか?

提供された sharedSecret の長さは 126 文字なので、これは 128 ビット暗号化であると想定しています。または、sharedSecret は 128 文字にする必要がありますか?

public class Crypto
{
    private static byte[] _salt = Encoding.ASCII.GetBytes("SALT GOES HERE");

    /// <summary>
    /// Encrypt the given string using AES.  The string can be decrypted using 
    /// DecryptStringAES().  The sharedSecret parameters must match.
    /// </summary>
    /// <param name="plainText">The text to encrypt.</param>
    /// <param name="sharedSecret">A password used to generate a key for encryption.</param>
    public static string EncryptStringAES(string plainText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException("plainText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        string outStr = null;                       // Encrypted string to return
        RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create a RijndaelManaged object
            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

            // Create a decrytor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                // prepend the IV
                msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        //Write all data to the stream.
                        swEncrypt.Write(plainText);
                    }
                }
                outStr = Convert.ToBase64String(msEncrypt.ToArray());
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return outStr;
    }

    /// <summary>
    /// Decrypt the given string.  Assumes the string was encrypted using 
    /// EncryptStringAES(), using an identical sharedSecret.
    /// </summary>
    /// <param name="cipherText">The text to decrypt.</param>
    /// <param name="sharedSecret">A password used to generate a key for decryption.</param>
    public static string DecryptStringAES(string cipherText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException("cipherText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create the streams used for decryption.                
            byte[] bytes = Convert.FromBase64String(cipherText);
            using (MemoryStream msDecrypt = new MemoryStream(bytes))
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                // Get the initialization vector from the encrypted stream
                aesAlg.IV = ReadByteArray(msDecrypt);
                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return plaintext;
    }

    private static byte[] ReadByteArray(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }

        return buffer;
    }
}
4

2 に答える 2

1

この場合、共有シークレットの長さはキーのビット長とは関係ありません。ここでは、C# がRfc2898DeriveBytesを使用してキーを取得する方法を確認できます。

Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

RFC 2898は、PKCS5 標準 (PBKDF2 を意味します) を定義しています。Microsoft のドキュメントに基づくと、デフォルトの反復回数は 1000 であるように見えるため、共有シークレット、salt、および反復回数を取得しています。それを別の PBKDF2 実装にプラグインすると、暗号化に使用する必要がある生のキーが得られます。

次に、RijndaelManagedオブジェクト (Rijndael は標準化される前の AES の名前でした) を作成し、デフォルトのキー サイズをビット単位で取得します (これを 8 で割ってバイトを取得します)。次に、キー変数からそのバイト数を取得します。このクラスのデフォルトのキー サイズがわかれば、それが AES キーのサイズです。

(ちなみに、これらのオブジェクトの 1 つを作成すると、ドキュメントには、ランダムな IV が生成され、デフォルトで CBC になると記載されているため、ここからはそれを想定できます)

次に、IV の長さを書き込み、次に IV 自体を書き込みます。

msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);

結局、暗号文が書き込まれ、ブロブ全体が完成します。

復号化側では、ほとんど同じことを逆に行います。最初にキーを取得し、次に暗号化された BLOB 全体を取得して、それを ReadByteArray にフィードし、IV を抽出します。次に、キー + IV を使用して復号化します。

これを Objective-C に実装することは、サンプルの暗号化された BLOB と共有シークレットを考えると、それほど難しくないはずです!

于 2013-05-25T01:45:09.513 に答える