RSA算法原理及實現(Java)

基本介紹

RSA加密算法是一種非對稱加密算法。這就意味着通過這個算法,你即將獲得一對密鑰,分別是公鑰和私鑰。你可以將公鑰公佈出去,別人利用你的公鑰加密後的內容,只能使用你的私鑰來解開,即可保證你和別人通信的安全性,這就是這個加密算法的意義所在。

算法步驟

參考來自:25行代碼實現完整的RSA算法

(1)pq1024(1)選擇 p、q兩個超級大的質數 ,都是1024位。

(2)n=pqφ(n)=(p1)(q1)n(2)令n = p * q。取 φ(n) =(p-1) * (q-1)。 計算與n互質的整數的個數。

(3)e{1<e<φ(n)}(n,e)(3)取 e ∈ \{1 < e < φ(n)\} ,( n , e )作爲公鑰對
65537,HTTP正式環境中取65537,依據來源於HTTP證書。

(4)edmodφ(n)=1d(n,d)(4)令 ed \quad mod \quad φ(n) = 1,計算d,( n , d ) 作爲私鑰對。
d計算d可以利用擴展歐幾裏的算法進行計算,非常簡單。

(5)pq=emodn=dmodn(5)銷燬 p、q。密文 = 明文 ^ e\quad mod\quad n , 明文 = 密文 ^ d\quad mod \quad n。

算法難點

  1. 如何儲存大數,動態語言一般不存在這個問題。不過在Java中就需要藉助BigInteger,其他靜態語言可能需要藉助第三方庫才能存儲。
  2. 如何計算一個超大整數的超大整數次冪,然後取超大整數模,這個文字有點繞,大概就是這個樣子:mod超大數^{超大數} \quad mod \quad 超大數,這個問題呢就需要使用到蒙哥馬利方法了。
  3. 如何產生兩個隨機1024位大質數?這個問題在Java直接利用如下方法即可:
public static BigInteger[] getRandomPQ() {
        BigInteger p = BigInteger.probablePrime(numLength, new Random());
        while (!p.isProbablePrime(accuracy)) {
            p = BigInteger.probablePrime(numLength, new Random());
        }
        BigInteger q = BigInteger.probablePrime(numLength, new Random());
        while (!q.isProbablePrime(accuracy)) {
            q = BigInteger.probablePrime(numLength, new Random());
        }
        return new BigInteger[]{p, q};
    }

不僅可以產生指定長度的大數,還可以控制其爲素數的準確率,即:1(2accuracy)1-(2^{-accuracy}),accuracy越大爲素數的準確率就越高。

Java代碼實現

import java.math.BigInteger;
import java.util.Random;

public class RSA {
    private final static int numLength = 1024;//素數長度
    private final static int accuracy = 100;//素數的準確率爲1-(2^(-accuracy))

    //獲取最大公約數
    private BigInteger getGCD(BigInteger a, BigInteger b) {
        if (b.byteValue() == 0) return a;
        return getGCD(b, a.mod(b));
    }

    //擴展歐幾里得方法,計算 ax + by = 1中的x與y的整數解(a與b互質)
    private static BigInteger[] extGCD(BigInteger a, BigInteger b) {
        if (b.signum() == 0) {
            return new BigInteger[]{a, new BigInteger("1"), new BigInteger("0")};
        } else {
            BigInteger[] bigIntegers = extGCD(b, a.mod(b));
            BigInteger y = bigIntegers[1].subtract(a.divide(b).multiply(bigIntegers[2]));
            return new BigInteger[]{bigIntegers[0], bigIntegers[2], y};
        }
    }

    //超大整數超大次冪然後對超大的整數取模,利用蒙哥馬利乘模算法,
    //(base ^ exp) mod n
    //依據(a * b) mod n=(a % n)*(b % n) mod n
    private static BigInteger expMode(BigInteger base, BigInteger exp, BigInteger mod) {
        BigInteger res = BigInteger.ONE;
        //拷貝一份防止修改原引用
        BigInteger tempBase = new BigInteger(base.toString());
        //從左到右實現簡答
        /*
            D=1
            WHILE E>=0
            IF E%2=0
                C=C*C % N
            E=E/2
          ELSE
            D=D*C % N
            E=E-1
          RETURN D
        */
        for (int i = 0; i < exp.bitLength(); i++) {
            if (exp.testBit(i)) {//判斷對應二進制位是否爲1
                res = (res.multiply(tempBase)).mod(mod);
            }
            tempBase = tempBase.multiply(tempBase).mod(mod);
        }
        return res;
    }

