2008年10月24日星期五

Ingrian手记——CryptoUtility使用说明(高阶)

在特殊场景下可能你的程序不方便使用App.config或Web.config(比如你将这些配置信息都存在数据库中)。这时你就需要用到下面介绍的几个CryptoSession的构造函数了。

   1: CryptoSession(string userName, string password);
   2: CryptoSession(string userName, string password, string keyNamePrefix);
   3: CryptoSession(string userName, string password, string keyName, bool isKeyNamePrefix);

这三个构造函数做的事情与使用配置文件时一样,当你不能使用配置文件时就使用这几个构造函数吧。不过另外还有一个构造函数,如下:

   1: CryptoSession(IAccountProvider accountProvider, IKeyNameProvider keyNameProvider);

这个构造函数非常Powerful,如果你有极特殊复杂的帐号信息获取逻辑的话那么可实现IAccountProvider和IKeyNameProvider这两个接口来向CryptoSession提供帐号信息和key name,注意这里提供的key name必须是完整的,而不能仅仅是prefix。

讲过了上面几种不同的构造方案后我们再来看一下CryptoSession的Open方法和Close方法。

Open方法会创建一个与Ingrian服务器交互的上下文(Context),并且会通过我们提供的key name来获取到Ingrian服务器上的“key对象”。在任何加解密操作前这个上下文和“key对象”都应该是已经存在的,也就是,像SqlConnection一样,在操作前必须先调用一下Open方法。不过,在基础篇中我们所给出的代码片段中就没有调用Open方法,这是因为CryptoSession会自动判断,如果没有Open过的话它会自己先Open一下。

顾名思义,Close方法就是把这些资源都释放掉的一个方法,Close后可再Open。如果不调用Close方法便再一次调用Open的话,Open会自己调用一遍Close方法以释放原有的资源。可能你会想,为什么要这么做呢,这不是多余吗?其实不然,这就是另一个高阶使用技巧。当你Open一段时间后,可能会出现因为某些原因“key对象”或者当前这个上下文会失效或者丢失的情况,当你有这样的怀疑时,你可以捕获异常,在异常处理中再Open一次,这样原有的资源会被释放掉,新的,有效的上下文和“key对象”会被获得。

Ingrian手记——CryptoUtility各项限制

Ingrian本身的API有一些限制,会影响到CryptoUtility,组件自身也有一些限制,所以在这里对这些使用限制做一个集中描述。

CryptoUtility组件中所使用的key name是有限制的,key name的整体长度不能超过15个ASCII字符。但由于使用时我们可以只指定前缀(prefix)部分,所以在这种情况下prefix的实际长度不能超过8个字符,因为在prefix之后会占用7个字符。

一次加解密动作的数据量是有限制的,每次操作的byte数不能超过167,999。如果你加解密的是字符串的话需注意byte数是以UTF8解码后的byte数计算的。

Ingrian手记——CryptoUtility配置详解

CryptoUtility并没有多少配置信息,因此它仅有的一些配置项也都很简单。

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <appSettings>
   4:     <!-- User name used to connect Ingrian servers, required. -->
   5:     <add key="ingrian.userName" value="sampleUserName"/>
   6:     
   7:     <!-- Password used to connect Ingrian servers, required. -->
   8:     <add key="ingrian.password" value="samplePassword"/>
   9:     
  10:     <!-- Key name used in cryptographic operations, optional.
  11:          If you do not provide one and nor you provide a
  12:          key name prefix, the component will use a default one. -->
  13:     <add key="ingrian.keyName" value="sampleKeyName"/>
  14:     
  15:     <!-- Key name prefix used to construct a key name for
  16:          cryptographic operations, optional. If you do not
  17:          provide one and nor you provide a key name, the
  18:          component will use a default key name. -->
  19:     <add key="ingrian.keyNamePrefix" value="sampleKeyNamePrefix"/>
  20:   </appSettings>
  21: </configuration>

ingrian.userName是必须提供的,在与Ingrian服务器连接时使用。

  1. ingrian.password是必须提供的,在与Ingrian服务器连接时使用。
  2. ingrian.keyName是可选的,指定各项操作时用的key name。如不提供,CryptoSession会再去找ingrian.keyNamePrefix这个配置项的内容,如有,就使用这个prefix,如没有,就使用组件默认提供的key name。
  3. ingrian.keyNamePrefix是可选的,指定一个prefix用来获取相应的key name。如不提供并且又没有提供key name,那么组件会使用默认提供的key name。

备注:这里所说的默认提供的key name并不是一成不变每次都是一样的,这个默认提供的key name有一个有效期,过期会变。不过这一切都不影响用户的使用,你可以将这里提到的备注忘掉。:-)

Ingrian手记——CryptoUtility使用说明(基础)

