《101 Windows Phone 7 Apps》读书笔记-PASSWORDS & SECRETS

简介:

课程内容

Ø 加密和解密

Ø 密码输入框

Ø 值转换

Ø DataTimeOffset

Ø 可观察集合

Ø INotifyPropertyChanged事件

 

    Passwords & Secrets是一个类似记事本风格的应用,它有一个主人保护密码。因此,我们可以用它来存储大量的密码和一些不希望落入他人之手的秘密。当然,它的记事功能是一流的,支持:

Ø 自动保存,使得速记变得快速而简单。

Ø 提供每条笔记的快速预览。

Ø 可自定义每条笔记的背景色、前景色和字体大小。

Ø 可通过Email发送笔记。

    此外,每条笔记的数据会通过256位的AES加密算法进行加密,确保数据的私密性。这个加密过程也是建立在主人保护密码的基础上的,所以,用户千万不能忘记各自的保护密码。没有它,应用程序就没有方法获取数据,出于安全考虑,应用程序不会存储该密码。

    为了使管理主人密码尽量简单,Passwords & Secrets支持忘记密码提示。应用程序也允许改变主人密码(这也是以知道当前密码为前提的)。

    为什么我需要对隔离存储空间中的数据进行加密?不是只有应用程序才能获取吗?

    除非Windows Phone OS存在漏洞,其他应用程序是无法读取本应用程序的隔离存储空间的。而且,也没有人可以远程获取隔离存储空间的数据。但是,如果有能力的黑客从物理上攻破了你的设备,那么他们当然可以读取存储在其中的数据。数据加密以后,黑客们实际上很难再读懂数据的意义了。

 

Basic Cryptography

    Silverlight的System.Security.Cryptography命名空间中包含了一些具备加密功能的函数。本应用程序封装了这些函数,形成了一个简单易用的Crypto类。该类包含两个简单的方法:Encrypt 和 Decrypt,它们利用密钥和解密/加密的数据来进行解密/加密的操作。

    Encrypt 和 Decrypt都调用了GetAlgorithm helper方法(在文件的最后有定义)。返回的算法可以创建encryptor 或者 decryptor,它会被传送给crypto stream,从而完成加密/解密过程。

    在Encrypt中,输入的字符串被转换成为UTF8编码的字符。然后,这些字符被写入crypto stream,来完成加密过程。通过ToArray方法,就可以在crypto stream使用的内存中获取加密后的字符。这些字符通过Base64编码转换为stream,这是一种代表字符串中二进制数据的常用方法。

    Decrypt从Base64编码的字符串开始,将其转换为写入crypto stream的字符。然后,使用相应的ToArray方法将解密后的UTF8编码的数据转换为字符串。

    对输入字符串使用SHA256(256位Secure Hash Algorithm)哈希加密,在其前面添加一个随机的“salt”。这有时候被称为salted hash。本应用程序调用这个方法来存储密钥的salted hash,而不是密钥本身,来确保安全性。毕竟,如果黑客得到了隔离存储空间中的数据,而密钥是以普通文本的形式存放,那么,对数据加密就显得毫无意义了。

    GenerateNewSalt方法随机产生一个指定长度的byte数组。与其他应用程序中使用Random类不同,该方法使用了一种高度伪随机数产生器RNGCryptoServiceProvider,更适用于加密应用。正如下一节所示,就在应用程序第一次运行时,调用一次该方法。它将随机产生的salt存放于隔离存储空间中,用于随后所有的加密、加密和哈希算法。

    GetAlgorithm方法用于构造唯一内置的加密算法,即AesManaged,一种AES对称加密算法。该算法初始化时,需要一个密钥和一个初始化向量(IV),因此,它由Rfc2898DeriveBytes实例来处理。

    Rfc2898DeriveBytes是基于密码的密钥派生功能- PBKDF2的实现。它使用密码和一个随机的salt值,将基于SHA1哈希功能的随机函数执行很多次(默认是1000次)。这就使得密码更难被破译。

    AesManaged中KeySize属性的默认值也是它所支持的最大值:256。这意味着密钥的长度是256,也就是为什么这个过程被称为256比特的加密。

Salt in Cryptography

    使用salt可以带来一些好处,特别是salt可以用来保密,使得黑客破译的速度变慢。在本应用中,虽然salt值必须传递给Rfc2898DeriveBytes的构造函数,但是它并没有改变,因为salt必须和加密后的数据一同存放。Hash函数中的salting也是一样的道理。这对于管理多个密码的服务器来说,是一种好的方法(因此,以字典为基础的攻击必须为每个用户重新产生数据,即使用户的密码相同,它们的hash值也不同),这在本应用程序中得到了较好的体现。

 

