在开发过程中,凡涉及注册、登录、身份验证,常常会需要引入大数算法,通过大数算法和哈希相结合,用于密钥生成等。
一般平台,可以使用开源的mpir算法库。任何能够使用标准C的平台都可以方便的使用mpir库进行大数运算。
详细信息可以查阅www.mpir.org。
但是,因为SymbianOS提供的STD C/C++库并不能完整的支持标准C,因此使用gcc或者armv5编译mpir库时,会遇到很多问题,从而导致编译无法通过。我在最近的项目中尝试在Symbian环境中编译mpir-2.3.0-lite版本。在经过了大量修改和函数重写之后,gcc编译勉强通过,但是armv5版本始终无法通过编译。而大家应该能够想想,gcc版本的mpir库有多大,放在产品中显然不合适。因此,只能另找方法解决这个问题。
幸运的是,Nokia提供了一套大数运算库。这套库在Symbian^3中已经包含在SDK中,S60 v3/v5开发时,则需要在Forum nokia下载:
http://wiki.forum.nokia.com/index.php/Symbian_Cryptography_APIs
在应用程序部署时,S60 v5/Symbian^3手机都已经带有相关的lib库,可以不必考虑相关库的打包问题。
不幸的是,这套库虽然提供了所谓的API帮助文档,但是与万恶的SymbianOS文档一样,几乎没有提供任何有价值的信息。幸亏有从symbian fundation下载的源代码,一一对照才知道该如何使用。
常用的操作包括:
1,生成128位随机数:
RInteger Rs = RInteger::NewRandomL(128);
2, 用指定数据构造大数:
TUint8 _n[128] = {0xEE,
TBuf8<128> n;
n.Append(_n, 128);
RInteger N = RInteger::NewL(n);
3, 基本运算:
RInteger r1 = RN.TimesL(Rx);
RInteger r2 = Ra.PlusL(r1); //r2 = Ra + r1;
RInteger r3 = RB.MinusL(r2);
4,使用RInteger中的Buffer数据:
TPtr8 RBPtr = RB.BufferLC()->Des();
……
CleanupStack::PopAndDestroy(1); //delete buffer.
4, ModPow运算:
ModPow运算是做大数运算中很常用的算法: g^a%N
也就是对g做指数为a的指数运算,然后用N取余。
ModPow的基本算法是:
static int ModPow(int x, int n, int m)
{
int z = 1;
for (int i = 0; i < n; i++) z = (z * x) % m;
return z;
}
更高效的算法是:
static int ModPow(int x, int n, int m)
{
int z = 1;
for (int p = x; n > 0; n >>= 1, p = (p * p) % m) if ((n & 1) != 0) z = (z * p) % m;
return z;
}
如果使用Symbian的RInteger,可以使用:
RInteger RS;
TRAPD(err, RS = left.ModularExponentiateL(g, a, N)); //g^a%N
if(err) //err
必须要注意的是:
如果采用这样的写法进行ModPow运算:
g.ExponentiateL(a).ModuloL(N);
则一般情况下肯定会出问题,理由并不难想象:指数运算的数据量是恐怖的,因此并不需要太大的a值,就可以让上面的指数运算ExponentiateL()抛出内存不足的错误。经过实践,如果保持缺省的栈内存和堆内存,如果底数g为16字节(128位),则只是4个字节(32位)的指数a,就会抛出内存不足。
因此先做指数运算再取模和直接使用ModPow算法调用ModularExponentiateL()绝对是不一样的。
另一件值得一提的事情是。对于取模运算,如果能够注意到模运算的分配率,可以大大的提高大数运算的效率,因为取模可以有效地降低数据的位数。例如:
(46 * 72)
等于((46 % 17) * (72 % 17)) % 17
即等于(12 * 4) % 17
= 48 % 17
= 14
最后附上使用CSHA1实现哈希的代码:
void GetMsgDigestBySHA1L( TDes8 &aDest, const TDesC8 &aSrc )