CryptoUtility是一个封装了Ingrian客户端组件的DLL,使.NET程序可以更方便的调用Ingrian来做加解密。下面让我们来看看如何使用这个DLL。

首先我们创建一个.NET项目,是Console,是Windows或是Web的都可以,这里我们以一个Console程序为例。创建好后我们将CryptoUtility.dll这个程序集引用到我们的Console程序中来,如图:

CryptoUtility_Reference 

接下来我们来看一段代码:

   1: using System;
   2: using Newegg.CryptoUtility;
   3:  
   4: namespace CryptoUtilityDemo
   5: {
   6:     class Program
   7:     {
   8:         static void Main(string[] args)
   9:         {
  10:             Console.WriteLine("Write something:");
  11:             string something = Console.ReadLine();
  12:  
  13:             using (ICrypto crypto = new CryptoSession())
  14:             {
  15:                 string encryptedText = crypto.Encrypt(something);
  16:  
  17:                 Console.WriteLine("Encrypted text is: {0}", encryptedText);
  18:  
  19:                 string decryptedText = crypto.Decrypt(encryptedText);
  20:  
  21:                 Console.WriteLine("Decrypted text is: {0}", decryptedText);
  22:             }
  23:  
  24:             Console.ReadKey();
  25:         }
  26:     }
  27: }

这段代码演示的是一个很简单的场景,让用户输入一段文字,然后创建一个CryptoSession对象来加密这段文字,之后再解密加密后的文字,再将CryptoSession对象Dispose掉。正如你看到的,CryptoUtility组件的使用是非常简单的。不过光有这段代码程序还跑不起来,这是因为Ingrian需要一个帐号连接到它的服务器上,并且我们还要提供一个key name才能够进行加密或解密等操作。CryptoSession的默认构造函数会自动从App.config或Web.config中读取这些信息,所以我们可以添加一个App.config或Web.config来指定这些信息,如下:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <appSettings>
   4:     <add key="ingrian.userName" value="sampleUser"/>
   5:     <add key="ingrian.password" value="samplePassword"/>
   6:   </appSettings>
   7: </configuration>

上面配置中被划掉的部分就是连接Ingrian服务器所需的用户名和密码了,可以看到这里我们并没有指定key name,这是因为如果我们不指定的话CryptoUtility会自动帮我们找出合适的key用来加解密,这也是一般我们提倡的做法。也许你会问,“那么在解密的时候CryptoUtility怎么知道我该用哪个key解密呢?”,答案是CryptoUtility足够聪明,它确实能够知道该用什么key来解密,我们在使用它时就不用关心这个问题了。

另外虽然没有在上面的代码片段中体现出,但是无论是在CryptoSession Open时,还是在加做解密动作时,都有可能会有异常抛出,例如连接不上Ingrian服务器就会抛出CryptoConnectionException,在加解密时出错就会抛出CryptoException等。如何根据需要在你的代码中捕获这些异常就要看你的了。下面给出一段异常处理的代码片段:

   1: private static void HandleExceptionCase()
   2: {
   3:     ICrypto crypto = new CryptoSession();
   4:  
   5:     try
   6:     {
   7:         crypto.Open();
   8:  
   9:         string encryptedText = crypto.Encrypt("123");
  10:         string decryptedText = crypto.Decrypt(encryptedText);
  11:  
  12:         //TODO
  13:     }
  14:     catch (CryptoConnectionException)
  15:     {
  16:         //Handle connection problems.
  17:     }
  18:     catch (CryptoKeyException)
  19:     {
  20:         //Handle key problems.
  21:     }
  22:     catch (CryptoException)
  23:     {
  24:         //Handle other problems.
  25:     }
  26:     finally
  27:     {
  28:         crypto.Close();
  29:     }
  30: }

可以看出对于CryptoSession对象的异常处理方式和SqlConnection之类的对象是一样的。也就是,平时不捕捉异常时也要养成使用C#关键字using的好习惯。但如果由于性能原因需要保留CryptoSession对象的话千万别忘了在用完之后将其Close(或Dispose)掉,这一点与SqlConnection之类对象的注意事项是一样的。

好了,以上就是CryptoUtility的基本使用方法,相信它已经足够应付绝大部分的应用场景了。在高阶篇中我会讲到一些CryptoUtility组件的高级使用方法,当这种简单的使用方法不能满足你的需求时就可以去看看了。

2008年10月23日星期四

Ingrian FAQ

我在这里汇总了一下迄今为止遇到的一些Ingrian相关的问题与解决方法,这篇博客的内容也会当有新问题时被更新。

Questions

Q1:使用Ingrian组件进行加解密操作时组件在建立与Ingrian Server的连接时(或之后)抛出异常。

Q2:当我解密一个很长的字符串时会报错。

Q3:为什么我用同一个用户和同一个key在两台机器上会得出不同的结果?

Q4:为什么我昨天加密的一个字符串会和我今天加密的同一个字符串结构不一样?

Answers

A1:这有可能是由于以下两个原因造成:

  1. 当前应用程序没有对Ingrian组件的读取与执行权限。请检查运行应用程序的帐户是否有对Ingrian安装目录下相应组件的读取与执行权限,包括配置文件、DLL与Windows注册表读取权限。
  2. Ingrian配置文件中有指定输出日志,应用程序没有对此日志文件的写权限。可以先指定一个我们已知有权限的日志文件的完整路径,确认是这个问题。如是,找出日志文件的路径,给与其相应帐户的写权限。

A2:这是Ingrian组件MSCAPI的一个限制,每次的操作都不能传递大于168,000个byte,当然这个问题和Padding也有些关系。总之在使用Ingrian组件时记住不能加解密超过167,999个byte。在String的情况下,因为字符串是以UTF8解码后得到的byte,所以一个字符串所得到的byte数要大于字符数,这点要注意。

A3:实际上你在程序中用到的并不是实际加解密用的key,而只是一个key name而已,那么这就可以解释为什么同一个用户同一个key name在两台机器上加密后的结果会不一样了。因为这两台机器很有可能连的是不同的Ingrian Server,例如GQC和生产环境的Server上的key是不一样的,虽然key name是一样的。

这也是一个告诫,不要混用QGC和生产环境的数据,也就是,用GQC的key去加密生产环境数据再导到生产环境的话就会引起在生产环境下无法解密这些由GQC加密过的数据,反之亦然。

A4:如果你真的运气那么好碰到了这种情况,那么只能说明时间又过了半年。O_O 我们会每半年变更一次key name(当然key也不是同一个了),所以加密的结果不一样了,不过你并不用担心,因为组件会自动找出曾经加密过它的key name,并用此去解密。

Ingrian手记——安装与配置

Ingrian是一全套企业级加解密解决方案,这个我们都知道,就不多说了。赶快来看看我们所关心的Ingrian客户端组件的安装与配置。

安装

Ingrian组件的安装是非常简单的,双击随便安装在一个文件夹下即可。

配置

Ingrian主要的配置文件在它的安装目录下,IngrianNAE.properties,这个配置文件里有Ingrian组件所要连的Ingrian服务器的IP、端口、是否要输出日志文件,日志的输出级别等信息,里面有详细的注释解释每一个配置项。

这里着重讲一下NAE_IP这个配置,这里我们指定Ingrian Server的IP,值得注意的是这里我们可以指定多个Ingrian Server的IP,当有多个以分号连接的IP时,Ingrian组件每次连接时首先会尝试第一个IP,如第一个IP连不上,那么会自动尝试第二个,以此类推,直到连上一个为止。也就是,这是Ingrian组件自己实现的一个客户端负载均衡(Client-Side Load Balancing)。另外还有一个配置项Connection_Timeout,我们建议将这个timeout设成2秒钟,这样在Load Balance的时候不至于由于第一个IP down掉了就一直等很长的时间。

注意:这个配置文件是全局的,也就是,所有在这台机器上的程序都会使用这份配置中的信息来连接Ingrian服务器,输出日志,等等。

另外Ingrian会从Windows注册表中找到这个配置文件,也就是,如果在注册表中将配置文件的路径改了,那么就有可能出现找不到配置文件或配置信息错误的情况。这个键值在"HKEY_LOCAL_MACHINE\SOFTWARE\Ingrian\NAE_Properties_Config"中,名字是ConfigFilename。

2008年10月22日星期三

关于我博客的Title

看起来很奇怪的Title不是吗?可能你已经猜到了,这是某种编程语言的语法。

F#

是的,我博客的Title就是用微软研究院研发的F#语言描述的。F#是一个Functional Language和Object-Oriented Imperative Language的混合物,它建立在.NET CLR之上,可以与任何其他.NET平台下的语言进行互操作,是微软即将支持的有一个主流语言。

Title表达的是什么?

想了解Title表达的是什么,得先了解一点F#的语法,放心,只是一点点就足够了。首先是“|>”这个操作符,它的作用是Pipelining,也就是将左边的表达式作为参数传递给右边表达式,然后计算。其次我们要了解“(属于 我)”这种表达式。括号当然是为了明确标明一个个体,在这里这个个体就是一个函数“属于”和一个参数“我”的结果值。在函数式编程中一个函数的结果值(返回值)也可以是另外一个函数,函数在函数式编程中是First-Class Citizen。再次回到我们的例子中,“(属于 我)”会返回一个函数,而这个函数接受一个参数。

完整的来看,我博客的Title便是:将“代码”当作参数传给“创造世界”这个函数,“创造世界”的返回值会当作参数传给“(属于 我)”这个函数。也就是,我博客Title的本意是:“用代码创造一个属于我的世界”。

以下是换做C#语法的情景:

   1: 世界.创造(代码).属于(我);