The LoginControl User Control

    有了Crypto类,我们可以创建一个登陆控件,用来处理所有应用程序密码输入的交互。LoginControl用户控件有3种不同的模式:

Ø 新用户模式:用户第一次使用时,必须选定主控密码。

Ø 普通登陆模式:用户登录时,必须输入之前选定的密码。

Ø 修改密码模式:在用户输入当前的密码以后,可以修改其主控密码。

注意:

Ø 该控件在需要输入密码的地方使用了password box。password box和text box类似,只是每个字符都是以小圆点显示(在你输入字符一段时间以后将会看到)。这与内置应用的密码输入行为相匹配。与Text属性不同,它使用的是Password属性。

Ø 第17章“Pick a Card Magic Trick”中的不透明度屏蔽技巧用来给出具有当前主题强调色的锁的图案。它的不透明度为50%,所以有点融入了背景中。

注意:

该列表使用了以下在Settings.cs文件中定义的一些设置:

Crypto类使用的Rfc2898DeriveBytes方法中,salt的长度至少是8个字节。本应用程序调用GenerateNewSalt,产生salt的长度是16个字节。

在普通登录模式中,该控件必须判断输入的密码是否正确。但是应用程序并没有存储用户密码。然而,它存储了密码的salted hash值。因此,为了验证输入的密码,应用程序调用相同的Crypto.Hash函数,并检查它与存储的值是否一致。

虽然未加密的密码没有被存储,但是应用程序将它保存在RAM中,所以应用程序能够解密用户保存的数据,并且对新数据进行加密。这些在CurrentContext类中完成,其定义如CurrentContext.cs所示。

在修改密码模式中,旧密码起到了非常重要的作用。因为那些使用旧密码加密的数据必须通过旧密码来解密,然后再使用新密码进行加密。否则的话,原来存储的数据会无法读取,因为无法使用新密码来解密旧密码加密的数据。

在Close中,每个password box的Password属性被设置为一个空字符串,而非空,因为如果设置为空的话,Password属性会跑出一个异常。

我们可以发现,LoginControl并不是一个通用的控件,而是为本应用定制的(虽然在更改密码过程中,通过给用户提供钩子来完成数据的重新加密并不是一件难事)。如本章的下面三节所示,它被使用在三个不同的地方。

 

The Main Page

    本应用的主页面包含了用户笔记的列表,如图21.2所示。点击每条记录,可以对其进行浏览和编辑。应用程序栏上的按钮使得我们可以增加新的记录。但在列表形成并显示之前,用户必须输入正确的密码。在用户没有登录的情况下,LoginControl除了header以外,会占据整个页面,应用程序栏中也没有了新增记录的按钮。

image

图21.2 应用程序主页面

注意:

应用程序标题中的“&”符号使用XML编码,避免XAML解析错误。

LoginControl用户控件作为本页面的一部分,而非独立的登录页面,这样是为了确保流畅的导航体验。当用户打开应用程序,登录,看到主页面上的数据,按硬件“Back”按钮,应该退出应用程序,而非回到登录页面!

LoginControl并不是简单地通过视觉掩盖来保护数据的,从背后的代码中我们可以看到,只有用户登录了以后,数据才会显示出来。而且,在用户登录之前,应用程序是无法显示数据的,因为对存储的数据进行解密的话,需要正确的密码。

list box中的条目模板与每个记录中的多个属性进行绑定(用于显示记录的Note类在本章的后面部分讲述)。与Modified属性的绑定使用了值转换器,它用来改变结果的显示。值转换器会在下面讲述。

 

