【问题标题】:RSA encryption from Flex client and Corresponding Decryption from Web service来自 Flex 客户端的 RSA 加密和来自 Web 服务的相应解密
【发布时间】:2011-05-24 22:21:08
【问题描述】:

我在 flex 客户端和用 c# 编写的 Web 服务之间设置 RSA 加密/解密机制时遇到问题。想法是这样的:我将从 flex 中加密一些文本,然后从 Web 服务中对其进行解密。我正在使用来自谷歌的 as3crypto 库。它正在正确加密/解密文本。我也有网络服务端的代码来正确加密/解密。我的问题是同步它们 - 基本上共享公钥以 flex 并将私钥保留给 Web 服务。

我的 flex“加密”函数采用 RSA 的模数和指数来进行文本加密,那么我如何从 Web 服务的 RSACryptoServiceProvider 获取这些模数和指数属性,所以它们使用相同的标准。 我试过了 RSAKeyInfo.Modulus RSAKeyInfo.Exponent 来自 web 服务并将它们提供给 flex 客户端。 在对 flex 进行加密后,我获取了密文并将其提供给 web 服务上的解密方法,但它给了我“坏数据”错误消息。

System.Security.Cryptography.CryptographicException: Bad Data.

   at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
   at System.Security.Cryptography.Utils._DecryptKey(SafeKeyHandle hPubKey, Byte[] key, Int32 dwFlags)
   at System.Security.Cryptography.RSACryptoServiceProvider.Decrypt(Byte[] rgb, Boolean fOAEP)
   at Microsoft.Samples.Security.PublicKey.App.RSADecrypt(Byte[] DataToDecrypt, RSAParameters RSAKeyInfo, Boolean DoOAEPPadding) in C:\Users
\Me\Desktop\After Release\5-24-2011-webServiceCrypto\publickeycryptography\CS\PublicKeyCryptography\PublicKey.cs:line 219
Encryption failed.

我如何确保它们都使用相同的 64 字节或 128 字节加密。即来自 flex 的输入应该符合 Web 服务 RSACryptoServiceProvider 的解密方法所期望的。 (我假设尺寸可能是个问题,可能不是 - 我迷路了)

这是代码,首先是 flex 客户端,然后是 web 服务 c# 代码

private function encrypt():void {
                var rsa:RSAKey = RSAKey.parsePublicKey(getModulus(), getExponent());
                trace("Modulus Lenght: " + getModulus().length);
                trace("Exponent Lenght : " + getExponent().length);
                var data:ByteArray = getInput();  //returns byteArray of plainText
                var dst:ByteArray = new ByteArray;
                rsa.encrypt(data, dst, data.length);
                trace("Enc Data: " + dst.toString() );
                currentResult = Hex.fromArray(dst);
                encryptedText = currentResult;
                trace("Encrypted:: " + currentResult);
            }

            //For testing purposes
            private function decrypt():void {
                var rsa:RSAKey = RSAKey.parsePrivateKey(getModulus(), getExponent(), getPrivate(), getP(), getQ(), getDMP1(), getDMQ1(), getCoeff());
                var data:ByteArray = Hex.toArray(encryptedText);
                trace("Byte array: " + data.toString());
                var dst:ByteArray = new ByteArray;
                rsa.decrypt(data, dst, data.length);
                decryptedText = Hex.fromArray(dst);
                trace("Decrypted text: " + Hex.toString(decryptedText));
            }

Web服务部分如下:

       try
        {
            //Create a UnicodeEncoder to convert between byte array and string.
            UnicodeEncoding ByteConverter = new UnicodeEncoding();

            //Create byte arrays to hold original, encrypted, and decrypted data.
            byte[] dataToEncrypt = ByteConverter.GetBytes("Data to Encrypt");
            byte[] encryptedData;
            byte[] decryptedData;

            //Create a new instance of RSACryptoServiceProvider to generate
            //public and private key data.
            using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
            {
                //Pass the data to ENCRYPT, the public key information 
                //(using RSACryptoServiceProvider.ExportParameters(false),
                //and a boolean flag specifying no OAEP padding.
                encryptedData = RSAEncrypt(dataToEncrypt, RSA.ExportParameters(false), false);
                //Pass the data to DECRYPT, the private key information 
                //(using RSACryptoServiceProvider.ExportParameters(true),
                //and a boolean flag specifying no OAEP padding.
                decryptedData = RSADecrypt(encryptedData, RSA.ExportParameters(true), false);
                //Display the decrypted plaintext to the console. 
                Console.WriteLine("\n\nDecrypted plaintext: {0}", ByteConverter.GetString(decryptedData));
            }
        }

