关于RSA非对称加密在Android应用中的使用

大家好,最近的一个项目中为了防止有人直接提交报文,所以团队打算在报文的传输过程中引入RSA加密的方式,用以防止这种直接通过报文的提交来进行功能的操作。

下面说下项目的背景。项目是一款采用H5架构的APP,我们主要来说明Android端的情况,主要分为三个部分,首先是一个Android的壳,是通过原生Android用来承载H5界面以及样式表等文件,其中涉及到版本控制,加密解密等功能,其次是第二部分,第二部分是前台部分,涉及到页面,Action层,第三部分是后台部分,涉及到Service,其中主要操作包括数据的操作,接口的调用等。我们想在原生Android层进行报文的加密,在后台进行解密校验等。下面是在原生Android部分做的一个RSA加密解密的Demo,其中还涉及到关于RSA中需要的公钥私钥的生成。

注意:通过RSA加密之后的密文是一串乱码,所以在这个Demo项目中我们给了个Base64的编码方式,以便有个更好的阅读和编码方式不至于出问题。同样的,在解密的时候,我们需要把这个Base64的先解码,在使用私钥进行解密。

首先我们来看下在原生Android下关于RSA加密解密的Demo的演示图片

整个布局文件包含4个EditText,2个Button,在最上面的EditText上输入需要加密的明文,点击加密,把加密的密文放到第二个EditText上,之后点击解密,把加密的密文在解密,整个过程所耗费的时间戳,打印在最下面的EditText上。下面是布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <EditText
        android:id="@+id/et1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" >
        <requestFocus />
    </EditText>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="84dp"
        android:orientation="horizontal" >
        <Button
            android:id="@+id/btn1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="加密" />
        <Button
            android:id="@+id/btn2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="解密" />
    </LinearLayout>
    <EditText
        android:id="@+id/et2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" />
    <EditText
        android:id="@+id/et3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" />
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="时间:" />
    <EditText
        android:id="@+id/et4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" />
</LinearLayout>

关于布局,很简单,我们就不多说了。我们来看看MainActivity,在MainActivity中,我们加载布局组件。然后定义2个按钮的点击事件

<span style="font-size:14px;">private void initView()
	{
		btn1 = (Button) findViewById(R.id.btn1);
		btn2 = (Button) findViewById(R.id.btn2);
		btn1.setOnClickListener(this);
		btn2.setOnClickListener(this);

		et1 = (EditText) findViewById(R.id.et1);
		et2 = (EditText) findViewById(R.id.et2);
		et3 = (EditText) findViewById(R.id.et3);
		et4 = (EditText) findViewById(R.id.et4);
	}

	@Override
	public void onClick(View v)
	{
		switch (v.getId())
		{
		// 加密
		case R.id.btn1:
			String source = et1.getText().toString().trim();
			try
			{
				//根据字符串String得到PublicKey
				PublicKey publicKey = RSAUtils.loadPublicKey(PUCLIC_KEY);
				// 加密
				byte[] encryptByte = RSAUtils.encryptData(source.getBytes(), publicKey);
				// 因为前面公钥是使用的Base64加密的,这里需要使用Base64解密
				String afterencrypt = Base64Utils.encode(encryptByte);
				et2.setText(afterencrypt);
			} catch (Exception e)
			{
				e.printStackTrace();
			}
			break;
		// 解密
		case R.id.btn2:
			long now_before = new Date().getTime();
			String encryptContent = et2.getText().toString().trim();
			try
			{

				// </span><span style="font-family: Arial, Helvetica, sans-serif;"><span style="font-size:10px;">根据字符串String得到PublicKey</span></span><span style="font-size:14px;">
				PrivateKey privateKey = RSAUtils.loadPrivateKey(PRIVATE_KEY);
				// 因为RSA加密后的内容经Base64再加密转换了一下,所以先Base64解密回来再给RSA解密,加密解密的过程中数据类型是Byte数组
				byte[] decryptByte = RSAUtils.decryptData(Base64Utils.decode(encryptContent), privateKey);
				String decryptStr = new String(decryptByte);
				et3.setText(decryptStr);
			} catch (Exception e)
			{
				e.printStackTrace();
			}</span>
<span style="font-size:14px;"><span style="white-space:pre">			</span>//时间戳
			long now_after = new Date().getTime();
			long date = now_after - now_before;
			Toast.makeText(getApplicationContext(), date+"", Toast.LENGTH_LONG).show();
			et4.setText(""+date);
			break;
		default:
			break;
		}
	}</span>

在initView()方法中,我们通过findViewById找到按钮和EditText组件。在onClick(View v)中我们来通过Activity接口的OnClickListener来定义加密和解密的按钮触发事件。在加密的过程中,我们根据前面定义的两个String常量publicKey和私钥的primaryKey调用RSAUtil的loadPublicKey方法,来生成的PublicKey,解密的过程中,也是如此,下面是loadPublicKey方法。

<span style="white-space:pre">	</span>/**
	 * 从字符串中加载公钥
	 * 
	 * @param publicKeyStr
	 *            公钥数据字符串
	 * @throws Exception
	 *             加载公钥时产生的异常
	 */
	public static PublicKey loadPublicKey(String publicKeyStr) throws Exception
	{
		try
		{
			byte[] buffer = Base64Utils.decode(publicKeyStr);
			KeyFactory keyFactory = KeyFactory.getInstance(RSA);
			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("公钥数据为空");
		}
	}

