RSA 隨記

先前弄SSL接觸了RSA算法,有點好奇其中的實現,畢竟以前的Cipher類到計算那部分就看不懂只能放棄了。因此今天補課了一下RSA的算法。java

簡介

RSA加密算法是一種非對稱加密算法。在公開密鑰加密和電子商業中RSA被普遍使用。RSA是1977年由羅納德·李維斯特(Ron Rivest)、阿迪·薩莫爾(Adi Shamir)和倫納德·阿德曼(Leonard Adleman)一塊兒提出的。當時他們三人都在麻省理工學院工做。RSA就是他們三人姓氏開頭字母拼在一塊兒組成的。算法

對極大整數作因數分解的難度決定了RSA算法的可靠性。安全

也就是說RSA算法的數學基礎涉及到了因數分解。app

相關計算

RSA算法由公鑰(PublicKey)和私鑰(PrivateKey)組成。測試

而密鑰主要由四個因子構成:N、L、E、D;而這四個因子的計算由兩個基礎的質數q和p組成。加密

這四個因子的計算方式分別是:spa

  • N : q * p
  • L : (q - 1)和(p - 1)的最小公倍數
  • E : E和L的最大公約數爲1(1 < E < L)。
  • D : D * E mod L = 1。即D和E的乘積除以L的餘數爲1。

而後是加密和解密的過程:code

  • 加密:密文 = 明文^E mod N。(RSA加密是對明文的E次方後除以N後求餘數的過程。)
  • 解密:明文 = 密文^D mod N。(RSA加密是對明文的D次方後除以N後求餘數的過程。)

驗證

知道了計算方法,那麼就開始寫個最簡單的Demo來驗證。cdn

//p = 3, q = 11
    private void rsa(int p, int q, String d) {
        int N = p * q;
        int L = IntStream
            .iterate(Math.max(p - 1, q - 1), i -> i + 1)
            .filter(i -> i % (p - 1) == 0 && i % (q - 1) == 0)
            .findFirst()
            .getAsInt();
        int E = IntStream
            .iterate(1, i -> i + 1)
            .filter(i -> L % i != 0)
            .findFirst()
            .getAsInt();
        int D = IntStream
            .iterate(1, i -> i + 1)
            .filter(i -> (E * i) % L == 1 && i < L)
            .findFirst()
            .getAsInt();

        // System.out.println("N:" + N + ";L:" + L + ";E:" + E + ";D:" + D);

        IntFunction<Byte> encode = (int data) -> Byte.valueOf(String.valueOf((int) (Math.pow((double) data, (double) E) % N)));
        IntFunction<Byte> decode = (int data) -> Byte.valueOf(String.valueOf((int) (Math.pow((double) data, (double) D) % N)));

        byte[] bytes = d.getBytes();
        byte[] encodeBytes = new byte[bytes.length];
        byte[] decodeBytes = new byte[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            encodeBytes[i] = encode.apply(bytes[i]);
        }

        for (int i = 0; i < bytes.length; i++) {
            decodeBytes[i] = decode.apply(encodeBytes[i]);
        }

        System.out.println(Arrays.toString(bytes));
        System.out.println(Arrays.toString(encodeBytes));
        System.out.println(Arrays.toString(decodeBytes));
    }
複製代碼

程序寫好就開始測試了,其中個人p=3,q=11。blog

可是這裏卻出現了問題,當我傳入字符串"key"的時候計算的結果以下:

可是我Demo就是根據原理來的,那是哪裏出問題了呢?

能夠發現,加密並解密出來的數字都沒有超過33,而後將數據直接修改爲{32, 33, 34}能夠發現當明文數字小於33的時候運算結果正確。那就能夠得出結論N(Number)決定了RSA能加密的最大數字。

其實細心點研究公式就能夠早早發現這個問題:公式中最後的計算結果須要mod N,那麼最終的結果天然是小於N了。

也就是說取的q和p的大小直接決定了加密的明文的大小範圍。因此網上許多資料都是直接說這兩個取值最好往大了取。

固然,當值取的過大的時候就會增長計算的時間開銷,目前PKCS#1給的建議是E=65537,該值是除了一、三、五、1七、257以外的最小素數。

補充

當前說起RSA密鑰的長度的時候都是指的它的模值的位長度。目前主流的由102四、204八、307二、4096等。低於1024的因爲安全問題已不建議使用。

那麼我這個Demo中的模值爲33,位長度爲6bits,最大加密1Byte不到2Byte,小的可憐。

那麼若是加密的時候位數不到怎麼辦,就須要進行padding,由於若是沒有padding,用戶沒法確分解密後內容的真實長度,字符串之類的內容問題還不大,以0做爲結束符,但對二進制數據就很難理解,由於不肯定後面的0是內容仍是內容結束符。其中PKCS#1建議的padding就佔用了11個字節。所以在使用RSA1024的時候加密數據只有117字節。這是爲了防止加密的數據爲二進制的時候沒法區分位的結尾。

其實我這個Demo是有不少錯誤的。因爲直接從byte轉化因此位數最大位8bits,不然就會出錯。

歡迎捉蟲

相關文章
相關標籤/搜索