    //產生公鑰與私鑰
    public static SecretKey generateKey(BigInteger p, BigInteger q) {
        //令n = p * q。取 φ(n) = (p-1) * (q-1)。
        BigInteger n = p.multiply(q);
        //計算與n互質的整數個數 歐拉函數
        BigInteger fy = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
        //取 e ∈ [1 < e < φ(n) ] ,( n , e )作爲公鑰對,這裏取65537
        BigInteger e = new BigInteger("65537");
        //計算ed與fy的模反元素d。令 ed mod φ(n)  = 1,計算d,然後將( n , d ) 作爲私鑰對
        BigInteger[] bigIntegers = extGCD(e, fy);
        //計算出的x不能是負數,如果是負數,則進行x=x+fy。使x爲正數,但是x<fy。
        BigInteger x = bigIntegers[1];
        if (x.signum() == -1) {
            x = x.add(fy);
        }
        //返回計算出的密鑰
        return new SecretKey(n, e, x);
    }

    public static SecretKey generateKey() {
        BigInteger[] pq = getRandomPQ();
        return generateKey(pq[0], pq[1]);
    }

    //加密
    public static BigInteger encrypt(BigInteger text, SecretKey.PublicKey publicKey) {
        return expMode(text, publicKey.e, publicKey.n);
    }

    //解密
    public static BigInteger decrypt(BigInteger cipher, SecretKey.PrivateKey privateKey) {
        return expMode(cipher, privateKey.d, privateKey.n);
    }

    //加密
    public static String encrypt(String text, SecretKey.PublicKey publicKey) {
        return encrypt(new BigInteger(text.getBytes()), publicKey).toString();
    }

    //解密
    public static String decrypt(String chipper, SecretKey.PrivateKey privateKey) {
        BigInteger bigInteger = expMode(new BigInteger(chipper), privateKey.d, privateKey.n);
        byte[] bytes = new byte[bigInteger.bitLength() / 8 + 1];
        for (int i = 0; i < bytes.length; i++) {
            for (int j = 0; j < 8; j++) {
                if (bigInteger.testBit(j + i * 8)) {
                    bytes[bytes.length - 1 - i] |= 1 << j;
                }
            }
        }
        return new String(bytes);
    }

    //產生兩個隨機1024位大質數
    public static BigInteger[] getRandomPQ() {
        BigInteger p = BigInteger.probablePrime(numLength, new Random());
        while (!p.isProbablePrime(accuracy)) {
            p = BigInteger.probablePrime(numLength, new Random());
        }
        BigInteger q = BigInteger.probablePrime(numLength, new Random());
        while (!q.isProbablePrime(accuracy)) {
            q = BigInteger.probablePrime(numLength, new Random());
        }
        return new BigInteger[]{p, q};
    }

    //密匙對
    static class SecretKey {
        BigInteger n, e, d;

        public SecretKey(BigInteger n, BigInteger e, BigInteger d) {
            this.n = n;
            this.e = e;
            this.d = d;
        }

        public PublicKey getPublicKey() {
            return new PublicKey(n, e);
        }

        public PrivateKey getPrivateKey() {
            return new PrivateKey(n, d);
        }

        //密鑰
        static class PrivateKey {
            public BigInteger n, d;

            public PrivateKey(BigInteger n, BigInteger d) {
                this.n = n;
                this.d = d;
            }
        }

        //公鑰
        static class PublicKey {
            public BigInteger n, e;

            public PublicKey(BigInteger n, BigInteger e) {
                this.n = n;
                this.e = e;
            }
        }
    }


    public static void main(String[] args) {
        SecretKey secretKey = RSA.generateKey();
        //明文內容不要超過1024位,超過後需要分段加密
        String text = "Hello world";
        String chipper = RSA.encrypt(text, secretKey.getPublicKey());

        System.out.println("加密後:\n" +
                //密文長度可能會隨着隨機密鑰的改變而改變,最長不超過2048位
                "密文二進制長度:" + new BigInteger(chipper).bitLength()
                + "\n"
                + chipper);
        String origin = RSA.decrypt(chipper, secretKey.getPrivateKey());
        System.out.println("解密後:\n" + origin);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章