From 532a67b10275c55c7403a3bdd2686f7bb9653d13 Mon Sep 17 00:00:00 2001 From: zhanglin33 Date: Fri, 19 Apr 2019 22:40:18 +0800 Subject: [PATCH] add RSA encryption and tests --- .../crypto/utils/classic/RSAUtils.java | 104 +++++++++++++++++- .../utils/classic/RIPEMD160UtilsTest.java | 2 +- .../crypto/utils/classic/RSAUtilsTest.java | 41 ++++++- .../blockchain/crypto/utils/sm/SM4Utils.java | 2 +- 4 files changed, 143 insertions(+), 6 deletions(-) diff --git a/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/RSAUtils.java b/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/RSAUtils.java index 9102e2d2..8dcd5c53 100644 --- a/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/RSAUtils.java +++ b/source/crypto/crypto-classic/src/main/java/com/jd/blockchain/crypto/utils/classic/RSAUtils.java @@ -7,7 +7,10 @@ import org.bouncycastle.asn1.pkcs.RSAPrivateKey; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.*; import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.encodings.PKCS1Encoding; +import org.bouncycastle.crypto.engines.RSAEngine; import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; +import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; @@ -28,7 +31,7 @@ import java.security.spec.X509EncodedKeySpec; /** * @author zhanglin33 * @title: RSAUtils - * @description: RSA2048 encryption and signature algorithms with SHA256, + * @description: RSA2048 encryption(ECB) and signature algorithms with SHA256, * and keys are output in both PKCS1v2 format and PKCS8 * @date 2019-03-25, 17:20 */ @@ -47,7 +50,11 @@ public class RSAUtils { private static final BigInteger VERSION_2PRIMES = BigInteger.valueOf(0); - private static final AlgorithmIdentifier RSA_ALGORITHM_IDENTIFIER = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + private static final AlgorithmIdentifier RSA_ALGORITHM_IDENTIFIER = + new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + + private static final int PLAINTEXT_BLOCKSIZE = 256 - 11; + private static final int CIPHERTEXT_BLOCKSIZE = 256; //-----------------Key Generation Algorithm----------------- @@ -136,6 +143,96 @@ public class RSAUtils { } + //-----------------Public Key Encryption Algorithm----------------- + + /** + * encryption + * + * @param plainBytes plaintext + * @param publicKey public key + * @return ciphertext + */ + public static byte[] encrypt(byte[] plainBytes, byte[] publicKey){ + RSAKeyParameters pubKey = bytes2PubKey_RawKey(publicKey); + return encrypt(plainBytes,pubKey); + } + + public static byte[] encrypt(byte[] plainBytes, byte[] publicKey, SecureRandom random){ + + RSAKeyParameters pubKey = bytes2PubKey_RawKey(publicKey); + ParametersWithRandom params = new ParametersWithRandom(pubKey,random); + + return encrypt(plainBytes,params); + } + + public static byte[] encrypt(byte[] plainBytes, CipherParameters params){ + + int blockNum = (plainBytes.length % PLAINTEXT_BLOCKSIZE == 0) ? (plainBytes.length / PLAINTEXT_BLOCKSIZE) + : (plainBytes.length / PLAINTEXT_BLOCKSIZE + 1); + int inputLength; + byte[] result = new byte[blockNum * CIPHERTEXT_BLOCKSIZE]; + byte[] buffer; + + AsymmetricBlockCipher encryptor = new PKCS1Encoding(new RSAEngine()); + encryptor.init(true, params); + try { + for (int i= 0; i < blockNum; i++) { + inputLength = ((plainBytes.length - i * PLAINTEXT_BLOCKSIZE) > i * PLAINTEXT_BLOCKSIZE)? + PLAINTEXT_BLOCKSIZE : (plainBytes.length - i * PLAINTEXT_BLOCKSIZE); + buffer = encryptor.processBlock(plainBytes, i * PLAINTEXT_BLOCKSIZE, inputLength); + System.arraycopy(buffer,0, + result, i * CIPHERTEXT_BLOCKSIZE, CIPHERTEXT_BLOCKSIZE); + } + } catch (InvalidCipherTextException e) { + e.printStackTrace(); + } + return result; + } + + /** + * decryption + * + * @param cipherBytes ciphertext + * @param privateKey private key + * @return plaintext + */ + public static byte[] decrypt(byte[] cipherBytes, byte[] privateKey){ + RSAPrivateCrtKeyParameters privKey = bytes2PrivKey_RawKey(privateKey); + return decrypt(cipherBytes,privKey); + } + + public static byte[] decrypt(byte[] cipherBytes, CipherParameters params){ + + if (cipherBytes.length % CIPHERTEXT_BLOCKSIZE != 0) + { + throw new com.jd.blockchain.crypto.CryptoException("ciphertext's length is wrong!"); + } + + int blockNum = cipherBytes.length / CIPHERTEXT_BLOCKSIZE; + int count = 0; + byte[] buffer; + byte[] plaintextWithZeros = new byte[blockNum * PLAINTEXT_BLOCKSIZE]; + byte[] result; + + AsymmetricBlockCipher decryptor = new PKCS1Encoding(new RSAEngine()); + decryptor.init(false,params); + try { + for (int i = 0; i < blockNum; i++){ + buffer = decryptor.processBlock(cipherBytes,i * CIPHERTEXT_BLOCKSIZE, CIPHERTEXT_BLOCKSIZE); + count += buffer.length; + System.arraycopy(buffer,0,plaintextWithZeros, i * PLAINTEXT_BLOCKSIZE, buffer.length); + } + } catch (InvalidCipherTextException e) { + throw new com.jd.blockchain.crypto.CryptoException(e.getMessage(), e); + } + + result = new byte[count]; + System.arraycopy(plaintextWithZeros,0,result,0,result.length); + + return result; + } + + /** * This outputs the key in PKCS1v2 format. * RSAPublicKey ::= SEQUENCE { @@ -304,7 +401,8 @@ public class RSAUtils { BigInteger dQ = privKey.getDQ(); BigInteger qInv = privKey.getQInv(); - return KeyUtil.getEncodedPrivateKeyInfo(RSA_ALGORITHM_IDENTIFIER, new RSAPrivateKey(modulus, pubExp, privExp, p, q, dP, dQ, qInv)); + return KeyUtil.getEncodedPrivateKeyInfo(RSA_ALGORITHM_IDENTIFIER, + new RSAPrivateKey(modulus, pubExp, privExp, p, q, dP, dQ, qInv)); } public static byte[] privKey2Bytes_RawKey(RSAPrivateCrtKeyParameters privKey){ diff --git a/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RIPEMD160UtilsTest.java b/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RIPEMD160UtilsTest.java index 98ef76f8..28285055 100644 --- a/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RIPEMD160UtilsTest.java +++ b/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RIPEMD160UtilsTest.java @@ -9,7 +9,7 @@ import static org.junit.Assert.assertEquals; /** * @author zhanglin33 * @title: RIPEMD160UtilsTest - * @description: Tests for the hash method in RIPEMD160 + * @description: Tests for the hash method in RIPEMD160Utils * @date 2019-04-10, 16:54 */ public class RIPEMD160UtilsTest { diff --git a/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RSAUtilsTest.java b/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RSAUtilsTest.java index 56da9c0d..7a933abb 100644 --- a/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RSAUtilsTest.java +++ b/source/crypto/crypto-classic/src/test/java/test/com/jd/blockchain/crypto/utils/classic/RSAUtilsTest.java @@ -14,7 +14,7 @@ import static org.junit.Assert.*; /** * @author zhanglin33 * @title: RSAUtilsTest - * @description: TODO + * @description: Tests for methods in RSAUtils * @date 2019-04-11, 17:10 */ public class RSAUtilsTest { @@ -110,6 +110,45 @@ public class RSAUtilsTest { assertTrue(isValidFromPubKeyBytes); } + @Test + public void encryptTest(){ + + byte[] data = new byte[246]; + Random random = new Random(); + random.nextBytes(data); + + AsymmetricCipherKeyPair keyPair = RSAUtils.generateKeyPair(); + AsymmetricKeyParameter pubKey = keyPair.getPublic(); + byte[] pubKeyBytes = RSAUtils.pubKey2Bytes_RawKey((RSAKeyParameters) pubKey); + + byte[] ciphertextFromPubKey = RSAUtils.encrypt(data,pubKey); + byte[] ciphertextFromPubKeyBytes = RSAUtils.encrypt(data,pubKeyBytes); + + assertEquals(512,ciphertextFromPubKey.length); + assertEquals(512,ciphertextFromPubKeyBytes.length); + } + + @Test + public void decryptTest(){ + + byte[] data = new byte[512]; + Random random = new Random(); + random.nextBytes(data); + + AsymmetricCipherKeyPair keyPair = RSAUtils.generateKeyPair(); + AsymmetricKeyParameter pubKey = keyPair.getPublic(); + AsymmetricKeyParameter privKey = keyPair.getPrivate(); + byte[] privKeyBytes = RSAUtils.privKey2Bytes_RawKey((RSAPrivateCrtKeyParameters) privKey); + + byte[] ciphertext = RSAUtils.encrypt(data,pubKey); + + byte[] plaintextFromPrivKey = RSAUtils.decrypt(ciphertext,privKey); + byte[] plaintextFromPrivKeyBytes = RSAUtils.decrypt(ciphertext,privKeyBytes); + + assertArrayEquals(data,plaintextFromPrivKey); + assertArrayEquals(data,plaintextFromPrivKeyBytes); + } + @Test public void performanceTest(){ diff --git a/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM4Utils.java b/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM4Utils.java index 63295f49..d2f17b64 100644 --- a/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM4Utils.java +++ b/source/crypto/crypto-sm/src/main/java/com/jd/blockchain/crypto/utils/sm/SM4Utils.java @@ -105,7 +105,7 @@ public class SM4Utils { } // To ensure that the ciphertext's length is integral multiples of 16 bytes - if ( cipherBytes.length % BLOCK_SIZE != 0 ) + if (cipherBytes.length % BLOCK_SIZE != 0) { throw new CryptoException("ciphertext's length is wrong!"); }