質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

1回答

572閲覧

java 累乗後の数の余りを求める計算の結果が想定と異なっている問題

tamintya

総合スコア34

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

1グッド

0クリップ

投稿2022/06/28 01:03

編集2022/06/28 04:55

RSA暗号のアルゴリズムを簡単に実装してみたのですが、暗号化した文章を復号化するときに失敗することがありました。
調べたところ"pow(tmpi,d) % n"の計算結果が関数電卓の結果と異なるときがあるのですが原因が分からないので教えていただきたいです。

該当のソースコード

java

1import java.util.Scanner; 2import java.util.ArrayList; 3import java.util.List; 4 5public class Main{ 6 public static void main(String[] args){ 7 8 //鍵共有 9 int p = 3; 10 int q = 43; 11 int n = p*q; //129 12 int pq2 = (p-1)*(q-1); // 84 13 14 int i = 2; 15 int judgement = 1; 16 int e = 0; 17 while(judgement == 1){ //ユークリッドの互除法でたがいに素なeを求める 18 if(Calc.euclid(pq2,i) == 1){ //eが求まった場合 19 e = i; 20 break; 21 }; 22 i++; 23 } 24 25 System.out.println("e = " + e); 26 27 int d = 0; 28 while(judgement == 1){ //dを求める 29 int tmp = (e*i) % pq2; //余りを求め1なら終了 30 if(tmp == 1){ 31 d = i; 32 break; 33 } 34 i++; 35 } 36 System.out.println("d = " + d); 37 38 39 Scanner scan = new Scanner(System.in); 40 System.out.println("暗号化なら1 , 復号化なら2 を入力してください。"); 41 String judge = scan.nextLine(); 42 43 //暗号化 44 if(judge.equals("1")){ 45 List<String> C = new ArrayList<>(); 46 System.out.println("暗号化したい文章を入力してください。"); 47 System.out.print("M = "); 48 49 String word = scan.nextLine();//文章の読み込み 50 C = Encryption.Enc(word, e, n);//暗号化 51 52 System.out.print("C = "); //暗号文の表示 53 for(i=0;i<C.size();i++){ 54 System.out.print(C.get(i)); 55 } 56 scan.close(); 57 } 58 59 //復号化 60 else if(judge.equals("2")){ 61 List<Character> M = new ArrayList<>(); 62 List<String> Mtmp = new ArrayList<>(); 63 64 System.out.println("復号化したい文章を入力してください。"); 65 System.out.print("C = "); 66 String word = scan.nextLine(); 67 for(int j=0;j<word.length();j++){ //8文字ごとに切り出し 68 String tmps = word.substring(j, j+8); 69 j = j+7; 70 Mtmp.add(tmps); 71 } 72 73 M = Decryption.Dec(Mtmp, d, n); //復号化 74 75 System.out.print("M = "); //平文の表示 76 for(int j=0;j<M.size();j++){ 77 System.out.print(M.get(j)); 78 } 79 scan.close(); 80 } 81 82 } 83 84} 85 ---------------------------------------------------------------------------------- 86import java.util.ArrayList; 87import java.util.List; 88 89public class Encryption { 90 public static List<String> Enc(String word, int e, int n){ 91 List<Integer> M = new ArrayList<>(); 92 List<String> C = new ArrayList<>(); 93 94 for(int i=0;i<word.length();i++){ //平文をASCⅡコードに変換し一文字ずつリストに格納 95 char tmpc = word.charAt(i); 96 System.out.println("ASC2 = " + (int)tmpc); 97 M.add((int)tmpc); 98 } 99 100 for(int i=0;i<word.length();i++){ //暗号化を行う 101 int tmpi = M.get(i); 102 double tmpd = Math.pow(tmpi,e) % n; //暗号獲得 103 System.out.println("10進数 = " + tmpd); 104 Integer tmpin = Integer.valueOf((int)tmpd); //int型に変換 105 System.out.println("int = " + tmpin); 106 String tmps = Integer.toBinaryString(tmpin); //2進数に変換 107 System.out.println("2進数 = " + tmps); 108 Integer tmpin2 = Integer.valueOf(tmps); //int型に変換 109 System.out.println("int = " + tmpin2); 110 String tmps2 = String.format("%08d", tmpin2); //0パディング 111 C.add(tmps2); 112 } 113 114 return C; 115 116 } 117} 118 119 ---------------------------------------------------------------------------------- 120import java.util.ArrayList; 121import java.util.List; 122 123public class Decryption { 124 public static List<Character> Dec(List<String> C, int d, int n){ 125 List<Character> M = new ArrayList<>(); 126 127 for(int i=0;i<C.size();i++){ //復号化 128 int tmpi = Integer.parseInt(C.get(i),2); //10進数に変換 129 System.out.println("10進数 = " + tmpi); 130 131 System.out.println("d : n = " + d + " : " + n); 132 double tmpid = Math.pow(tmpi, d); 133 System.out.println("tmpi^d : " + tmpi + "^" + d + " = " + Math.pow(tmpi, d)); 134 System.out.println("tmpid % n : " + tmpid + "%" + n + " = " + tmpid % n); 135 136 double tmpd = Math.pow(tmpi,d) % n; //復号化 137 System.out.println("変換後 = " + tmpd); 138 int tmpin = Integer.valueOf((int)tmpd); //int型に変換 139 System.out.println("int = " + tmpin); 140 M.add((char)tmpin); //ASCⅡに変換 141 } 142 143 return M; 144 } 145} 146 147---------------------------------------------------------------------------------- 148public class Calc { 149 public static int euclid(int n, int i){ 150 int big = Math.max(n, i); 151 int small = Math.min(n, i); 152 153 int sur = big % small; 154 155 if(sur == 0){ 156 return small; 157 } 158 159 sur = euclid(small, sur); 160 161 return sur; 162 } 163 164} 165

実行結果

C:\Users\RSA暗号_ソースコード>java Main e = 5 d = 17 暗号化なら1 , 復号化なら2 を入力してください。 1 暗号化したい文章を入力してください。 M = a ASC2 = 97 10進数 = 16.0 int = 16 2進数 = 10000 int = 10000 C = 00010000 C:\Users\RSA暗号_ソースコード>java Main e = 5 d = 17 暗号化なら1 , 復号化なら2 を入力してください。 2 復号化したい文章を入力してください。 C = 00010000 10進数 = 16 d : n = 17 : 129 tmpi^d : 16^17 = 2.9514790517935283E20 tmpid % n : 2.9514790517935283E20%129 = 97.0 変換後 = 97.0 int = 97 M = a C:\Users\RSA暗号_ソースコード>java Main e = 5 d = 17 暗号化なら1 , 復号化なら2 を入力してください。 1 暗号化したい文章を入力してください。 M = I ASC2 = 73 10進数 = 55.0 int = 55 2進数 = 110111 int = 110111 C = 00110111 C:\Users\RSA暗号_ソースコード>java Main e = 5 d = 17 暗号化なら1 , 復号化なら2 を入力してください。 2 復号化したい文章を入力してください。 C = 00110111 10進数 = 55 d : n = 17 : 129 tmpi^d : 55^17 = 3.856254795069075E29 tmpid % n : 3.856254795069075E29%129 = 64.0 変換後 = 64.0 int = 64 M = @

追記

3回目の実行時にclass Decryptionのdouble tmpd = Math.pow(tmpi,d) % n; //復号化の計算結果が73になると想定したが実行すると64が返されてしまう点が違っている。

jimbe👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2022/06/28 02:12

どこがどういうふうに違ってるんでしょうか
tamintya

2022/06/28 04:47

ご指摘ありがとうございます。 違っている点を質問文に追記しました。
guest

回答1

0

ベストアンサー

double tmpd = Math.pow(tmpi,d) % n; //復号化 など各所でdoubleを使っていますが、

doubleの有効桁数は10進数で約16桁です。
Math.pow(a,b)の戻り値はdoubleなので結果が大きな値になる累乗の計算はどうしようもありません。

値が大きくなることを避けるために累乗の剰余算については、
a*bをnで割った余りは、(aをnで割った余り)*(bをnで割った余り)nで割った余りに等しい」
という性質を使って普通計算します。

端的に言えば、掛け算を複数回した余りを求めるときは、掛け算1回ごとに余りを取ります。
例えば(a*b*c)%n((((a%n)*b)%n)*c)%nと計算した方がいいということです。

参考
mod の下での指数計算をするアルゴリズムを python で書いてみる - わかばめにっき

投稿2022/06/28 01:17

編集2022/06/28 05:25
ozwk

総合スコア13521

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tamintya

2022/06/28 04:53

回答ありがとうございます。そのような性質があることを初めて知りました! しかし、ご指摘の計算方法を変更しても結果が変わらないのですが他にどの部分に間違いがあるのでしょうか。
ozwk

2022/06/28 05:20

追記編集しました
tamintya

2022/06/28 09:38 編集

掛け算1回ごとに余りを求める用に変更したところ目的の結果を得ることができました。 分かりやすく説明していただきありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問