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

  • 相关阅读:
    CSS去掉 a 标签点击后出现的虚线框
    AMD 和 CMD的区别
    sublime text常用快捷键
    jsonp详解
    JSON详解
    JS知识总结
    input 单选按钮radio 取消选中(转载)
    koala 编译scss不支持中文解决方案
    Spring事务的传播行为 @Transactional(转)
    Ubuntu下JDK+Tomcat+MySql环境的搭建
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/4341335.html
Copyright © 2011-2022 走看看