diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Paillier.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Paillier.java new file mode 100644 index 000000000..a3baf728c --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/Paillier.java @@ -0,0 +1,195 @@ +package cn.hutool.crypto.asymmetric; + +import cn.hutool.core.util.HexUtil; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Random; + +/** + * 同态加密算法Paillier + * + * 加法同态,存在有效算法+,E(x+y)=E(x)+E(y)或者 x+y=D(E(x)+E(y))成立,并且不泄漏 x 和 y。 + * 乘法同态,存在有效算法*,E(x×y)=E(x)*E(y)或者 xy=D(E(x)*E(y))成立,并且不泄漏 x 和 y。 + * + * 方案安全性可以归约到判定性合数剩余假设(Decisional Composite Residuosity Assumption, DCRA),即给定一个合数n和整数z,判定z是否在n^2下是否是n次剩余是困难的。 + * 这个假设经过了几十年的充分研究,到目前为止还没有多项式时间的算法可以攻破,所以Paillier加密方案的安全性被认为相当可靠。 + * + * 字符串文本加解密相互配对,此时无法使用同态加法和同态乘法 + * 数值类型不可使用字符串加解密 + * + * 公钥加密和同态加法/同态乘法运算 + * 私钥解密 + * + * @author Revers. + **/ +public class Paillier { + + //公钥 n g + //私钥 n lambda u + private static int bitLength = 2048; + private static int certainty = 256; + + /** + * 生成密钥算法。(默认) + * @return PaillierKeyPair 公钥私钥对 + */ + public static final PaillierKeyPair generateKey() { + return generateKey(bitLength,certainty); + } + + /** + * 生成密钥算法 + * + * @param bitLength 密钥位数 + * @param certainty 此构造函数的执行时间与此参数的值成比例。 + * @return PaillierKeyPair 公钥私钥对 + */ + public static final PaillierKeyPair generateKey(int bitLength,int certainty) { + BigInteger p =new BigInteger(bitLength / 2, certainty, new SecureRandom()); + BigInteger q =new BigInteger(bitLength / 2, certainty, new SecureRandom()); + BigInteger n = p.multiply(q); + BigInteger nSquare = n.multiply(n); + BigInteger lambda = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE)) + .divide(p.subtract(BigInteger.ONE).gcd(q.subtract(BigInteger.ONE))); + BigInteger g = n.add(BigInteger.ONE); + BigInteger u = g.modPow(lambda, nSquare).subtract(BigInteger.ONE).divide(n).modInverse(n); + PaillierpublicKey publicKey = new PaillierpublicKey(n,g); + PaillierPrivateKey privateKey = new PaillierPrivateKey(n, lambda,u); + PaillierKeyPair keyPair = new PaillierKeyPair(publicKey,privateKey); + return keyPair; + } + + /** + * 字符串加密算法。输入一个明文和一个公钥 + * 只能使用字符串解密算法 decryptString ,不可使用同态加法和同态乘法 + * + * @param text 明文,字符串形式 + * @param publicKey 公钥 + * + * @return byte[]密文 + */ + public static final byte[] encryptString(String text, PaillierpublicKey publicKey) { + BigInteger r = new BigInteger(bitLength, new Random()); + BigInteger n = publicKey.getN(); + BigInteger nsquare = n.multiply(n); + return publicKey.getG().modPow( new BigInteger(HexUtil.encodeHexStr(text),16), nsquare).multiply(r.modPow(n, nsquare)).mod(nsquare).toByteArray(); + } + + /** + * 字符串解密算法。输入一个密文和一个私钥 + * + * @param ciphertext byte[]密文 + * @param privateKey 私钥 + * @return 解密的明文 + */ + public static final String decryptString(byte[] ciphertext, PaillierPrivateKey privateKey) { + BigInteger n = privateKey.getN(); + BigInteger lambda = privateKey.getLambda(); + BigInteger u = privateKey.getu(); + BigInteger nsquare = n.multiply(n); + String s = new BigInteger(ciphertext).modPow(lambda, nsquare).subtract(BigInteger.ONE).divide(n).multiply(u).mod(n).toString(); + return HexUtil.decodeHexStr(new BigInteger(s).toString(16)); + } + + /** + * 加密算法。输入一个明文和一个公钥 + * + * @param text 明文,为防止字符串数据,只能使用 BigInteger类型 + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] encrypt(BigInteger text, PaillierpublicKey publicKey) { + BigInteger r = new BigInteger(bitLength, new Random()); + BigInteger n = publicKey.getN(); + BigInteger nsquare = n.multiply(n); + return publicKey.getG().modPow(text, nsquare).multiply(r.modPow(n, nsquare)).mod(nsquare).toByteArray(); + } + + /** + * 解密算法。输入一个密文和一个私钥 + * + * @param ciphertext byte[]密文 + * @param privateKey 私钥 + * @return 解密的明文 + */ + public static final String decrypt(byte[] ciphertext, PaillierPrivateKey privateKey) { + BigInteger n = privateKey.getN(); + BigInteger lambda = privateKey.getLambda(); + BigInteger u = privateKey.getu(); + BigInteger nsquare = n.multiply(n); + return new BigInteger(ciphertext).modPow(lambda, nsquare).subtract(BigInteger.ONE).divide(n).multiply(u).mod(n).toString(); + } + + /** + * 同态标量加算法。输入两个密文和一个公钥 + * + * @param ciphertext 密文1,BigInteger + * @param ciphertext2 密文2,BigInteger + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] add(BigInteger ciphertext,BigInteger ciphertext2,PaillierpublicKey publicKey){ + return ciphertext.add(ciphertext2).multiply(publicKey.getN()).toByteArray(); + } + + /** + * 同态标量加算法。输入两个密文和一个公钥 + * + * @param ciphertext 密文1,10进制字符串 + * @param ciphertext2 密文2,10进制字符串 + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] add(String ciphertext,String ciphertext2,PaillierpublicKey publicKey){ + return new BigInteger(ciphertext).multiply(new BigInteger(ciphertext2)).mod(publicKey.getN().multiply(publicKey.getN())).toByteArray(); + } + + /** + * 同态标量加算法。输入两个密文和一个公钥 + * + * @param ciphertext byte[] 10进制字符串 + * @param ciphertext2 byte[] 10进制字符串 + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] add(byte[] ciphertext,byte[] ciphertext2,PaillierpublicKey publicKey){ + return new BigInteger(ciphertext).multiply(new BigInteger(ciphertext2)).mod(publicKey.getN().multiply(publicKey.getN())).toByteArray(); + } + + /** + * 同态标量乘算法。输入一个密文和一个标量明文和一个公钥 + * + * @param ciphertext 密文,BigInteger + * @param number 明文,10进制字符串 + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] multiply(BigInteger ciphertext,BigInteger number,PaillierpublicKey publicKey){ + return ciphertext.modPow(number,publicKey.getN().multiply(publicKey.getN())).toByteArray(); + } + + /** + * 同态标量乘算法。输入一个密文和一个标量明文和一个公钥 + * + * @param ciphertext 密文,byte[] + * @param number 明文,10进制字符串 + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] multiply(String ciphertext,BigInteger number,PaillierpublicKey publicKey){ + return new BigInteger(ciphertext).modPow(number,publicKey.getN().multiply(publicKey.getN())).toByteArray(); + } + + /** + * 同态标量乘算法。输入一个密文和一个标量明文和一个公钥 + * + * @param ciphertext 密文,byte[] + * @param number 明文,10进制字符串 + * @param publicKey 公钥 + * @return byte[]密文 + */ + public static final byte[] multiply(byte[] ciphertext,BigInteger number,PaillierpublicKey publicKey){ + return new BigInteger(ciphertext).modPow(number,publicKey.getN().multiply(publicKey.getN())).toByteArray(); + } +} diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierKeyPair.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierKeyPair.java new file mode 100644 index 000000000..cc68c1b3c --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierKeyPair.java @@ -0,0 +1,32 @@ +package cn.hutool.crypto.asymmetric; + +/** + * 存放Paillier 公钥私钥对 + * + * @author Revers. + **/ +public class PaillierKeyPair { + private PaillierPrivateKey privateKey; + private PaillierpublicKey publicKey; + + public PaillierKeyPair(PaillierpublicKey PaillierpublicKey,PaillierPrivateKey PaillierPrivateKey) { + this.privateKey = PaillierPrivateKey; + this.publicKey = PaillierpublicKey; + } + + public PaillierPrivateKey getPrivateKey() { + return privateKey; + } + + public void setPrivateKey(PaillierPrivateKey privateKey) { + this.privateKey = privateKey; + } + + public PaillierpublicKey getPublicKey() { + return publicKey; + } + + public void setPublicKey(PaillierpublicKey publicKey) { + this.publicKey = publicKey; + } +} diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierPrivateKey.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierPrivateKey.java new file mode 100644 index 000000000..a6af60d58 --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierPrivateKey.java @@ -0,0 +1,39 @@ +package cn.hutool.crypto.asymmetric; + +import java.math.BigInteger; + +/** 存放Paillier 私钥 + * + * @author Revers. + **/ +public class PaillierPrivateKey{ + private final BigInteger n; + private final BigInteger lambda; + private final BigInteger u; + + public PaillierPrivateKey(BigInteger n, BigInteger lambda, BigInteger u) { + if (n == null) { + throw new NullPointerException("n is null"); + } + if (lambda == null) { + throw new NullPointerException("lambda is null"); + } + if (u == null) { + throw new NullPointerException("u is null"); + } + this.n = n; + this.lambda = lambda; + this.u = u; + } + + public BigInteger getN() { + return n; + } + public BigInteger getLambda() { + return lambda; + } + public BigInteger getu() { + return u; + } + +} diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierpublicKey.java b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierpublicKey.java new file mode 100644 index 000000000..4aebdeade --- /dev/null +++ b/hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/PaillierpublicKey.java @@ -0,0 +1,30 @@ +package cn.hutool.crypto.asymmetric; + +import java.math.BigInteger; + +/** 存放Paillier 公钥 + * + * @author Revers. + **/ +public class PaillierpublicKey{ + private BigInteger n; + private BigInteger g; + + public PaillierpublicKey(BigInteger n, BigInteger g) { + if (n == null) { + throw new NullPointerException("n is null"); + } + if (g == null) { + throw new NullPointerException("g is null"); + } + this.n = n; + this.g = g; + } + + public BigInteger getN() { + return n; + } + public BigInteger getG() { + return g; + } +} diff --git a/hutool-crypto/src/test/java/cn/hutool/crypto/asymmetric/PaillierTest.java b/hutool-crypto/src/test/java/cn/hutool/crypto/asymmetric/PaillierTest.java new file mode 100644 index 000000000..472129207 --- /dev/null +++ b/hutool-crypto/src/test/java/cn/hutool/crypto/asymmetric/PaillierTest.java @@ -0,0 +1,49 @@ +package cn.hutool.crypto.asymmetric; + +import org.junit.Test; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; + +/** + * @author Revers. + **/ +public class PaillierTest { + @Test + public void test() throws NoSuchAlgorithmException { + //生成 公钥和私钥 + PaillierKeyPair paillierKeyPair = Paillier.generateKey(); + PaillierpublicKey publicKey = paillierKeyPair.getPublicKey(); + PaillierPrivateKey privateKey = paillierKeyPair.getPrivateKey(); + + //创建两个大整数m1,m2: + BigInteger m1 = new BigInteger("10"); + BigInteger m2 = new BigInteger("40"); + + //公钥加密私钥解 + byte[] em1 = Paillier.encrypt(m1, publicKey); + System.out.println("m1解密结果" + Paillier.decrypt(em1, privateKey)); + + //同态特性 + byte[] em2 = Paillier.encrypt(m2, publicKey); + System.out.println("m1加密em1 :"+ new BigInteger(em1).toString(16)); + System.out.println("m2加密em2 :"+ new BigInteger(em2).toString(16)); + byte[] add = Paillier.add(em1, em2, publicKey); + System.out.println("m1+m2 密文的和:" + new BigInteger(add).toString(16)); + System.out.println("m1+m2 密文的和的解:" + Paillier.decrypt(add,privateKey)); + + byte[] mul = Paillier.multiply(em1, m2, publicKey); + System.out.println("m1*m2 密文的积:" + new BigInteger(mul).toString(16)); + System.out.println("m1*m2 密文的积的解:" + Paillier.decrypt(mul,privateKey)); + + //加解密字符串 + String s = "123456dfsgsdg!@#%!@@#$!#%是豆腐干山豆根v啊微软"; + System.out.println("字符串明文: "+s); + + byte[] encrypt = Paillier.encryptString(s, publicKey); + System.out.println("字符串密文: "+ new BigInteger(encrypt).toString(16) ); + + String decrypt = Paillier.decryptString(encrypt, privateKey); + System.out.println("字符串密文解密: "+ decrypt); + } +}