RSA加密解密實現(JAVA)
阿新 • • 發佈:2018-11-02
1.關於RSA演算法的原理解析參考:http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
2.RSA金鑰長度、明文長度和密文長度參考:https://blog.csdn.net/liuhuabai100/article/details/7585879
3.以下示例程式碼可以將金鑰Base64轉碼之後儲存到文字檔案內,也可以從文字檔案中讀取金鑰。
public class RSAGenerator { /** * 演算法 */ private String ALGORITHM_RSA = "RSA"; private String DEFAULT_ENCODING = "UTF-8"; public static final String KEY_TYPE_PUBLIC = "PUBLIC"; public static final String KEY_TYPE_PRIVATE = "PRIVATE"; /** * 公鑰 */ private RSAPublicKey publicKey; private String publicKeyStr; /** * 私鑰 */ private RSAPrivateKey privateKey; private String privateKeyStr; /** * 用於加解密 */ private Cipher cipher; /** * 明文塊的長度 它必須小於密文塊的長度 - 11 */ private int originLength = 128; /** * 密文塊的長度 */ private int encrytLength = 256; /** * 生成金鑰對 * @return */ public RSAGenerator generateKeyPair() { try { // RSA加密演算法 KeyPairGenerator keyPairGenerator = KeyPairGenerator .getInstance(ALGORITHM_RSA); // 建立金鑰對,長度採用2048 keyPairGenerator.initialize(2048); KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 分別得到公鑰和私鑰 publicKey = (RSAPublicKey) keyPair.getPublic(); privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 使用 Base64編碼 publicKeyStr = Base64Util.encode(publicKey.getEncoded()); privateKeyStr = Base64Util.encode(privateKey.getEncoded()); //將BASE64編碼的結果儲存到檔案內 String classPath = this.getClass().getClassLoader().getResource("").toString(); String prefix = classPath.substring(classPath.indexOf(":") + 1); String publicFilePath = prefix+"public.txt"; File publicFile= new File(publicFilePath); saveBase64KeyToFile(publicFile, publicKeyStr); String privateFilePath = prefix+"private.txt"; File privateFile= new File(privateFilePath); saveBase64KeyToFile(privateFile, privateKeyStr); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return this; } /** * 用公鑰加密 * @param content * @return 加密後的16進位制字串 */ public String encryptByPublic(String content) { String encode = ""; try { cipher = Cipher.getInstance(ALGORITHM_RSA); cipher.init(Cipher.ENCRYPT_MODE, publicKey); // 該金鑰能夠加密的最大位元組長度 int splitLength = publicKey.getModulus().bitLength() / 8 - 11; byte[][] arrays = splitBytes(content.getBytes(), splitLength); // 加密 StringBuffer buffer = new StringBuffer(); for (byte[] array : arrays) { buffer.append(bytesToHexString(cipher.doFinal(array))); } encode = buffer.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return encode; } /** * 用私鑰加密 * * @param content * @return 加密後的16進位制字串 */ public String encryptByPrivate(String content) { try { Cipher cipher = Cipher.getInstance(ALGORITHM_RSA); cipher.init(Cipher.ENCRYPT_MODE, privateKey); // 該金鑰能夠加密的最大位元組長度 int splitLength = ((RSAPrivateKey) privateKey).getModulus() .bitLength() / 8 - 11; byte[][] arrays = splitBytes(content.getBytes(), splitLength); StringBuffer stringBuffer = new StringBuffer(); for (byte[] array : arrays) { stringBuffer.append(bytesToHexString(cipher.doFinal(array))); } return stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } return null; } /** * 用私鑰解密 * @param content * @return 解密後的原文 */ public String decryptByPrivate(String content) { String decode = ""; try { cipher = Cipher.getInstance(ALGORITHM_RSA); cipher.init(Cipher.DECRYPT_MODE, privateKey); // 該金鑰能夠加密的最大位元組長度 int splitLength = privateKey.getModulus().bitLength() / 8; byte[] contentBytes = hexStringToBytes(content); byte[][] arrays = splitBytes(contentBytes, splitLength); StringBuffer stringBuffer = new StringBuffer(); for (byte[] array : arrays) { stringBuffer.append(new String(cipher.doFinal(array))); } decode = stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return decode; } /** * 用私鑰解密 * * @param content * @return 解密後的原文 */ public String decryptByPublic(String content) { String decode = ""; try { cipher = Cipher.getInstance(ALGORITHM_RSA); cipher.init(Cipher.DECRYPT_MODE, publicKey); // 該金鑰能夠加密的最大位元組長度 int splitLength = publicKey.getModulus().bitLength() / 8; byte[] contentBytes = hexStringToBytes(content); byte[][] arrays = splitBytes(contentBytes, splitLength); StringBuffer stringBuffer = new StringBuffer(); for (byte[] array : arrays) { stringBuffer.append(new String(cipher.doFinal(array))); } decode = stringBuffer.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return decode; } /** * 根據限定的每組位元組長度,將位元組陣列分組 * @param bytes 等待分組的位元組組 * @param splitLength 每組長度 * @return 分組後的位元組組 */ public static byte[][] splitBytes(byte[] bytes, int splitLength) { // bytes與splitLength的餘數 int remainder = bytes.length % splitLength; // 資料拆分後的組數,餘數不為0時加1 int quotient = remainder > 0 ? bytes.length / splitLength + 1 : bytes.length / splitLength; byte[][] arrays = new byte[quotient][]; byte[] array = null; for (int i = 0; i < quotient; i++) { // 如果是最後一組(quotient-1),同時餘數不等於0,就將最後一組設定為remainder的長度 if (i == quotient - 1 && remainder != 0) { array = new byte[remainder]; System.arraycopy(bytes, i * splitLength, array, 0, remainder); } else { array = new byte[splitLength]; System.arraycopy(bytes, i * splitLength, array, 0, splitLength); } arrays[i] = array; } return arrays; } /** * 將位元組陣列轉換成16進位制字串 * @param bytes 即將轉換的資料 * @return 16進位制字串 */ public static String bytesToHexString(byte[] bytes) { StringBuffer sb = new StringBuffer(bytes.length); String temp = null; for (int i = 0; i < bytes.length; i++) { temp = Integer.toHexString(0xFF & bytes[i]); if (temp.length() < 2) { sb.append(0); } sb.append(temp); } return sb.toString(); } /** * 將16進位制字串轉換成位元組陣列 * * @param hex * 16進位制字串 * @return byte[] */ public static byte[] hexStringToBytes(String hex) { int len = (hex.length() / 2); hex = hex.toUpperCase(); byte[] result = new byte[len]; char[] chars = hex.toCharArray(); for (int i = 0; i < len; i++) { int pos = i * 2; result[i] = (byte) (toByte(chars[pos]) << 4 | toByte(chars[pos + 1])); } return result; } /** * 將char轉換為byte * * @param c * char * @return byte */ private static byte toByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } /** * 儲存公鑰到檔案 * * @param file * @return */ public boolean savePublicKey(File file) { return saveKeyToFile(publicKey, file); } /** * 儲存私鑰到檔案 * * @param file * @return */ public boolean savePrivateKey(File file) { return saveKeyToFile(privateKey, file); } /** * 儲存金鑰到檔案 * @param key 金鑰 * @param file 檔案 * @return */ private boolean saveKeyToFile(Key key, File file) { boolean result = false; FileOutputStream fos = null; try { fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos); // 公鑰預設使用的是X.509編碼,私鑰預設採用的是PKCS #8編碼 byte[] encode = key.getEncoded(); // 注意,此處採用writeObject方法,讀取時也要採用readObject方法 oos.writeObject(encode); oos.close(); result = true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } return result; } private boolean saveBase64KeyToFile(File file, String key) { boolean result = false; FileOutputStream fos = null; try { fos = new FileOutputStream(file); fos.write(key.getBytes()); fos.close(); result = true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } return result; } /** * 從BASE64檔案中讀取KEY值 * @param fileName * @param keyType */ public void getKeyFromBase64File(String fileName,String keyType) { try { InputStream inputStream = this.getClass().getClassLoader().getResource(fileName).openStream(); ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] subByte = new byte[1024]; int len = 0; while((len=inputStream.read(subByte))>0) { outStream.write(subByte,0,len); } inputStream.close(); outStream.close(); String base64Key = new String(outStream.toByteArray(), DEFAULT_ENCODING); byte[] keybyte = Base64Util.decode(base64Key); // 預設編碼 KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA); if (KEY_TYPE_PUBLIC.equals(keyType)) { X509EncodedKeySpec x509eks = new X509EncodedKeySpec(keybyte); publicKey = (RSAPublicKey) keyFactory.generatePublic(x509eks); System.out.println(publicKey.getAlgorithm()); } else { PKCS8EncodedKeySpec pkcs8eks = new PKCS8EncodedKeySpec(keybyte); privateKey = (RSAPrivateKey) keyFactory .generatePrivate(pkcs8eks); } } catch (IOException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } } /** * 從檔案中得到公鑰 * * @param file */ public void getPublicKey(File file) { getKey(file, KEY_TYPE_PUBLIC); } /** * 從檔案中得到私鑰 * * @param file */ public void getPrivateKey(File file) { getKey(file, KEY_TYPE_PRIVATE); } /** * 從檔案中得到金鑰 * * @param file * @param keyType */ private void getKey(File file, String keyType) { FileInputStream fis = null; try { fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis); byte[] keybyte = (byte[]) ois.readObject(); // 關閉資源 ois.close(); // 預設編碼 KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA); if (KEY_TYPE_PUBLIC.equals(keyType)) { X509EncodedKeySpec x509eks = new X509EncodedKeySpec(keybyte); publicKey = (RSAPublicKey) keyFactory.generatePublic(x509eks); System.out.println(publicKey.getAlgorithm()); } else { PKCS8EncodedKeySpec pkcs8eks = new PKCS8EncodedKeySpec(keybyte); privateKey = (RSAPrivateKey) keyFactory .generatePrivate(pkcs8eks); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (InvalidKeySpecException e) { e.printStackTrace(); } finally { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
程式碼中涉及到的Base64Util如下:
public class Base64Util { private static final char S_BASE64CHAR[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; private static final byte S_DECODETABLE[]; static { S_DECODETABLE = new byte[128]; for (int i = 0; i < S_DECODETABLE.length; i++) S_DECODETABLE[i] = 127; for (int i = 0; i < S_BASE64CHAR.length; i++) S_DECODETABLE[S_BASE64CHAR[i]] = (byte) i; } /** * * @param ibuf * @param obuf * @param wp * @return */ private static int decode0(char ibuf[], byte obuf[], int wp) { int outlen = 3; if (ibuf[3] == '=') outlen = 2; if (ibuf[2] == '=') outlen = 1; int b0 = S_DECODETABLE[ibuf[0]]; int b1 = S_DECODETABLE[ibuf[1]]; int b2 = S_DECODETABLE[ibuf[2]]; int b3 = S_DECODETABLE[ibuf[3]]; switch (outlen) { case 1: // '\001' obuf[wp] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3); return 1; case 2: // '\002' obuf[wp++] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3); obuf[wp] = (byte) (b1 << 4 & 240 | b2 >> 2 & 15); return 2; case 3: // '\003' obuf[wp++] = (byte) (b0 << 2 & 252 | b1 >> 4 & 3); obuf[wp++] = (byte) (b1 << 4 & 240 | b2 >> 2 & 15); obuf[wp] = (byte) (b2 << 6 & 192 | b3 & 63); return 3; } throw new RuntimeException("Internal error"); } /** * * @param data * @param off * @param len * @return */ public static byte[] decode(char data[], int off, int len) { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[(len / 4) * 3 + 3]; int obufcount = 0; for (int i = off; i < off + len; i++) { char ch = data[i]; if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; obufcount += decode0(ibuf, obuf, obufcount); } } if (obufcount == obuf.length) { return obuf; } else { byte ret[] = new byte[obufcount]; System.arraycopy(obuf, 0, ret, 0, obufcount); return ret; } } /** * * @param data * @return */ public static byte[] decode(String data) { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[(data.length() / 4) * 3 + 3]; int obufcount = 0; for (int i = 0; i < data.length(); i++) { char ch = data.charAt(i); if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; obufcount += decode0(ibuf, obuf, obufcount); } } if (obufcount == obuf.length) { return obuf; } else { byte ret[] = new byte[obufcount]; System.arraycopy(obuf, 0, ret, 0, obufcount); return ret; } } /** * * @param data * @param off * @param len * @param ostream * @throws IOException */ public static void decode(char data[], int off, int len, OutputStream ostream) throws IOException { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[3]; for (int i = off; i < off + len; i++) { char ch = data[i]; if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; int obufcount = decode0(ibuf, obuf, 0); ostream.write(obuf, 0, obufcount); } } } /** * * @param data * @param ostream * @throws IOException */ public static void decode(String data, OutputStream ostream) throws IOException { char ibuf[] = new char[4]; int ibufcount = 0; byte obuf[] = new byte[3]; for (int i = 0; i < data.length(); i++) { char ch = data.charAt(i); if (ch != '=' && (ch >= S_DECODETABLE.length || S_DECODETABLE[ch] == 127)) continue; ibuf[ibufcount++] = ch; if (ibufcount == ibuf.length) { ibufcount = 0; int obufcount = decode0(ibuf, obuf, 0); ostream.write(obuf, 0, obufcount); } } } /** * * @param data * @return */ public static String encode(byte data[]) { return encode(data, 0, data.length); } /** * * @param data * @param off * @param len * @return */ public static String encode(byte data[], int off, int len) { if (len <= 0) return ""; char out[] = new char[(len / 3) * 4 + 4]; int rindex = off; int windex = 0; int rest; for (rest = len - off; rest >= 3; rest -= 3) { int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255); out[windex++] = S_BASE64CHAR[i >> 18]; out[windex++] = S_BASE64CHAR[i >> 12 & 63]; out[windex++] = S_BASE64CHAR[i >> 6 & 63]; out[windex++] = S_BASE64CHAR[i & 63]; rindex += 3; } if (rest == 1) { int i = data[rindex] & 255; out[windex++] = S_BASE64CHAR[i >> 2]; out[windex++] = S_BASE64CHAR[i << 4 & 63]; out[windex++] = '='; out[windex++] = '='; } else if (rest == 2) { int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255); out[windex++] = S_BASE64CHAR[i >> 10]; out[windex++] = S_BASE64CHAR[i >> 4 & 63]; out[windex++] = S_BASE64CHAR[i << 2 & 63]; out[windex++] = '='; } return new String(out, 0, windex); } /** * * @param data * @param off * @param len * @param ostream * @throws IOException */ public static void encode(byte data[], int off, int len, OutputStream ostream) throws IOException { if (len <= 0) return; byte out[] = new byte[4]; int rindex = off; int rest; for (rest = len - off; rest >= 3; rest -= 3) { int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255); out[0] = (byte) S_BASE64CHAR[i >> 18]; out[1] = (byte) S_BASE64CHAR[i >> 12 & 63]; out[2] = (byte) S_BASE64CHAR[i >> 6 & 63]; out[3] = (byte) S_BASE64CHAR[i & 63]; ostream.write(out, 0, 4); rindex += 3; } if (rest == 1) { int i = data[rindex] & 255; out[0] = (byte) S_BASE64CHAR[i >> 2]; out[1] = (byte) S_BASE64CHAR[i << 4 & 63]; out[2] = 61; out[3] = 61; ostream.write(out, 0, 4); } else if (rest == 2) { int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255); out[0] = (byte) S_BASE64CHAR[i >> 10]; out[1] = (byte) S_BASE64CHAR[i >> 4 & 63]; out[2] = (byte) S_BASE64CHAR[i << 2 & 63]; out[3] = 61; ostream.write(out, 0, 4); } } /** * * @param data * @param off * @param len * @param writer * @throws IOException */ public static void encode(byte data[], int off, int len, Writer writer) throws IOException { if (len <= 0) return; char out[] = new char[4]; int rindex = off; int rest = len - off; int output = 0; do { if (rest < 3) break; int i = ((data[rindex] & 255) << 16) + ((data[rindex + 1] & 255) << 8) + (data[rindex + 2] & 255); out[0] = S_BASE64CHAR[i >> 18]; out[1] = S_BASE64CHAR[i >> 12 & 63]; out[2] = S_BASE64CHAR[i >> 6 & 63]; out[3] = S_BASE64CHAR[i & 63]; writer.write(out, 0, 4); rindex += 3; rest -= 3; if ((output += 4) % 76 == 0) writer.write("\n"); } while (true); if (rest == 1) { int i = data[rindex] & 255; out[0] = S_BASE64CHAR[i >> 2]; out[1] = S_BASE64CHAR[i << 4 & 63]; out[2] = '='; out[3] = '='; writer.write(out, 0, 4); } else if (rest == 2) { int i = ((data[rindex] & 255) << 8) + (data[rindex + 1] & 255); out[0] = S_BASE64CHAR[i >> 10]; out[1] = S_BASE64CHAR[i >> 4 & 63]; out[2] = S_BASE64CHAR[i << 2 & 63]; out[3] = '='; writer.write(out, 0, 4); } } }
測試類:
public class RSATest { @Test public void test() { RSAGenerator rsaGenerator = new RSAGenerator().generateKeyPair(); String str="數表的質數又稱素數。指整數在一個大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數"; String encode = rsaGenerator.encryptByPublic(str); System.out.println(encode); System.out.println(rsaGenerator.decryptByPrivate(encode)); System.out.println("用私鑰加密公鑰解密"); String encrypt = rsaGenerator.encryptByPrivate(str); System.out.println(encrypt); System.out.println(rsaGenerator.decryptByPublic(encrypt)); } @Test public void readKeyFromBase64File(){ //從BASE64檔案中讀取KEY值 RSAGenerator rsaGenerator = new RSAGenerator(); rsaGenerator.getKeyFromBase64File("private.txt", RSAGenerator.KEY_TYPE_PRIVATE); rsaGenerator.getKeyFromBase64File("public.txt", RSAGenerator.KEY_TYPE_PUBLIC); String str="數表的質數又稱素數。指整數在一個大於1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數"; String encode = rsaGenerator.encryptByPublic(str); System.out.println(encode); System.out.println(rsaGenerator.decryptByPrivate(encode)); } }
以上程式碼大部分都是參考自網路,感謝網友的分享