• RSA算法C语言实现(支持任意位密钥)


    代码路径:https://github.com/prophetss/rsa

      之前分享过三种常用MD5、SHA2和AES加密算法(点这里)实现源码,前三者分别属于哈希加密和对称加密,而另一种很常用的非对称加密RSA算法实现这次分享出来。RSA算法的原理和用途大家可以网上自行搜索。虽然其算法原理很简单,但是由于其密钥长度很长(一般至少1024位),所以实际在其相互运算以及大质数查找会牵扯很多算法理论,因此我这里代码实现中数学运算是直接基于GMP(The GNU Multiple Precision Arithmetic Library),我将其linux64位下编译好的动静态库都已上传,如果环境相同可以不用安装直接使用。另外针对RSA算法内的大质数p和q以及公钥e和私钥d等也是有要求的(参考这里),这块我是参考JDK内RSA算法的实现。

      下面开始准备工作,首先是大质数的选取,由于大质数判断是相当困难,所以当前对于大质数的选取都是概率选取,先看下JDK内实现(我查看的代码版本是jdk-10.0.1)

    BigInteger.java内762-769行,其中SMALL_PRIME_THRESHOLD为常量95 ,DEFAULT_PRIME_CERTAINTY为常量100,rnd入参是一个随机数,所以是按95位为分界对应两个不同方法,我这里只考虑长密钥所以直接看大的

        public static BigInteger probablePrime(int bitLength, Random rnd) {
            if (bitLength < 2)
                throw new ArithmeticException("bitLength < 2");
    
            return (bitLength < SMALL_PRIME_THRESHOLD ?
                    smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
                    largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
        }

    BigInteger.java内822-841行,certainty就是之前传入的常量100,而其结果是调用BitSieve类中的retrieve方法获取,为了简单些我这就跳过这个函数代码了,里面大体是循环调用BigInteger类内primeToCertainty方法。

        private static BigInteger largePrime(int bitLength, int certainty, Random rnd) {
            BigInteger p;
            p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
            p.mag[p.mag.length-1] &= 0xfffffffe;
    
            // Use a sieve length likely to contain the next prime number
            int searchLen = getPrimeSearchLen(bitLength);
            BitSieve searchSieve = new BitSieve(p, searchLen);
            BigInteger candidate = searchSieve.retrieve(p, certainty, rnd);
    
            while ((candidate == null) || (candidate.bitLength() != bitLength)) {
                p = p.add(BigInteger.valueOf(2*searchLen));
                if (p.bitLength() != bitLength)
                    p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
                p.mag[p.mag.length-1] &= 0xfffffffe;
                searchSieve = new BitSieve(p, searchLen);
                candidate = searchSieve.retrieve(p, certainty, rnd);
            }
            return candidate;
        }

    BigInteger.java内934-962行,可以看到最后调用MillerRabin和LucasLehmer算法,前者中rounds为迭代轮数,而这个算法检测一轮为质数实际为不为质数的概率为1/4(参考这里),后者也是一个伪素数检测算法,感兴趣可以参考这里,所以可以看出JDK中RSA算法获取的是概率质数,其概率跟其位数相关。

        boolean primeToCertainty(int certainty, Random random) {
            int rounds = 0;
            int n = (Math.min(certainty, Integer.MAX_VALUE-1)+1)/2;
    
            // The relationship between the certainty and the number of rounds
            // we perform is given in the draft standard ANSI X9.80, "PRIME
            // NUMBER GENERATION, PRIMALITY TESTING, AND PRIMALITY CERTIFICATES".
            int sizeInBits = this.bitLength();
            if (sizeInBits < 100) {
                rounds = 50;
                rounds = n < rounds ? n : rounds;
                return passesMillerRabin(rounds, random);
            }
    
            if (sizeInBits < 256) {
                rounds = 27;
            } else if (sizeInBits < 512) {
                rounds = 15;
            } else if (sizeInBits < 768) {
                rounds = 8;
            } else if (sizeInBits < 1024) {
                rounds = 4;
            } else {
                rounds = 2;
            }
            rounds = n < rounds ? n : rounds;
    
            return passesMillerRabin(rounds, random) && passesLucasLehmer();
        }

      通过查询GMP库中对应函数,其文档有两个相关函数int mpz_probab_prime_p (const mpz t n, int reps)和void mpz_nextprime (mpz t rop, const mpz t op)前者是概率判断n(入参)是否为质数,其概率值为4的reps(入参)次方,后者是直接给出一个大于op(入参)的概率质数rop(出参)。所以前者就是对应JDK中的primeToCertainty方法,后者对应probablePrime方法,为了简单我这里使用mpz_nextprime 函数直接获取,这个函数没有给出是质数的概率,文档也没有说明只是说结果是合数的概率很小(原文:“This function uses a probabilistic algorithm to identify primes. For practical purposes it’s adequate, the chance of a composite passing will be extremely small.”)。我查了下源码,这个概率可能是4的-25次方(最后调用了25轮MillerRabin算法)

      有了获取大质数的获取方法剩下的难点就是查询GMP文档学习英语了..JDK内的RSA加密解密是基于CRT的,我不太清楚这块原理所以将这部分去掉了,代码封装了字符串加解密(每次加密数据大小不可超过密钥大小),下面展示下在我机子上的运行结果(2048bit密钥):

  • 相关阅读:
    小程序-文章:微信小程序常见的UI框架/组件库总结
    小程序-文章:微信第三方登录(静默授权和非静默授权)
    asterisk
    Java实现 洛谷 P1423 小玉在游泳
    Java实现 洛谷 P1423 小玉在游泳
    Java实现 洛谷 P1423 小玉在游泳
    Java实现 洛谷 P1035 级数求和
    Java实现 洛谷 P1035 级数求和
    Java实现 洛谷 P1035 级数求和
    Java实现 洛谷 P1035 级数求和
  • 原文地址:https://www.cnblogs.com/prophet-ss/p/8972863.html
Copyright © 2020-2023  润新知