zoukankan      html  css  js  c++  java
  • 【WP开发】不同客户端之间传输加密数据

    在上一篇文章中,曾说好本次将提供一个客户端之间传输加密数据的例子。前些天就打算写了,只是因一些人类科技无法预知的事情发生,故拖到今天。

    本示例没什么技术含量,也没什么亮点,Bug林立,只不过提供给有需要的朋友娱乐娱乐一下,喜欢钻牛角尖的朋友最好别看,否则会让你一把鼻涕一把泪的。

    好,废话到此为止。

    因为在Windows上的RT应用程序的加/解密方法和上一篇文章中我给大家讲述的WP加解密的方法是一样的,毕竟那是共享的API。为了达到充分装逼的效果,我准备的服务器应用程序为Windows Forms应用程序,这类项目相信大家都无比熟悉了,如果你不知道Windows Forms是啥,那就没办法了。

    客户端当然是WP手机端了。为了装逼而又不复杂,我的思路是这样的:

    1、在WP端上选择一张.jpg靓照,通过DES算法加密,然后通过HTTP POST到服务器应用程序上。

    2、作为服务器的Windows Forms应用收到文件后,用DES算法解密,并保存接收到的文件。

    3、为了便于处理,加密和解密的密钥都固定。key为12345678共八个字节,iv为12345678共八个字节。

    先说服务器端,因为大家都熟悉。那么,如果建立一个临时的HTTP服务器来监听连接呢。就是为了方便,所以我才不建ASP.NET应用程序,这样的小演示,就不要劳烦IIS君了。其实,在System.Net命名空间下,有一个HttpListener类,它可以通过编写代码建立一个简单的HTTP服务器,并添加绑定的URI列表,可以监听HTTP请求,然后作出处理。

    在使用HttpListener前,最好调用它的静态的IsSupported属性来确认一下,你当前的系统是否能支持HTTP监听。

                if (!HttpListener.IsSupported)
                {
                    MessageBox.Show("你当前的系统太破,不支持HTTP监听。");
                    this.Close();
                }

    通过这一检查后,说明系统是支持的,然后再实现后面的功能。这里顺便说说配置服务器地址时要注意的一些小事。

    1、绑定的URI加到Prefixes集合中。

    2、URI的路径可以自己安排,比如http://192.168.1.101:8080/sv/,也可以http://192.168.1.101:8080/a/b/c/,假设你的IP是192.168.1.101,不应该用localhost,因为它只有本机才能访问,实际上就是127.0.0.1。

    URI必须以HTTP开头,以/结尾,尤其是结尾,不要少了/,否则会发生异常。

    如果不确定我的IP呢,或者说绑定本地多个IP呢,可以这样http://+:80/rec/,80是端口号,你可以根据实际来指定。

    调用Start方法开始监听,调用Stop方法停止。我这里用一个Task来循环监听连接。主要代码如下:

                this.mHttpListener.Start();
                Task backTask = new Task(WorkInBack, mHttpListener);
                backTask.Start();
    
                ……
            private async void WorkInBack(object obj)
            {
                HttpListener httpSvr = obj as HttpListener;
                while (httpSvr.IsListening)
                {
                    HttpListenerContext context = await httpSvr.GetContextAsync();
                    // 从标头中提取文件名
                    string fileName = context.Request.Headers.Get("file_name");
                    fileName = Path.Combine(this.docFolderPath, fileName);
                    if (File.Exists(fileName))
                    {
                        File.Delete(fileName);
                    }
                    using (FileStream outStream = File.OpenWrite(fileName))
                    {
                        MemoryStream tempStream = new MemoryStream();
                        // 解密
                        byte[] key = { 1, 2, 3, 4, 5, 6, 7, 8 };
                        byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 };
                        DESCryptoServiceProvider des=new DESCryptoServiceProvider();
                        CryptoStream streamCrypt = new CryptoStream(tempStream, des.CreateDecryptor(key, iv), CryptoStreamMode.Write);
                        context.Request.InputStream.CopyTo(streamCrypt);
                        //streamCrypt.Flush();
                        // 复制内存流到文件流
                        tempStream.Position = 0L;
                        tempStream.CopyTo(outStream);
                        streamCrypt.Dispose();
                        tempStream.Dispose();
                    }
                }
            }

    在传统.net程序中使用DES加解密相信大家都用得不少了,我就不介绍了。IsListening属性可以判断监听器是否正在工作,如果Stop了就不再接收连接请求了。

    文件名是从file_name标头中获取的,这个标头是自定义的,在WP客户端发送文件时顺便加上这个标头。

    下面是WP客户端的实现。

    这里用到我以前说过的文件选择器。前面文章中我说过,FileOpenPicker的PickSingleFileAndContinue方法调用后,会暂时离开当前应用,等用户选择完后,会重新激活应用,并通过OnActivated方法把用户选择的文件传递进来。

            protected override void OnActivated(IActivatedEventArgs args)
            {
                if (args.Kind == ActivationKind.PickFileContinuation)
                {
                    FileOpenPickerContinuationEventArgs e = (FileOpenPickerContinuationEventArgs)args;
                    if (e.Files.Count > 0)
                    {
                        StorageFile theFile = e.Files[0];
                        Frame root = Window.Current.Content as Frame;
                        if (root != null)
                        {
                            MainPage page=root.Content as MainPage;
                            if (page != null)
                            {
                                page.SendFile(theFile);
                            }
                        }
                    }
                }
                Window.Current.Activate();
            }

    SendFile方法是在MainPage页面类中公开的一个自定义方法,代码如下:

            public async void SendFile(StorageFile file)
            {
                IRandomAccessStream encryptedstream = null;
                using (IRandomAccessStream inStream = await file.OpenReadAsync())
                {
                    encryptedstream = await EncryptoDataAsync(inStream);
                }
                using (HttpClient client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Add("file_name", file.Name);
                    HttpResponseMessage response = null;
                    using (HttpStreamContent content = new HttpStreamContent(encryptedstream))
                    {
                        response = await client.PostAsync(new Uri(txtServer.Text), content);
                    }
                    if (response != null && response.StatusCode == HttpStatusCode.Ok)
                    {
                        // 发送成功
                    }
                }
    
            }

    HttpClient比较好的地方是它对要发送的内容可以以不同内容格式进行封装。

    比如,要发字符串,就用HttpStringContent;要发送 multipart/form-data MIME数据就用HttpMultipartFormDataContent。此处,发送的是文件,应该用HttpStreamContent,以流的形式发送。

    在上面代码中,EncryptoDataAsync是我定义的一个方法,作用当然是将输入的文件流加密,再存放到内存流中,并将内存流返回。代码:

            /// <summary>
            /// 加密
            /// </summary>
            /// <param name="inputStream"></param>
            /// <returns></returns>
            private async Task<IRandomAccessStream> EncryptoDataAsync(IRandomAccessStream inputStream)
            {
                byte[] bytekey = { 1, 2, 3, 4, 5, 6, 7, 8 };
                byte[] byteiv = { 1, 2, 3, 4, 5, 6, 7, 8 };
                IBuffer key = bytekey.AsBuffer();
                IBuffer iv = byteiv.AsBuffer();
                SymmetricKeyAlgorithmProvider prd = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.DesCbcPkcs7);
                // 创建密钥
                CryptographicKey crykey = prd.CreateSymmetricKey(key);
                // 加密数据
                InMemoryRandomAccessStream mms = new InMemoryRandomAccessStream();
                IBuffer orgData = null;
                using (DataReader reader = new DataReader(inputStream))
                {
                    uint len=(uint)inputStream.Size;
                    await reader.LoadAsync(len);
                    orgData = reader.ReadBuffer(len);
                }
                IBuffer res = CryptographicEngine.Encrypt(crykey, orgData, iv);
                await mms.WriteAsync(res);
                mms.Seek(0UL);
                return mms;
            }

    前面在说双向加密时,介绍过RT API中加密解密的过程。

    先通过SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.DesCbcPkcs7)获得一个实例。
    然后,创建CryptographicKey实例,作为加密用的key。

    最后,用CryptographicEngine类来完成加密。

    ==============================

    示例核心部分已经向大家介绍了。其余部分省略5000多个字,稍后我会把源代码打包上传。

    注意在使用源码时,以管理员身份运行VS,这样HTTP服务才能监听;手机端用真机测试容易连接;要是连不上,就先把防火墙关了再试,试完了重新开启防火墙即可。

    下载地址:http://files.cnblogs.com/files/tcjiaan/Sample.zip

  • 相关阅读:
    CodeForces 660D Number of Parallelograms
    【POJ 1082】 Calendar Game
    【POJ 2352】 Stars
    【POJ 2481】 Cows
    【POJ 1733】 Parity Game
    【NOI 2002】 银河英雄传说
    【NOI 2015】 程序自动分析
    【POJ 1704】 Georgia and Bob
    【HDU 2176】 取(m堆)石子游戏
    【SDOI 2016】 排列计数
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/4341335.html
Copyright © 2011-2022 走看看