Value Converters

    在数据绑定中,值转换器可以将源数据转换为一个完全不同的目标类型,使得我们可以在不丢失数据绑定好处的情况下,嵌入自定义逻辑。

    值转换器被经常用来在源数据和目标数据类型之间进行转换。比如,我们可以使用一些nonbrush的数据源来改变元素的背景色或者前景色,就像Microsoft Excel中的条件格式一样。另外一个例子,Silverlight for Windows Phone Toolkit中的toggle switch控件具有一个称为OnOffConverter的值转换器,它把非空的布尔类型值IsChecked转变为“On” 或者 “Off”为默认内容的字符串。

    在Passwords & Secrets应用中,我们想要对每条记录的Modified属性显示做轻微的自定义。它的数据类型是DateTimeOffset,如果没有值转换器,它的显示效果如下:

    12/11/2012 10:18:49 PM -08:00

    -08:00代表时区,它表示与国际标准时间(UTC)之间的偏差。

    我们使用的自定义值转换器省略了时区信息和秒,因为那些信息我们不需要。因此显示效果如下:

    12/11/2010 10:18 PM

    即使Modified属性是DateTime类型,而非DateTimeOffset,为了将秒从字符串中省去,值转换器仍旧是有用的。

    DateTime和DateTimeOffset这两种数据类型有何区别?

    DateTime是指与任何时区无关的逻辑时间点,而DateTimeOffset是指与UTC时间存在偏差的实际时间点。在本应用中,DateTimeOffset更适合给每条记录的修改时间使用,因为即使用户接下来会到另一个时区,他们也不希望时间点会改变。但是,在前一章“Alarm Clock”中,提醒时间使用了DateTime类型。假设你在一个时区设置了闹钟,但是在闹钟要响起的时候,你却在另一个时区。例如,我们设置闹钟在早晨8点,那么,无论我们在哪个时区,我们都希望闹钟准时响起。

    对于大多数情况来说,使用DateTimeOffset要优于DateTime。但是,相对于DateTime来说,.NET Framework引入DateTimeOffset要晚几年,所以命名已经被使用了(类的设计者拒绝称之为DateTime2 或者 DateTimeEx)。幸运的是,这些数据类型的用户可以经常交换地使用它们。

    为了创建一个值转换器,我们必须写一个实现System.Windows.Data 中的IValueConverter接口的类。该接口具有两个简单的方法:一个是Convert,它被传入必须转换为目标实例的源实例;另一个是ConvertBack,它正好相反。列表21.7包含了列表21.6中使用的DateConverter值转换器的实现。

    每次源值改变时,Convert方法就会被调用。传入DateTimeOffset值,返回短格式的日期和时间字符串。ConvertBack方法不是必须的,因为它只在双向数据绑定中使用。因此,它返回一个虚值。

Additional Data for Value Converters

    IValueConverter方法传入一个参数和一个本地化语言类型。默认情况下,参数设置为空,本地化语言设置为目标元素的Language属性值。但是,绑定的用户仍旧可以通过Binding.ConverterParameter 和 Binding.ConverterCulture来控制这两个值。

   传递给值转换器类的ConverterParameter参数可以是任意的用户数据,就像元素的Tag属性一样。ConverterCulture可以设置为因特网工程任务推动小组(IETF)语言标签(如en-US 或者 ko-KR),值转换器接收合适的CultureInfo对象。在DateConverter中,ToString方法已经遵守了当前的语言规则,所以没有必要再做本地化相关的工作。

    与基本的格式转换不同,值转换器是把用户逻辑融入数据绑定过程的关键。在原始数据进行显示之前想要对其进行某种改变,或者基于原始数据的目标更新,这些都可以通过实现了IValueConverter的类来轻松实现。

    人们创建的最常见的值转换器是Boolean-to-Visibility(常常被称为BooleanToVisibilityConverter),它可以将数据在可视性枚举值和布尔值(或者是非空布尔值)进行转换。在一个方向上,true被转换为Visible,而false和null被映射为Collapsed。另一个方向上,Visible被映射为true,而Collapsed被映射为false。这对于以下的情况比较有用,即将一个XAML控件元素的可视性与另一个不相关的XAML控件元素的状态关联起来。比如,下面的XAML片段实现了一个Show Button check box,而不需要使用程序代码(除值转换器之外)。

    在这种情况下,button只有在check box的IsChecked属性为true时可见。

image

图21.3 应用程序栏展开页面

注意:

如图21.3所示,应用程序栏的第一个menu起到下面的作用:在用户没有登录的情况下给出密码提示,在用户已经登录的情况下给出密码修改页面。

正如之前所提到的,作为list box 控件数据内容的NotesList集合与普通的集合不同(如List<Note>),它是一个可观察的集合。

    在发生任何改变时(比如新增条目或者删除条目),可观察的集合会触发一个CollectionChanged事件。数据绑定自动将此消息发送给目标控件(本页面的list box),从而保持一致性。

    尽管可观察的集合可以处理list box控件中条目的增加和删除,但是每个Note条目必须在其属性改变时发送通知,确保它反应在数据绑定的list box中。如列表21.9所示,Note通过实现INotifyPropertyChanged接口来完成此功能。

INotifyPropertyChanged只有一个成员- PropertyChanged事件。如果其中任何一个属性在一个合适的时间被改变,那么这个事件就会被触发,数据绑定负责目标控件的数据刷新。