static public byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
    {
        try
        {
            byte[] encryptedData;
            //Create a new instance of RSACryptoServiceProvider.
            using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
            {

                //Import the RSA Key information. This only needs
                //toinclude the public key information.
                RSA.ImportParameters(RSAKeyInfo);

                //Encrypt the passed byte array and specify OAEP padding.  
                //OAEP padding is only available on Microsoft Windows XP or
                //later.  
                encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
            }
            return encryptedData;
        }
        //Catch and display a CryptographicException  
        //to the console.
        catch (CryptographicException e)
        {
            Console.WriteLine(e.Message);

            return null;
        }

    }


 static public byte[] RSADecrypt(byte[] DataToDecrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
    {
        try
        {
            Console.WriteLine("Modulus Lenghth :" + RSAKeyInfo.Modulus.Length);
            Console.WriteLine("Exponent Length :" + RSAKeyInfo.Exponent.Length);
            byte[] decryptedData;
            //Create a new instance of RSACryptoServiceProvider.
            using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
            {
                //Import the RSA Key information. This needs
                //to include the private key information.
                RSA.ImportParameters(RSAKeyInfo);

                //Decrypt the passed byte array and specify OAEP padding.  
                //OAEP padding is only available on Microsoft Windows XP or
                //later.  
                decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
            }
            return decryptedData;
        }
        //Catch and display a CryptographicException  
        //to the console.
        catch (CryptographicException e)
        {
            Console.WriteLine(e.ToString());

            return null;
        }

    }

我不太确定这种 RSA 设置是否适合... 欢迎任何评论/建议/或推荐的解决方案, 谢谢大家

【问题讨论】:

  • 我删除了 Webservices 标签并用 Public-Key-Encryption 替换它;因为这似乎是标记这个问题的更重要的方式。
  • 当用户有权访问密钥时,加密是无用的。您将永远无法实现比 SSL 更安全的解决方案,因此您不妨使用它...
  • 感谢 flextras。 @drkstr- 用户有公钥但不知道私钥,那它怎么没用?而且我的网络服务是通过 https 公开的,因此可以实现一定程度的安全性。
  • 现在我很好奇...这是某种需要加密的监管恶作剧吗?我可以想象客户端像你想要的那样加密数据并发送到 Web 服务器的情况。但是网络服务器没有解密密钥。其他一些机器偶尔会抓取该数据并对其进行解密。
  • 它没用,因为客户端加密只会对已经拥有它的人隐藏数据。 @Marc 即使如此,在服务器端运行加密会更好。客户端加密唯一有意义的情况是 SSL 不可用,或者数据在发送到服务器之前需要在本地存储(如离线模式)。由于客户端上已经存在未加密的数据,并且 SSL 将对通过网络发送的任何内容进行加密,因此根本不需要在客户端运行任何类型的加密。

标签: c# wcf apache-flex actionscript-3 public-key-encryption


【解决方案1】:

尤里卡!尤里卡!我明白了。

问题是在从 Web 服务解密后,加密的字节数组在两者之间缺少 0,因此当更改为字符串时,它变得不可读 '????????'文本。所以我只是放了 paddWithZeros() 函数来用字节之间的 0 填充解密的字节数组,它就起作用了。

感谢 Kevin,您的解决方案让我了解了我应该考虑的事项。所以在解密过程中我将参数 fOAEP 指定为 false,因此它将使用 PKCS#1 进行填充(使两个库使用相同的标准)。

RSA.Decrypt(DataToDecrypt, DoOAEPPadding); // DoOAEPPadding = false

我得到的另一个错误是错误数据异常。当我将 RSA cryptoServiceProvider 的参数(模数和指数)共享给 actionScript 方法时,此问题已得到修复。

我还将 c# RSA 属性(如 Modulus n、Exponent e、Private d..etc)的 byte[] 数组更改为十六进制字符串,以便能够与 as3crypto 库共享。

我很乐意分享对我有用的方法;为他人节省一些时间。

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
               xmlns:s="library://ns.adobe.com/flex/spark" 
               xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">

    <fx:Script>
        <![CDATA[
            import com.hurlant.crypto.Crypto;
            import com.hurlant.crypto.rsa.RSAKey;
            import com.hurlant.crypto.symmetric.ICipher;
            import com.hurlant.crypto.symmetric.IPad;
            import com.hurlant.util.Hex;

            private var currentResult:String;
            private var encryptedText:String;
            private var decryptedText:String;

            private function encrypt(plainText:String):String {
                var rsa:RSAKey = RSAKey.parsePublicKey(getModulus(), getExponent());
                var data:ByteArray = Hex.toArray(Hex.fromString(plainText));  //returns byteArray of plainText
                var dst:ByteArray = new ByteArray;
                rsa.encrypt(data, dst, data.length);
                currentResult = Hex.fromArray(dst);
                encryptedText = currentResult;
                trace ("Cipher: " + currentResult);
                return currentResult;
            }

            private function getInput():ByteArray {
                return null;
            }

            private function getModulus():String {
                return "b6a7ca9002b4df39af1ed39251a5d"; //read this value from web service.
            }

            private function getExponent():String {
                return "011"; //read this value from web service.
            }

            //For debugging and testing purposes
            // private function decrypt(cipherText:String):String {
                // var rsa:RSAKey = RSAKey.parsePrivateKey(getModulus(), getExponent(), getPrivate(), getP(), getQ(), getDMP1(), getDMQ1(), getCoeff());
                // var data:ByteArray = Hex.toArray(cipherText);
                // var dst:ByteArray = new ByteArray;
                // rsa.decrypt(data, dst, data.length);
                // decryptedText = Hex.fromArray(dst);
                     //trace('decrypted : ' + decryptedText);
                // return Hex.toString(decryptedText);
            // }

        ]]>
    </fx:Script>

    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <mx:VBox >
        <s:Button label="Encrypt Text" click="encrypt('my plain text')" />
        <s:Button label="Decrypt Text" click="decrypt({encryptedText})" />
    </mx:VBox>
</s:Application>

而解密的web服务部分是这样的:

 static public string RSADecrypt(string cipherText)
    {
        UnicodeEncoding ByteConverter = new UnicodeEncoding();
        byte[] DataToDecrypt = StringToByteArray(cipherText);
        bool DoOAEPPadding = false;
        try
        {
            byte[] decryptedData;
            using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
            {
                KeyInfo keyInfo = new KeyInfo();
                RSAParameters RSAKeyInfo = keyInfo.getKey();
                RSA.ImportParameters(RSAKeyInfo); 
                decryptedData = RSA.Decrypt(DataToDecrypt, DoOAEPPadding);
            }
            byte[] paddedOutput = paddWithZeros(decryptedData); //to sync with as3crypto
            return (ByteConverter.GetString(paddedOutput));

        }catch (CryptographicException e)
        {
            //handle error
            return null;
        }
    }

我会阅读一些有关 RSA 的填充方案的信息,看看是否有任何误解。

谢谢

【讨论】:

  • 您的答案取决于您未提供的 paddWithZeros 方法
  • 这里的问题是从字节数组到字符串的转换。看起来动作脚本使用的是 8 位的 ascii,但 c# 使用的是 Unicode。如果使用正确的编码,则无需用零填充。
【解决方案2】:

似乎过于复杂。我以前曾在一些高安全性系统上工作过,但这很荒谬。除非您不希望用户知道他刚刚输入的文本,否则为什么您需要对正在发送的文本进行这种级别的加密?

对于具有二进制数据格式 (AMF) 的实际传输协议(我想是 HTTP),只需使用强 SSL 密钥(IE6 最大为 256 位,您可以使用 512 但仅与较新的浏览器兼容),一切都应该没问题.我怀疑您的系统对于利用加密文本的使用是否重要。

【讨论】:

  • Jax,但我有没有提到我正在加密用户给定的输入?被加密的数据不是用户的输入。某些信息应该端到端加密 - 客户端到服务器。这个概念并不过分复杂。在公钥加密中,只要您使用我的公钥进行加密,您使用什么库/框架并不重要。我的问题是,如何在 flex 中加密并在 c# 中解密?我知道有与 flex 相关的安全问题,但我们先不谈这些。
  • @Rose 没有实际需要在客户端加密数据。如果数据在客户端存在未加密,那么它已经泄露给另一端的任何人。数据在通过 SSL 发送时被加密,因此在数据到达服务器之前无需加密任何数据。底线是客户端是公开的,不应该被信任、加密或不加密。您最好关注服务器端的安全性和身份验证。
  • 如果您使用的是安全协议,它会被端到端加密。我的观点是,第二次加密它不会给你任何价值,并且很可能会减慢你的系统(特别是如果你有很多数据),因为闪存上的加密/解密过程不是本机的。这是有原因的,因为它不是必需的。 AS3Crypto 存在的唯一原因是与套接字一起用于 Flash 中未提供的新型协议。
【解决方案3】:

我使用 as3crypto 和 JAVA 网络服务。以下是一些想法:

一个。我通过 openssl 生成了我的 RSA 公钥和私钥

b.我的客户端在应用程序启动时加载公共 .cer 文件(如果您只是从生成的密钥硬编码它们也可以)。

var pemString : String = new String(data.target.data);
var x509Cert : X509Certificate = new X509Certificate(pemString);
var publicRSAKey : RSAKey = x509Cert.getPublicKey();

c。通过

加密我的字符串
var inputByteArray : ByteArray = Hex.toArray(Hex.fromString(inputString));
var outputByteArray : ByteArray = new ByteArray();
appSettingsModel.publicRSAKey.encrypt(inputByteArray, outputByteArray, inputByteArray.length);

d。我没有写 JAVA 方面的东西,但无论如何你都没有使用 JAVA。我知道 as3crypto 默认使用 PKCS1 填充:

RSAKEY.as

private function _encrypt(op:Function, src:ByteArray, dst:ByteArray, length:uint, pad:Function, padType:int):void {
            // adjust pad if needed
            if (pad==null) pad = pkcs1pad;

这可以更改,但我还没有尝试过。根据您的代码,您可能正在尝试使用 OAEP 方案解密,但我不知道您是如何设置该布尔值的。您可能想看看 bool 为 false 时使用的填充方案,并尝试更改一侧或另一侧以匹配填充策略。

【讨论】:

    猜你喜欢
    • 2015-10-29
    • 1970-01-01
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 2015-01-09
    相关资源
    最近更新 更多