Created
May 20, 2020 18:56
-
-
Save mnrn/d662c34c3e36264d9e97cdc8ebb7474a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @brief RSA鍵暗号のサンプルコード | |
| * @date 2016/07/03 | |
| * @ref techtipshoge.blogspot.com/2014/04/javarsa.html | |
| */ | |
| //******************************************************************************** | |
| // パッケージ宣言 | |
| //******************************************************************************** | |
| package rsakey; | |
| //******************************************************************************** | |
| //必要なJavaファイルのインポート | |
| //******************************************************************************** | |
| import java.math.BigInteger; | |
| import java.util.Random; | |
| import java.util.Scanner; | |
| //******************************************************************************** | |
| // クラスの定義 | |
| //******************************************************************************** | |
| /** | |
| * @brief RSA鍵暗号のテストプログラム | |
| * @note ASCIIコードに対して暗号化を行います | |
| */ | |
| public class RSA { | |
| /** | |
| * @brief ここからはじまります | |
| * @param[in] args コマンドライン引数 | |
| */ | |
| public static void main(String[] args) { | |
| // メッセージ読み込み | |
| System.out.println("Input text"); | |
| Scanner sc = new Scanner(System.in); | |
| String msg = sc.nextLine(); | |
| // RSAテスト実行 | |
| RSA instance = new RSA(); | |
| instance.execute(msg); | |
| } | |
| /** | |
| * @brief RSAのテストプログラムを実行します | |
| * @param[in] msg 暗号化するメッセージ | |
| */ | |
| private void execute(String msg) { | |
| // RSA暗号鍵の生成 | |
| BigInteger[] keys = generate(KEY_BIT); | |
| // 正確には公開鍵:P = pair(public, common)であり、秘密鍵:S = pair(secret, common)と表される | |
| System.out.println("common : " + keys[COMMON_INDEX].toString(RADIX)); | |
| System.out.println("public : " + keys[PUBLIC_INDEX].toString(RADIX)); | |
| System.out.println("screte : " + keys[SECRET_INDEX].toString(RADIX)); | |
| // テキストの暗号化 | |
| String encrypted = encrypt(msg, keys[PUBLIC_INDEX], keys[COMMON_INDEX]); | |
| System.out.println("cipher : " + encrypted); | |
| // テキストの復号化 | |
| String decrypted = decrypt(encrypted, keys[SECRET_INDEX], keys[COMMON_INDEX]); | |
| System.out.println("plain : " + decrypted); | |
| } | |
| /** | |
| * @brief RSAの鍵を生成します。 | |
| * @param bitlen 共通鍵nの最大ビット長 | |
| * @return 鍵の配列 | |
| */ | |
| private BigInteger[] generate(int bitlen) { | |
| BigInteger[] keys = new BigInteger[KEYS_NUM]; | |
| Random rnd = new Random(); | |
| for (;;) { | |
| BigInteger p = BigInteger.probablePrime(bitlen >> 1, rnd); // 確率的素数pを取得 | |
| BigInteger q = BigInteger.probablePrime(bitlen >> 1, rnd); // 確率的素数qを取得 | |
| if (p.equals(q)) { continue; } // p = qならばやりなおし | |
| // n = pqとしたときのΦ(n)を計算(ただし、ΦはオイラーのΦ関数でΦ(n) = (p - 1)(q - 1)) | |
| BigInteger phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE)); | |
| BigInteger e = BigInteger.probablePrime(bitlen, rnd); // Φ(n)と互いに素な奇数eを選ぶ | |
| if (!e.gcd(phi).equals(BigInteger.ONE)) { continue; } // gcd(e, Φ(n)) = 1でなければやりなおし | |
| keys[COMMON_INDEX] = p.multiply(q); // n = pqとする | |
| keys[PUBLIC_INDEX] = e; // eを公開鍵として扱う | |
| keys[SECRET_INDEX] = e.modInverse(phi); // 法Φ(n)の下でのeの逆元dを秘密鍵として扱う | |
| break; | |
| } | |
| return keys; | |
| } | |
| /** | |
| * @brief メッセージの暗号化を行う | |
| * @note RSA公開鍵暗号系において、メッセージMに対し署名を行うことに当たる | |
| * @param msg 元の文章 | |
| * @param e 公開鍵 | |
| * @param n 公開鍵 | |
| * @return 暗号化された文 | |
| */ | |
| private String encrypt(String msg, BigInteger e, BigInteger n) { | |
| BigInteger a = encode(msg); // メッセージをBigIntegerへ変換 | |
| return a.modPow(e, n).toString(RADIX); // 公開鍵演算 P(M) = M^e mod n | |
| } | |
| /** | |
| * @brief 暗号文に対して復号を行う | |
| * @note RSA公開鍵暗号系において、署名の検証、確認に当たる | |
| * @param ciphertext 暗号文 | |
| * @param d 秘密鍵 | |
| * @param n 秘密鍵 | |
| * @return 復号された文 | |
| */ | |
| private String decrypt(String ciphertext, BigInteger d, BigInteger n) { | |
| BigInteger a = new BigInteger(ciphertext, RADIX); // 暗号文をBigIntegerへ変換 | |
| return decode(a.modPow(d, n)); // 秘密鍵演算 S(C) = C^d mod n | |
| } | |
| /** | |
| * @brief ASCII文字列をBigIntegerに変換する | |
| * @param msg 変換対象のASCII文字列 | |
| * @return 変換されたBigInteger | |
| */ | |
| private BigInteger encode(String msg) { | |
| BigInteger code = BigInteger.ZERO; | |
| for (int i = 0; i < msg.length(); i++) { | |
| code = code.multiply(BigInteger.valueOf(CHAR_NUM_MAX)); | |
| code = code.add(BigInteger.valueOf(msg.charAt(i))); | |
| } | |
| return code; | |
| } | |
| /** | |
| * @brief 256進数のBigIntegerをASCII文字列に変換する | |
| * @param code 変換対象のBigInteger | |
| * @return 変換されたASCII文字列 | |
| */ | |
| private String decode(BigInteger code) { | |
| StringBuilder sb = new StringBuilder(); | |
| while (code.compareTo(BigInteger.ZERO) > 0) { | |
| int rem = code.mod(BigInteger.valueOf(CHAR_NUM_MAX)).shortValue(); | |
| sb.append((char)rem); | |
| code = code.divide(BigInteger.valueOf(CHAR_NUM_MAX)); | |
| } | |
| return sb.reverse().toString(); | |
| } | |
| private final int COMMON_INDEX = 0; /**< 鍵の要素nの添字 */ | |
| private final int PUBLIC_INDEX = 1; /**< 鍵の要素eの添字 */ | |
| private final int SECRET_INDEX = 2; /**< 鍵の要素dの添字 */ | |
| private final int KEYS_NUM = 3; /**< 鍵の数 */ | |
| private final int RADIX = 16; /**< 16進数で表記します */ | |
| private final int KEY_BIT = 1024; /**< 鍵のビット長 */ | |
| private final int CHAR_NUM_MAX = 256; /**< ASCII文字の最大数 */ | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment