swift 如何在NetCore中使用iOS CryptoKit加密和C#解密

bd1hkmkf  于 2023-01-25  发布在  Swift
关注(0)|答案(1)|浏览(305)

我想在iOS应用程序中使用SymmetricKey和CryptoKit加密数据,并在Net Core中使用C#在服务器端解密。
iOS代码:

class Security {
    
    static let keyStr = "d5a423f64b607ea7c65b311d855dc48f"  //32
    static let iv="31348c0987c7"    //12
    
    class func encode(_ text:String)->String {
        let key=SymmetricKey(data: Security.keyStr.data(using: .utf8)!)
        let nonce=try! AES.GCM.Nonce(data: iv.data(using: .utf8)!)
        let encrypted=try! AES.GCM.seal(text.data(using: .utf8)!, using: key, nonce: nonce)
        

        return encrypted.combined!.base64EncodedString()
    }
    
}

我将加密结果传递到后端,然后进行解密
C#代码:

public string decrypt(string encryptedText)
        {
            string keyStr = "d5a423f64b607ea7c65b311d855dc48f";
            string iv = "31348c0987c7";

            string plaintext = "";

        Debug.WriteLine(encryptedText);

        using (Aes aesAlg = Aes.Create())
        {

            Debug.WriteLine(AesGcm.IsSupported);

            var key = System.Text.Encoding.UTF8.GetBytes(keyStr);
            var iV = System.Text.Encoding.UTF8.GetBytes(iv);
            aesAlg.Key = key;
            aesAlg.IV = iV;

            // Create a decryptor to perform the stream transform.
            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for decryption.
            using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(request.pswd)))
            {
                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();
                    }
                }
            }
        }

        Debug.WriteLine(plaintext);

    }

例如:测试加密为:金属氮 MOXy 氮 MOXy 氧氯化物氮 MOXy 氮 MOXy 氮 MOXy 氧氯化物氮 MOXy 氧氯化物氧氯化物氧氯化物=
当我到线:

aesAlg.IV = iV;

我得到一个错误“指定的初始化向量(IV)不匹配此算法的块大小”。看起来C#需要字节[16],但在iOS中我似乎坚持使用12。
我在这一点上卡住了。任何想法都非常感谢。谢谢。

6ju8rftf

6ju8rftf1#

发布的Swift代码在GCM模式下应用AES,s. AES.GCM。发布的C#代码也使用AES,但不是GCM模式,而是默认的CBC模式(s. AesMode)。
CBC模式使用16字节的IV,而GCM模式使用12字节的nonce,这就是错误消息所指向的。
要成功解密,C#端也必须使用GCM模式的AES。在.NET中,AesGcm类支持GCM模式的AES(从.NET Core 3.0开始)。
还应注意,Swift代码给出的数据是12字节随机数、密文和16字节标签(按此顺序)的级联的Base64编码,这些数据必须在C#代码中分离,其中各部分被单独处理。
一个可能的C#实现,用于解密由发布的Swift代码生成的密文,如下所示:

byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");

Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
Span<byte> nonce = nonceCiphertextTagSpan[..12];
Span<byte> ciphertext = nonceCiphertextTagSpan[12..^16];
Span<byte> tag = nonceCiphertextTagSpan[^16..];
byte[] plaintext = new byte[ciphertext.Length];

using AesGcm aesGcm = new AesGcm(key);
aesGcm.Decrypt(nonce, ciphertext, tag, plaintext); // throws an 'CryptographicException: The computed authentication tag did not match the input authentication tag' if authentication fails

Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test

编辑:C#/BouncyCastle是本机.NET类AesGcm的一种替代方法。您的环境可能支持这种方法:

using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
...
byte[] nonceCiphertextTag = Convert.FromBase64String("MzEzNDhjMDk4N2M3CI68IDEJeBR4OFtWO3GPO3TIgos=");
byte[] key = Encoding.UTF8.GetBytes("d5a423f64b607ea7c65b311d855dc48f");

Span<byte> nonceCiphertextTagSpan = nonceCiphertextTag.AsSpan();
byte[] nonce = nonceCiphertextTagSpan[..12].ToArray();
byte[] ciphertextTag = nonceCiphertextTagSpan[12..].ToArray();

GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
AeadParameters aeadParameters = new AeadParameters(new KeyParameter(key), 128, nonce);
gcmBlockCipher.Init(false, aeadParameters);
byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(ciphertextTag.Length)];
int length = gcmBlockCipher.ProcessBytes(ciphertextTag, 0, ciphertextTag.Length, plaintext, 0);
gcmBlockCipher.DoFinal(plaintext, length); // throws an 'InvalidCipherTextException: mac check in GCM failed' if authentication fails

Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Test

注意,与原生的AesGcm类不同,C#/BouncyCastle需要将密文和标记连接在一起,因此只需要分离nonce。

相关问题