被触发的PropertyChanged事件由OnPropertyChanged helper方法来处理。为了避免bug,将一个handler变量赋值给event handler字段。否则,如果当前线程在检查handler是否为空并调用它时,另一个线程对其进行删除操作,那么NullReferenceException异常便会抛出(在没有listener的情况下,event handler字段变为空)。

Dependency属性自动实现change-notification机制,其特性与INotifyPropertyChanged类似,但略有不同。因此,在使用Dependency属性时,不需要额外的代码,就可以完成与数据绑定配合的change notification。

某些属性会因为额外的属性发生改变而触发PropertyChanged事件。比如,当EncryptedContent被设置为一个新值的时候,PropertyChanged会因为readonly Title属性而被触发。这是因为Title的值是基于EncryptedContent的值,所以EncryptedContent的改变会导致Title的改变。

可观察集合通过实现INotifyCollectionChanged接口来完成出色的功能,INotifyCollectionChanged接口与INotifyPropertyChanged类似,包含了一个CollectionChanged事件。但是,让用户自己写collection 类,并且实现INotifyCollectionChanged接口,而不是单纯地使用ObservableCollection类,这种情况也是非常少见的。

 

The Details Page

    详细页面如图21.4所示,当用户点击主页面上的list box控件中的记录时,便会弹出。该页面显示了一条记录的全部内容,允许用户对其进行编辑、删除或者利用其内容发送邮件。另外,通过它还可以进入每条记录的设置页面,用来控制字体的颜色和大小。在浏览模式中,应用程序栏可见。在编辑模式中,应用程序栏隐藏。

    该页面的text box基本占据了整个屏幕,它已经被用户自定义了,我们移除了它的边界,无论text box是否获得焦点,其背景色一直可见。这种风格来源于

%ProgramFiles%\Microsoft SDKs\Windows Phone\v7.0\Design\System.Windows. xaml,不是新创建的。

注意

该页面使用了navigatingFrom标志来检查页面是否处于被导航的状态。那是因为OnNavigatedFrom后,Loaded事件会被再次触发,此时如果将焦点给text box的话,会导致屏幕键盘闪现。

本页面的设置页面的代码会在下一章详述,因为它和本应用程序使用的方式相同。

在导航到别的页面时,页面的Loaded事件被错误触发!这是当前Windows Phone版本的一个bug。为了避免这种闪屏或其他的问题,考虑在OnNavigatedFrom中设置一个标志,在Loaded事件中进行检查。那样的话,我们就可以确保页面加载逻辑只在页面加载时执行。



本文转自施炯博客园博客,原文链接:http://www.cnblogs.com/dearsj001/archive/2012/08/09/101App4WP7_Passwords.html,如需转载请自行联系原作者

相关文章
|
Linux C++ Windows
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
285 0
【Azure 应用服务】Azure App Service(Windows)环境中如何让.NET应用调用SAP NetWeaver RFC函数
|
C# Windows
【Azure App Service】在App Service for Windows上验证能占用的内存最大值
根据以上测验,当使用App Service内存没有达到预期的值,且应用异常日志出现OutOfMemory时,就需要检查Platform的设置是否位64bit。
223 11
|
Java 应用服务中间件 开发工具
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
[App Service for Windows]通过 KUDU 查看 Tomcat 配置信息
191 2
|
Java 应用服务中间件 Windows
【App Service for Windows】为 App Service 配置自定义 Tomcat 环境
【App Service for Windows】为 App Service 配置自定义 Tomcat 环境
152 2
|
PHP Windows
【Azure App Service for Windows】 PHP应用出现500 : The page cannot be displayed because an internal server error has occurred. 错误
【Azure App Service for Windows】 PHP应用出现500 : The page cannot be displayed because an internal server error has occurred. 错误
275 1
|
PHP 开发工具 git
【Azure 应用服务】在 App Service for Windows 中自定义 PHP 版本的方法
【Azure 应用服务】在 App Service for Windows 中自定义 PHP 版本的方法
202 1
|
网络安全 API 数据安全/隐私保护
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Windows)
187 0
|
Shell PHP Windows
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
177 0
|
存储 Linux Windows
【应用服务 App Service】App Service For Windows 如何挂载Storage Account File Share 示例
【应用服务 App Service】App Service For Windows 如何挂载Storage Account File Share 示例
143 0
|
应用服务中间件 nginx Windows
【Azure 应用服务】在App Service for Windows中实现反向代理
【Azure 应用服务】在App Service for Windows中实现反向代理
178 0