您的位置:首页 > 移动开发 > Android开发

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

2015-10-27 11:43 921 查看
大家好,最近的一个项目中为了防止有人直接提交报文,所以团队打算在报文的传输过程中引入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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: