Android接口安全 - RSA+AES混合加密方案

转载请注明出处:

http://blog.csdn.net/aa464971/article/details/51034462

本文以Androidclient加密提交数据到Java服务端后进行解密为样例。

生成RSA公钥和密钥的方法请參考:

http://blog.csdn.net/aa464971/article/details/51035200

Android端的加密思路须要4步:

1.生成AES密钥;

2.使用RSA公钥加密刚刚生成的AES密钥;

3.再使用第1步生成的AES密钥,通过AES加密须要提交给服务端的数据;

4.将第2与第3生成的内容传给服务端。

JAVA服务端的解密思路仅仅需3步:

1.获取到client传过来的AES密钥密文和内容密文;

2.使用RSA私钥解密从client拿到的AES密钥密文。

3.再使用第2步解密出来的明文密钥。通过AES解密内容的密文。

AES的代码能够在JAVA和Android上通用

package com.dyhdyh.encrypt;

import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * AES
 * @author dengyuhan
 * @create 2016/3/31 15:43
 */
public class AES {
    // /** 算法/模式/填充 **/
    private static final String CipherMode = "AES/ECB/PKCS5Padding";
    // private static final String CipherMode = "AES";

    /**
     * 生成一个AES密钥对象
     * @return
     */
    public static SecretKeySpec generateKey(){
		try {
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
	        kgen.init(128, new SecureRandom());
	        SecretKey secretKey = kgen.generateKey();
	        byte[] enCodeFormat = secretKey.getEncoded();
	        SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			return key;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
    }

    /**
     * 生成一个AES密钥字符串
     * @return
     */
    public static String generateKeyString(){
    	return byte2hex(generateKey().getEncoded());
    }

    /**
     * 加密字节数据
     * @param content
     * @param key
     * @return
     */
    public static byte[] encrypt(byte[] content,byte[] key) {
        try {
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过byte[]类型的密钥加密String
     * @param content
     * @param key
     * @return 16进制密文字符串
     */
    public static String encrypt(String content,byte[] key) {
        try {
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"));
            byte[] data = cipher.doFinal(content.getBytes("UTF-8"));
            String result = byte2hex(data);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过String类型的密钥加密String
     * @param content
     * @param key
     * @return 16进制密文字符串
     */
    public static String encrypt(String content,String key) {
        byte[] data = null;
        try {
            data = content.getBytes("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        data = encrypt(data,new SecretKeySpec(hex2byte(key), "AES").getEncoded());
        String result = byte2hex(data);
        return result;
    }

    /**
     * 通过byte[]类型的密钥解密byte[]
     * @param content
     * @param key
     * @return
     */
    public static byte[] decrypt(byte[] content,byte[] key) {
        try {
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 通过String类型的密钥 解密String类型的密文
     * @param content
     * @param key
     * @return
     */
    public static String decrypt(String content, String key) {
        byte[] data = null;
        try {
            data = hex2byte(content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        data = decrypt(data, hex2byte(key));
        if (data == null)
            return null;
        String result = null;
        try {
            result = new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 通过byte[]类型的密钥 解密String类型的密文
     * @param content
     * @param key
     * @return
     */
    public static String decrypt(String content,byte[] key) {
    	try {
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.DECRYPT_MODE,new SecretKeySpec(key, "AES"));
            byte[] data = cipher.doFinal(hex2byte(content));
            return new String(data, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 字节数组转成16进制字符串
     * @param b
     * @return
     */
    public static String byte2hex(byte[] b) { // 一个字节的数。
        StringBuffer sb = new StringBuffer(b.length * 2);
        String tmp = "";
        for (int n = 0; n < b.length; n++) {
            // 整数转成十六进制表示
            tmp = (Integer.toHexString(b[n] & 0XFF));
            if (tmp.length() == 1) {
                sb.append("0");
            }
            sb.append(tmp);
        }
        return sb.toString().toUpperCase(); // 转成大写
    }

    /**
     * 将hex字符串转换成字节数组
     * @param inputString
     * @return
     */
    private static byte[] hex2byte(String inputString) {
        if (inputString == null || inputString.length() < 2) {
            return new byte[0];
        }
        inputString = inputString.toLowerCase();
        int l = inputString.length() / 2;
        byte[] result = new byte[l];
        for (int i = 0; i < l; ++i) {
            String tmp = inputString.substring(2 * i, 2 * i + 2);
            result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
        }
        return result;
    }
}

Android - RSA实现

package com.dyhdyh.encrypt;

import android.util.Base64;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * 用于Android平台的RSA加密解密
 *
 * @desc
 * @author dengyuhan
 * @create 2016-3-31 下午2:36:18
 */
public class RSA {
	private static final String ALGORITHM = "RSA";
	private static final String TRANSFORMATION = "RSA";

	/**
	 * 从文件里输入流中载入公钥
	 *
	 * @param in
	 *            公钥输入流
	 * @throws Exception
	 *             载入公钥时产生的异常
	 */
	public static RSAPublicKey loadPublicKey(InputStream in) throws Exception {
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String readLine = null;
			StringBuilder sb = new StringBuilder();
			while ((readLine = br.readLine()) != null) {
				if (readLine.charAt(0) == '-') {
					continue;
				} else {
					sb.append(readLine);
					sb.append('\r');
				}
			}
			return loadPublicKey(sb.toString());
		} catch (IOException e) {
			throw new Exception("公钥数据流读取错误");
		} catch (NullPointerException e) {
			throw new Exception("公钥输入流为空");
		}
	}

	/**
	 * 从字符串中载入公钥
	 *
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @return
	 * @throws Exception
	 *             载入公钥时产生的异常
	 */
	public static RSAPublicKey loadPublicKey(String publicKeyStr)
			throws Exception {
		try {
			byte[] buffer = Base64.decode(publicKeyStr, Base64.DEFAULT);
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("公钥非法");
		}catch (NullPointerException e) {
			throw new Exception("公钥数据为空");
		}
	}

	/**
	 * 从文件里载入私钥
	 *
	 * @param in
	 *            私钥输入流
	 * @return
	 * @throws Exception
	 */
	public static RSAPrivateKey loadPrivateKey(InputStream in) throws Exception {
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String readLine = null;
			StringBuilder sb = new StringBuilder();
			while ((readLine = br.readLine()) != null) {
				if (readLine.charAt(0) == '-') {
					continue;
				} else {
					sb.append(readLine);
					sb.append('\r');
				}
			}
			return loadPrivateKey(sb.toString());
		} catch (IOException e) {
			throw new Exception("私钥数据读取错误");
		} catch (NullPointerException e) {
			throw new Exception("私钥输入流为空");
		}
	}

	/**
	 * 从字符串中载入私钥
	 *
	 * @desc
	 * @param privateKeyStr
	 *            私钥字符串
	 * @return
	 * @throws Exception
	 */
	public static RSAPrivateKey loadPrivateKey(String privateKeyStr)
			throws Exception {
		try {
			byte[] buffer = Base64.decode(privateKeyStr, Base64.DEFAULT);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("私钥非法");
		}catch (NullPointerException e) {
			throw new Exception("私钥数据为空");
		}
	}

	/**
	 * 公钥加密
	 *
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
			throws Exception {
		// 模长
		int key_len = publicKey.getModulus().bitLength() / 8;
		// 加密数据长度 <= 模长-11
		String[] datas = splitString(data, key_len - 11);
		String mi = "";
		// 假设明文长度大于模长-11则要分组加密
		for (String s : datas) {
			mi += bcd2Str(encryptByPublicKey(s.getBytes(), publicKey));
		}
		return mi;
	}

	/**
	 * 公钥加密
	 * @desc
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, RSAPublicKey publicKey)
			throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		return cipher.doFinal(data);
	}

	/**
	 * 私钥加密
	 * @desc
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPrivateKey(byte[] data,
			RSAPrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		return cipher.doFinal(data);
	}

	/**
	 * 私钥加密
	 * @desc
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static String encryptByPrivateKey(String data,
			RSAPrivateKey privateKey) throws Exception {
		// 模长
		int key_len = privateKey.getModulus().bitLength() / 8;
		// 加密数据长度 <= 模长-11
		String[] datas = splitString(data, key_len - 11);
		String mi = "";
		// 假设明文长度大于模长-11则要分组加密
		for (String s : datas) {
			mi += bcd2Str(encryptByPrivateKey(s.getBytes(), privateKey));
		}
		return mi;
	}

	/**
	 * 私钥解密
	 *
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static String decryptByPrivateKey(String data,
			RSAPrivateKey privateKey) throws Exception {
		// 模长
		int key_len = privateKey.getModulus().bitLength() / 8;
		byte[] bytes = data.getBytes();
		byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
		// 假设密文长度大于模长则要分组解密
		String ming = "";
		byte[][] arrays = splitArray(bcd, key_len);
		for (byte[] arr : arrays) {
			ming += new String(decryptByPrivateKey(arr, privateKey));
		}
		return ming;
	}

	/**
	 * 私钥解密
	 * @desc
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] data,
			RSAPrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		return cipher.doFinal(data);
	}

	/**
	 * 公钥解密
	 * @desc
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static String decryptByPublicKey(String data,
			RSAPublicKey publicKey) throws Exception {
		// 模长
		int key_len = publicKey.getModulus().bitLength() / 8;
		byte[] bytes = data.getBytes();
		byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
		// 假设密文长度大于模长则要分组解密
		String ming = "";
		byte[][] arrays = splitArray(bcd, key_len);
		for (byte[] arr : arrays) {
			ming += new String(decryptByPublicKey(arr, publicKey));
		}
		return ming;
	}

	/**
	 * 公钥解密
	 * @desc
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPublicKey(byte[] data,
			RSAPublicKey publicKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		return cipher.doFinal(data);
	}

	/**
	 * ASCII码转BCD码
	 *
	 */
	private static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
		byte[] bcd = new byte[asc_len / 2];
		int j = 0;
		for (int i = 0; i < (asc_len + 1) / 2; i++) {
			bcd[i] = asc_to_bcd(ascii[j++]);
			bcd[i] = (byte) (((j >= asc_len) ?

0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
		}
		return bcd;
	}

	private static byte asc_to_bcd(byte asc) {
		byte bcd;

		if ((asc >= '0') && (asc <= '9'))
			bcd = (byte) (asc - '0');
		else if ((asc >= 'A') && (asc <= 'F'))
			bcd = (byte) (asc - 'A' + 10);
		else if ((asc >= 'a') && (asc <= 'f'))
			bcd = (byte) (asc - 'a' + 10);
		else
			bcd = (byte) (asc - 48);
		return bcd;
	}

	/**
	 * BCD转字符串
	 */
	private static String bcd2Str(byte[] bytes) {
		char temp[] = new char[bytes.length * 2], val;

		for (int i = 0; i < bytes.length; i++) {
			val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
			temp[i * 2] = (char) (val > 9 ?

val + 'A' - 10 : val + '0');

			val = (char) (bytes[i] & 0x0f);
			temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
		}
		return new String(temp);
	}

	/**
	 * 拆分字符串
	 */
	private static String[] splitString(String string, int len) {
		int x = string.length() / len;
		int y = string.length() % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		String[] strings = new String[x + z];
		String str = "";
		for (int i = 0; i < x + z; i++) {
			if (i == x + z - 1 && y != 0) {
				str = string.substring(i * len, i * len + y);
			} else {
				str = string.substring(i * len, i * len + len);
			}
			strings[i] = str;
		}
		return strings;
	}

	/**
	 * 拆分数组
	 */
	private static byte[][] splitArray(byte[] data, int len) {
		int x = data.length / len;
		int y = data.length % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		byte[][] arrays = new byte[x + z][];
		byte[] arr;
		for (int i = 0; i < x + z; i++) {
			arr = new byte[len];
			if (i == x + z - 1 && y != 0) {
				System.arraycopy(data, i * len, arr, 0, y);
			} else {
				System.arraycopy(data, i * len, arr, 0, len);
			}
			arrays[i] = arr;
		}
		return arrays;
	}
}

JAVA - RSA实现

package com.dyhdyh.encrypt;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import sun.misc.BASE64Decoder;

/**
 * 用于java平台的RSA加密解密
 *
 * @desc
 * @author dengyuhan
 * @create 2016-3-31 下午2:36:18
 */
public class RSA {
	private static final String ALGORITHM = "RSA";
	private static final String TRANSFORMATION = "RSA";

	/**
	 * 从文件里输入流中载入公钥
	 *
	 * @param in
	 *            公钥输入流
	 * @throws Exception
	 *             载入公钥时产生的异常
	 */
	public static RSAPublicKey loadPublicKey(InputStream in) throws Exception {
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String readLine = null;
			StringBuilder sb = new StringBuilder();
			while ((readLine = br.readLine()) != null) {
				if (readLine.charAt(0) == '-') {
					continue;
				} else {
					sb.append(readLine);
					sb.append('\r');
				}
			}
			return loadPublicKey(sb.toString());
		} catch (IOException e) {
			throw new Exception("公钥数据流读取错误");
		} catch (NullPointerException e) {
			throw new Exception("公钥输入流为空");
		}
	}

	/**
	 * 从字符串中载入公钥
	 *
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @return
	 * @throws Exception
	 *             载入公钥时产生的异常
	 */
	public static RSAPublicKey loadPublicKey(String publicKeyStr)
			throws Exception {
		try {
			BASE64Decoder base64Decoder = new BASE64Decoder();
			byte[] buffer = base64Decoder.decodeBuffer(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("公钥非法");
		} catch (IOException e) {
			throw new Exception("公钥数据内容读取错误");
		} catch (NullPointerException e) {
			throw new Exception("公钥数据为空");
		}
	}

	/**
	 * 从文件里载入私钥
	 *
	 * @param in
	 *            私钥输入流
	 * @return
	 * @throws Exception
	 */
	public static RSAPrivateKey loadPrivateKey(InputStream in) throws Exception {
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String readLine = null;
			StringBuilder sb = new StringBuilder();
			while ((readLine = br.readLine()) != null) {
				if (readLine.charAt(0) == '-') {
					continue;
				} else {
					sb.append(readLine);
					sb.append('\r');
				}
			}
			return loadPrivateKey(sb.toString());
		} catch (IOException e) {
			throw new Exception("私钥数据读取错误");
		} catch (NullPointerException e) {
			throw new Exception("私钥输入流为空");
		}
	}

	/**
	 * 从字符串中载入私钥
	 *
	 * @desc
	 * @param privateKeyStr
	 *            私钥字符串
	 * @return
	 * @throws Exception
	 */
	public static RSAPrivateKey loadPrivateKey(String privateKeyStr)
			throws Exception {
		try {
			BASE64Decoder base64Decoder = new BASE64Decoder();
			byte[] buffer = base64Decoder.decodeBuffer(privateKeyStr);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e) {
			throw new Exception("私钥非法");
		} catch (IOException e) {
			throw new Exception("私钥数据内容读取错误");
		} catch (NullPointerException e) {
			throw new Exception("私钥数据为空");
		}
	}

	/**
	 * 公钥加密
	 *
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
			throws Exception {
		// 模长
		int key_len = publicKey.getModulus().bitLength() / 8;
		// 加密数据长度 <= 模长-11
		String[] datas = splitString(data, key_len - 11);
		String mi = "";
		// 假设明文长度大于模长-11则要分组加密
		for (String s : datas) {
			mi += bcd2Str(encryptByPublicKey(s.getBytes(), publicKey));
		}
		return mi;
	}

	/**
	 * 公钥加密
	 * @desc
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, RSAPublicKey publicKey)
			throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		return cipher.doFinal(data);
	}

	/**
	 * 私钥加密
	 * @desc
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] encryptByPrivateKey(byte[] data,
			RSAPrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION);
		cipher.init(Cipher.ENCRYPT_MODE, privateKey);
		return cipher.doFinal(data);
	}

	/**
	 * 私钥加密
	 * @desc
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static String encryptByPrivateKey(String data,
			RSAPrivateKey privateKey) throws Exception {
		// 模长
		int key_len = privateKey.getModulus().bitLength() / 8;
		// 加密数据长度 <= 模长-11
		String[] datas = splitString(data, key_len - 11);
		String mi = "";
		// 假设明文长度大于模长-11则要分组加密
		for (String s : datas) {
			mi += bcd2Str(encryptByPrivateKey(s.getBytes(), privateKey));
		}
		return mi;
	}

	/**
	 * 私钥解密
	 *
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static String decryptByPrivateKey(String data,
			RSAPrivateKey privateKey) throws Exception {
		// 模长
		int key_len = privateKey.getModulus().bitLength() / 8;
		byte[] bytes = data.getBytes();
		byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
		// 假设密文长度大于模长则要分组解密
		String ming = "";
		byte[][] arrays = splitArray(bcd, key_len);
		for (byte[] arr : arrays) {
			ming += new String(decryptByPrivateKey(arr, privateKey));
		}
		return ming;
	}

	/**
	 * 私钥解密
	 * @desc
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPrivateKey(byte[] data,
			RSAPrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION,new BouncyCastleProvider());
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		return cipher.doFinal(data);
	}

	/**
	 * 公钥解密
	 * @desc
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static String decryptByPublicKey(String data,
			RSAPublicKey publicKey) throws Exception {
		// 模长
		int key_len = publicKey.getModulus().bitLength() / 8;
		byte[] bytes = data.getBytes();
		byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
		// 假设密文长度大于模长则要分组解密
		String ming = "";
		byte[][] arrays = splitArray(bcd, key_len);
		for (byte[] arr : arrays) {
			ming += new String(decryptByPublicKey(arr, publicKey));
		}
		return ming;
	}

	/**
	 * 公钥解密
	 * @desc
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static byte[] decryptByPublicKey(byte[] data,
			RSAPublicKey publicKey) throws Exception {
		Cipher cipher = Cipher.getInstance(TRANSFORMATION,new BouncyCastleProvider());
		cipher.init(Cipher.DECRYPT_MODE, publicKey);
		return cipher.doFinal(data);
	}

	/**
	 * ASCII码转BCD码
	 *
	 */
	private static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
		byte[] bcd = new byte[asc_len / 2];
		int j = 0;
		for (int i = 0; i < (asc_len + 1) / 2; i++) {
			bcd[i] = asc_to_bcd(ascii[j++]);
			bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
		}
		return bcd;
	}

	private static byte asc_to_bcd(byte asc) {
		byte bcd;

		if ((asc >= '0') && (asc <= '9'))
			bcd = (byte) (asc - '0');
		else if ((asc >= 'A') && (asc <= 'F'))
			bcd = (byte) (asc - 'A' + 10);
		else if ((asc >= 'a') && (asc <= 'f'))
			bcd = (byte) (asc - 'a' + 10);
		else
			bcd = (byte) (asc - 48);
		return bcd;
	}

	/**
	 * BCD转字符串
	 */
	private static String bcd2Str(byte[] bytes) {
		char temp[] = new char[bytes.length * 2], val;

		for (int i = 0; i < bytes.length; i++) {
			val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
			temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

			val = (char) (bytes[i] & 0x0f);
			temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
		}
		return new String(temp);
	}

	/**
	 * 拆分字符串
	 */
	private static String[] splitString(String string, int len) {
		int x = string.length() / len;
		int y = string.length() % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		String[] strings = new String[x + z];
		String str = "";
		for (int i = 0; i < x + z; i++) {
			if (i == x + z - 1 && y != 0) {
				str = string.substring(i * len, i * len + y);
			} else {
				str = string.substring(i * len, i * len + len);
			}
			strings[i] = str;
		}
		return strings;
	}

	/**
	 * 拆分数组
	 */
	private static byte[][] splitArray(byte[] data, int len) {
		int x = data.length / len;
		int y = data.length % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		byte[][] arrays = new byte[x + z][];
		byte[] arr;
		for (int i = 0; i < x + z; i++) {
			arr = new byte[len];
			if (i == x + z - 1 && y != 0) {
				System.arraycopy(data, i * len, arr, 0, y);
			} else {
				System.arraycopy(data, i * len, arr, 0, len);
			}
			arrays[i] = arr;
		}
		return arrays;
	}
}

JAVA的RSA跟Android的RSA有所不同:

1.载入key的时候。JAVA上用的是BASE64Decoder

BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] buffer = base64Decoder.decodeBuffer(publicKeyStr);

而Android上用的Base64,这个地方仅仅是API不一样,作用是一样的

byte[] buffer = Base64.decode(publicKeyStr, Base64.DEFAULT);

2.在JAVA平台上调用Cipher.getInstance()的时候。须要多传一个參数。也就是BouncyCastleProvider的实例:

Cipher cipher = Cipher.getInstance("RSA",new BouncyCastleProvider());

这个类jdk上是没有的。所以须要加入一个jar包bcprov-jdk15-143.jar

假设不这样做。JAVA上解密的时候就会抛出一个BadPaddingException

Exception in thread "main" javax.crypto.BadPaddingException: Blocktype mismatch: 0
	at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:332)
	at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:272)
	at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
	at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:382)
	at javax.crypto.Cipher.doFinal(Cipher.java:2087)
	at com.dyhdyh.encrypt.RSA.decryptByPrivateKey(RSA.java:255)
	at com.dyhdyh.encrypt.RSA.decryptByPrivateKey(RSA.java:238)

这是由于Android的加密标准与JAVA的加密标准不一致导致,Android上的RSA实现是"RSA/None/NoPadding"。而标准JDK实现是"RSA/None/PKCS1Padding",这造成了在Android上加密后无法在server上解密

最后来看看如何混合加密(这里仅仅举一种方式,附件里有完整的)

Android上加密

1.将openssl生成出来的公钥,放入assets目录内(不一定要放这里,仅仅要能拿到文件内容即可)。

2.载入放在assets文件中的公钥

//载入RSA公钥
RSAPublicKey rsaPublicKey = RSA.loadPublicKey(getAssets().open("rsa_public_key.pem"));

3.再生成一个AES的密钥,用于AES加密

//生成一个AES密钥
String aesKey=AES.generateKeyString();

4.通过RSA的公钥来加密刚刚生成的AES密钥

//用RSA公钥加密AES的密钥
String encryptAesKey = RSA.encryptByPublicKey(aesKey, rsaPublicKey);

5.最后使用AES来加密须要传输的数据。AES加密须要传入两个參数,第一个是明文数据,第二个是3步生成出来的密钥

//再使用AES加密内容。传给server
String encryptContent = AES.encrypt(content, aesKey);

6.第5步返回的字符串就是加密过后的数据。最后将4和5传给服务端。接下来就是服务端的事情了。

client传过来密文之后,接下来就须要服务端来解密了

JAVA解密

1.载入RSA私钥(这里的私钥是跟client的公钥是成对的)

//载入私钥
RSAPrivateKey privateKey = RSA.loadPrivateKey(new FileInputStream("G:/RSA密钥/pkcs8_rsa_private_key.pem"));

2.通过RSA的私钥解密client传来的AES-KEY(也就是client的第4),由于这个key是加过密的,所以我们须要先将key解密成明文

//解密AES-KEY
String decryptAesKey = RSA.decryptByPrivateKey(aesKey, privateKey);

3.AES-KEY加密成明文之后,如今能够拿这个key来解密client传过来的数据了

//AES解密数据
String decrypt = AES.decrypt(content, decryptAesKey);

RSA-AES混合加密就是这个样子,有什么问题请跟帖回复。我会继续改进

最后附上完整demo

http://download.csdn.net/detail/aa464971/9478798

Android交流群:146262062

时间: 2024-08-06 07:37:06

Android接口安全 - RSA+AES混合加密方案的相关文章

AES与RSA混合加密完整实例

前言 前段时间看到一篇文章讲如何保证API调用时数据的安全性(传送门:https://blog.csdn.net/ityouknow/article/details/80603617),文中讲到利用RSA来加密传输AES的秘钥,用AES来加密数据,并提供如下思路: 说人话就是前.后端各自生成自己的RSA秘钥对(公钥.私钥),然后交换公钥(后端给前端的是正常的明文公钥,前端给后端的是用后端公钥加密后的密文公钥:PS:其实我觉得直接交换两个明文公钥就行了),后端生成AES的明文key,用明文key进

Android数据库(sqlite)加密方案

最近因为一些项目的安全性需要将数据库加密,一开始想到的就是先将数据库通过AES加密,然后运行时再解密,另一种是将数据库里的内容加密. 很快这两种方案都是不理想的,第一种加密方式形同虚设,第二种,如果加密的字段是要查找数据就变得麻烦. 所以第三种方案就是在内存里解密,在网上查到SQLITE是支持加密的, 所以就根据网上的指导一步步地将SQLITE编译成支持加密的. 那下一步就是怎样做成SDK去方便使用?第一个念头就是将原生的数据库使用方式移植过来,但做起来比开始想像的难了点,但最终也在修修补补中完

RSA与AES混合加密算法的实现

使用 PVRTC 压缩格式创建纹理(Creating textures in the PVRTC compression format) 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. 有关该篇

polarssl rsa &amp; aes 加密与解密

上周折腾加密与解密,用了openssl, crypto++, polarssl, cyassl, 说起真的让人很沮丧,只有openssl & polarssl两个库的RSA & AES 加密和解密,我用起来了,crypto++各种模板,各种多继承,看的头大,而且对各种常用的加密算法也不了解,所以这个库我在折腾了一天之后就放弃了:cyassl这个库现在没什么印象了:openssl没什么好说的,用起来很方便,尤其是使用win32openssl,都不用自己编译,下载下来安装好了就能用,着实方便:

RSA AES 前端JS与后台JAVA的加密解密的是实现

AES CryptoJS 前提是编码方式,key,vi中设置一样,就可以进行跨语言加密解密 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <link rel="stylesheet" href="<%=

iOS开发之 AES+Base64数据混合加密与解密

2016-04-08 09:03 编辑: liubinqww 分类:iOS开发 来源:liubinqww 投稿 4 889 "APP的数据安全已经牵动着我们开发者的心,简单的MD5/Base64等已经难以满足当下的数据安全标准,本文简单的介绍下AES与Base64的混合加密与解密" AES:高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES

基于RSA的WEB前端密码加密方案

受制于WEB页面源码的暴露,因此传统的对称加密方案以及加密密钥都将暴露在JS文件中,同样可以被解密. 目前比较好的解决方案是WEB页面全程或用户登录等关键环节使用HTTPS进行传输. 另外一种解决方案就是通过RSA进行加密. RSA是一种非对称加密,也就是客户端通过公钥进行加密,服务端通过私钥进行解密.RSA算法请点击百度百科进行了解. 也就是说公钥并不能进行解密,因此进行明文传输也是安全的. 1.加密流程 服务端生成一组公钥与私钥,将公钥发送给客户端进行密码加密,在使用密钥进行解密. 2.密钥

android接口加密

最近公司写的android接口需要加密,防止被恶意攻击. 1 大多数接口加密都是携带证书,大家可以参考这位大兄弟的  http://my.oschina.net/jjface/blog/339144 2 这种携带证书比较麻烦,而且代码比较多,比较繁琐一般同学都搞不定,所以就想了个简单的办法,传两个参数,一个是string类型的另一种是MD5加密的密文,在服务端写个拦截器/或者过滤器去拦截他,然后做自己相应的逻辑处理,把string类型的字段拿过来加上其他一些东西,用MD5加密和传过来的密文做相应

PHP开发接口使用RSA进行加密解密方法

网络安全问题很重要,尤其是保证数据安全,遇到很多在写接口的程序员直接都是明文数据传输,在我看来这是很不专业的.本人提倡经过接口的数据都要进行加密解密之后进行使用. 这篇文章主要介绍使用PHP开发接口,数据实现RSA加密解密后使用,实例分析了PHP自定义RSA类实现加密与解密的技巧,非常具有实用价值,需要的朋友可以参考下. 简单介绍RSA: RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.但是有不少新手对它不太了解.下面仅作简要介绍.RSA是第一个比较完善的公开密钥算法,它既