ElGamal暗号化アルゴリズム
4818 ワード
ElGamalアルゴリズムは一般的な暗号化アルゴリズムであり、Diffie-Hellmanアルゴリズムと密接に関連している.
このアルゴリズムの安全性は,有限領域における離散対数問題の計算に依存している.離散対数を解くことは困難であり,その逆演算指数演算は簡単である.
アルゴリズムの考え:AliceとBobが2人いると仮定し、BobはElGamal暗号化アルゴリズムを使ってAliceに情報を送信しようとしている.Aliceに対しては、まず素数qを選択します.α素数qの素です.[本ルートの概念は、モードq乗算群(要循環群)の生成元に対応する.] AliceはXA、XA∈(1、q-1) を生成する.計算YA=αXA mod q Aの秘密鍵はXAであり、公開鍵は{q、α, YA} 公開鍵は信頼された公開センターディレクトリに存在します.どのユーザもアクセスできます.
Bobに対しては、まず上記の中心ディレクトリに行ってAliceの公開鍵{q,α, そして自分が送りたい明文Mをきれいに洗って準備します.ランダム整数kを選択します.k∈[1,q-1] は、自分の暗号文を解読することができる秘密鍵PrivatK=(YA)k mod qを計算する.αXA*k mod q) はMを平文ペア(C 1,C 2)に暗号化する.ここでC 1=αk mod q,C 2=PrivatK*M mod q 明文ペアはAlice に送信されます.
Aliceは明文を受け取りました. Private K=(C 1)XA mod q(つまり、XA mod q)αk*XA mod q) M=C 2*Private K-1 mod q ここに来てください.アルゴリズムの多くは掛け算、べき乗などの演算です.残りの肝心な内容は素数pの原根をどうやって探しますか?あるいは有限ドメインGF(p)の生成元をどうやって探しますか?グループという概念で議論します.pは素数で、Zp={1,2,3,…,p-1}を掛け算を考えて、0元素を抜きました.2つの定理: Euler定理:Pとaをラッセルの二つの整数とすると、aがある.φ(p)=1 mod p ラグランジュの定理:Gを有限群とし、HをGのサブ群とし、124 Hを12464;G 12462を排除する.
このような2つの概念を振り返ると、Gは群であり、a_Gは、式a k=eが成立する最小整数kを要素aの段と呼ぶ.群の段はその群の中の要素の個数を指す.注目すべきは、ある生成元であるサブ群を生成する.このサブ群の次数はこの要素の次数である.
Zp中のすべての元素はpと相互素であるため、Oullによって定理され、Zp中のすべての元素の次数はp-1を超えない.φ(p)はp-1で、少なくともaがあります.φ(p)=1 mod p)です.Zpのいずれかの要素に対して、この元素を生成元として形成される循環群は、S(群Sの次数が数値的にはこの元素の次数)として設定されています.群の閉鎖性から、SがZpのサブグループであることが分かります.ラグランジュの定理から、Zpの要素の次数は、必ず124 Zp−1の因子であることが分かります.
したがって、aのような要素があれば、Kaの次数はp−1の平凡な因子(すなわち因子1または因子p−1)であると結論することができる.Aまたは単位元、または生成元は、Zpの単位元が1であるということを知っていますが、単位元の一意性によって、aが1でないとaは必ず生成元となります.問題は、p-1の因子が多いかもしれません.階段はp-1の平凡な要素ではないですか?
このため、p-1の因子数が少ないように特殊な素数を構築しました.p-1=2*Qを取って、pは素数で、Qも素数です.Qは素数ですので、因子は1、Qだけです.p-1の因子は{1、2、Q、p-1}だけです.以上で明らかになりました.上記の条件を満たす素数pを見つけて、Zpの中でこのような元素aを探します.aの次数は2ではなく、Qではなく、a^2 mod pです.=1&a^Q mod p!=1、aが単位でない場合は、aは必ず元を生成します.
注意Zpは必ずしも生成元があるとは限らないので、もし1から(p-1)までは上記の検査を経ても満足できないなら、もう一つの素数pを取りたいです.コード実現上の問題点は、mpzubupum(tmp.mt,6)==1をmpzubuplimeup(tmp.mt,6)=2に変更すると、本当に速いです.
コードの実装
このアルゴリズムの安全性は,有限領域における離散対数問題の計算に依存している.離散対数を解くことは困難であり,その逆演算指数演算は簡単である.
アルゴリズムの考え:AliceとBobが2人いると仮定し、BobはElGamal暗号化アルゴリズムを使ってAliceに情報を送信しようとしている.Aliceに対しては、まず素数qを選択します.α素数qの素です.[本ルートの概念は、モードq乗算群(要循環群)の生成元に対応する.]
Bobに対しては、まず上記の中心ディレクトリに行ってAliceの公開鍵{q,α, そして自分が送りたい明文Mをきれいに洗って準備します.
Aliceは明文を受け取りました.
このような2つの概念を振り返ると、Gは群であり、a_Gは、式a k=eが成立する最小整数kを要素aの段と呼ぶ.群の段はその群の中の要素の個数を指す.注目すべきは、ある生成元であるサブ群を生成する.このサブ群の次数はこの要素の次数である.
Zp中のすべての元素はpと相互素であるため、Oullによって定理され、Zp中のすべての元素の次数はp-1を超えない.φ(p)はp-1で、少なくともaがあります.φ(p)=1 mod p)です.Zpのいずれかの要素に対して、この元素を生成元として形成される循環群は、S(群Sの次数が数値的にはこの元素の次数)として設定されています.群の閉鎖性から、SがZpのサブグループであることが分かります.ラグランジュの定理から、Zpの要素の次数は、必ず124 Zp−1の因子であることが分かります.
したがって、aのような要素があれば、Kaの次数はp−1の平凡な因子(すなわち因子1または因子p−1)であると結論することができる.Aまたは単位元、または生成元は、Zpの単位元が1であるということを知っていますが、単位元の一意性によって、aが1でないとaは必ず生成元となります.問題は、p-1の因子が多いかもしれません.階段はp-1の平凡な要素ではないですか?
このため、p-1の因子数が少ないように特殊な素数を構築しました.p-1=2*Qを取って、pは素数で、Qも素数です.Qは素数ですので、因子は1、Qだけです.p-1の因子は{1、2、Q、p-1}だけです.以上で明らかになりました.上記の条件を満たす素数pを見つけて、Zpの中でこのような元素aを探します.aの次数は2ではなく、Qではなく、a^2 mod pです.=1&a^Q mod p!=1、aが単位でない場合は、aは必ず元を生成します.
注意Zpは必ずしも生成元があるとは限らないので、もし1から(p-1)までは上記の検査を経ても満足できないなら、もう一つの素数pを取りたいです.コード実現上の問題点は、mpzubupum(tmp.mt,6)==1をmpzubuplimeup(tmp.mt,6)=2に変更すると、本当に速いです.
コードの実装
#include
#include
#include
using namespace std;
#define mt get_mpz_t()
typedef mpz_class bn;
gmp_randstate_t rstate;
struct public_keys {
// Big prime p, primitive root, Y = XA^(primitive root)
bn p, pr, Y;
public_keys(bn _p = 0, bn _pr = 0, bn _Y = 0) : p(_p), pr(_pr), Y(_Y) {}
};
// Trusted third party
map ttp;
// start end
inline bn randNum(bn start, bn end) {
bn tmp;
mpz_urandomm(tmp.mt, rstate, bn(end + 1 - start).mt);
return tmp + start;
}
// a^b mod n
inline bn aebmodn(bn a, bn b, bn n) {
bn ret;
mpz_powm(ret.mt, a.mt, b.mt, n.mt);
return ret;
}
// 2^bits , , name
bn elgmal_keyGen(string name, unsigned long int bits) {
bn p = 2, pr = -1, Y = -1, bounds = 2;
mpz_pow_ui(bounds.mt, bounds.mt, bits);
bool found = 0;
while(1) {
mpz_urandomm(p.mt, rstate, bounds.mt);
mpz_nextprime(p.mt, p.mt);
bn tmp = (p - 1) / 2;
if (p < bounds && mpz_probab_prime_p(tmp.mt, 6) == 1) {
// pr 1 p-1
pr = randNum(1, p - 1);
while (1) {
// pr^tmp % p
bn pexpn = aebmodn(pr, tmp, p);
if (pr != 1 && (pr * pr) % p != 1 && pexpn != 1) {
found = 1;
break;
}
pr = (pr + 1) % p;
}
}
if (found) break;
}
bn XA = randNum(2, p - 2);
Y = aebmodn(pr, XA, p);
ttp[name] = public_keys(p, pr, Y);
return XA;
}
// (C1, C2)
struct cipher_text {
bn c1, c2;
cipher_text(bn _c1 = 0, bn _c2 = 0) : c1(_c1), c2(_c2) {}
};
/*
* name m
* (C1, C2)
*/
cipher_text elgamal_encrypt(string name, bn m) {
public_keys pk = ttp[name];
bn k = randNum(1, pk.p - 1);
bn privateKey;
mpz_powm(privateKey.mt, pk.Y.mt, k.mt, pk.p.mt);
bn cipher1 = aebmodn(pk.pr, k, pk.p);
bn cipher2 = (m * privateKey) % pk.p;
return cipher_text(cipher1, cipher2);
}
// name dk ct
bn elgamal_decrypt(string name, bn dk, cipher_text ct) {
public_keys pk = ttp[name];
bn privateKey = aebmodn(ct.c1, dk, pk.p);
bn k_inverse;
mpz_invert(k_inverse.mt, privateKey.mt, pk.p.mt);
return (ct.c2 * k_inverse) % pk.p;
}
int main() {
gmp_randinit_mt(rstate);
gmp_randseed_ui(rstate, time(NULL));
// Alice
bn dk = elgmal_keyGen("Alice", 128);
cout<>n;
if (n > ttp["Alice"].p) cout<
» ./ElGamal
Input the message: 1024
decrypt result : 1024
このブログとLJFさんの討論を参考にしました.