根据传入的字符串公钥,拿到PublicKey。首先给字符串公钥的进行Base64的解码,拿到Byte[],根据X509EncodedKeySpec给定的编码密钥创建一个新的 X509EncodedKeySpec。使用keyFactory的genratePublic方法返回PublicKey。 这样就拿到了publickey。

拿到了publicKey,我们调用RSAUtil中的encryptData(),来进行加密的操作。把加密的结果方式EditText上用以显示。下面我们看下解密的方法

/**
	 * 用公钥加密 <br>
	 * 每次加密的字节数,不能超过密钥的长度值减去11
	 * 
	 * @param data
	 *            需加密数据的byte数据
	 * @param pubKey
	 *            公钥
	 * @return 加密后的byte型数据
	 */
	public static byte[] encryptData(byte[] data, PublicKey publicKey)
	{
		try
		{
			Cipher cipher = Cipher.getInstance(RSA);
			// 编码前设定编码方式及密钥
			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
			// 传入编码数据并返回编码结果
			return cipher.doFinal(data);
		} catch (Exception e)
		{
			e.printStackTrace();
			return null;
		}
	}

根据Cipher设置密文的类型为RSA,调用init方法设置编码前编码方式和密钥,传入编码数据并且放回编码结果。在把编码结果放到EditText上用以显示。

解密的方法大同小异。

/**
	 * 从字符串中加载私钥<br>
	 * 加载时使用的是PKCS8EncodedKeySpec(PKCS#8编码的Key指令)。
	 * 
	 * @param privateKeyStr
	 * @return
	 * @throws Exception
	 */
	public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception
	{
		try
		{
			byte[] buffer = Base64Utils.decode(privateKeyStr);
			// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
			KeyFactory keyFactory = KeyFactory.getInstance(RSA);
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (NoSuchAlgorithmException e)
		{
			throw new Exception("无此算法");
		} catch (InvalidKeySpecException e)
		{
			throw new Exception("私钥非法");
		} catch (NullPointerException e)
		{
			throw new Exception("私钥数据为空");
		}
	}

/**
	 * 用私钥解密
	 * 
	 * @param encryptedData
	 *            经过encryptedData()加密返回的byte数据
	 * @param privateKey
	 *            私钥
	 * @return
	 */
	public static byte[] decryptData(byte[] encryptedData, PrivateKey privateKey)
	{
		try
		{
			Cipher cipher = Cipher.getInstance(RSA);
			cipher.init(Cipher.DECRYPT_MODE, privateKey);
			return cipher.doFinal(encryptedData);
		} catch (Exception e)
		{
			return null;
		}
	}

最后给大家一串字符串的公钥和私钥用以测试使用

//公钥
private static String PUCLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCHpicKssPjh3p1aHEtLQrGjvFqYQe9Qwj+P56dj8fnYa3xamxzwZrHzhZCgjjxKBOgTyhwUcCAnjMxp9laIf1KkIvE2RN5Nkaq6NvW5BZEvqUMW7BEh4yiZdAXK+MjLWm2Qhf8j0YEI5R4DEYCjshMJ0wYVpM05K8kNNFP7B+F9QIDAQAB";
//私钥
private static String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIemJwqyw+OHenVocS0tCsaO8WphB71DCP4/np2Px+dhrfFqbHPBmsfOFkKCOPEoE6BPKHBRwICeMzGn2Voh/UqQi8TZE3k2Rqro29bkFkS+pQxbsESHjKJl0Bcr4yMtabZCF/yPRgQjlHgMRgKOyEwnTBhWkzTkryQ00U/sH4X1AgMBAAECgYBj2pB01KFUdV9U3Bwr6DM9dO4Lo/+hd55AIq7tR3EdR49W3kOVdpgsqu1B6kBmbVz9LigTfmqZg1smG2vpaInd7OLLjgZzumOrArvLQdwScNM5Dn+kZIBJ7N5iVag5aP2KCX9AM/CIqyW6J0nfB9KUffU2YkmE+ZdZurVWm3Y0JQJBAM8pRUVUIu4jfKrntIb3X7Ffo4OoP/ODVAeDmQkJaaNqmDpycm+SqyKZDqZBC8PaFBRwW9UDVcuZvL7lNcmoS+cCQQCnoO6S0ZkQhgWpS0AmmwcCK/nsmc2XhOLf1rQ1dm4EKDSEvAkG67cOsR/2Usl6IYlGlQKJyZwabKbC3Z/WZgPDAkA+5n4U9d4BRp8k2WO0E0pn9e0VHbIFQ1vxSCDgYI5Fwyjjnjpm7DawM58CFf/3gLDWH+OSQwf64PwxTjFNwJ8DAkAw8gy3UfwflwKQLCjPHPUu7ShMrZwaYfLc6RQ1iB8Xl6W+HCmGm80XvSBYDFRIFQLAWUIkeXnbPV50B8JkF+WBAkA7oVRBgJcDcx7KBuRCwrd+goCslV6hz36Uc4CdJsZfyVDcLFthFgFGoePWSPh08POGJIHrwt+SrWlKLsoXWNbu"

下面把项目源码(RSADemo)上传到CSDN供大家下载,在提供一个项目(Keys)可以用来生成公钥和私钥。

RSADemo:http://download.csdn.net/detail/a514224717/9216251

Keys:http://download.csdn.net/detail/a514224717/9216337


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值