Android中RSA加密工具类示例
2021-08-15 本文已影响0人
静水红阳
概述
RSA是一种比较常见的非对称加密算法,需要有着两个秘钥:公钥和私钥。
公钥和私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能够解密;如果用私钥对数据进行加密,那么只有用对应的公钥才能解密。
因为加密和解密使用的是两个不同的秘钥,所以这种算法叫做非对称加密算法。
RSA的算法原理在此不再陈述,本文注重于如何在Android中调用这种算法对数据进行加密和解密。
代码实现
class RSACipherUtil {
private var publicKey: PublicKey?
private var privateKey: PrivateKey?
val KEY_ALGORITHM = "RSA"
private val PUBLIC_KEY = "RSAPublicKey"
private val PRIVATE_KEY = "RSAPrivateKey"
val SIGNATURE_ALGORITHM = "MD5withRSA"
//公钥和私钥Base64字符串
var publicKeyString = ""
var privateKeyString = ""
/**
* RSA最大加密明文大小
*/
private val MAX_ENCRYPT_BLOCK = 117
/**
* RSA最大解密密文大小
*/
private val MAX_DECRYPT_BLOCK = 128
private val keySize = 1024
private val seedStr = "test"
init {
initKey()
publicKey = getPublicKey(publicKeyString)
privateKey = getPrivateKey(privateKeyString)
}
/**
* 初始化秘钥
*/
private fun initKey() {
//2,通过秘钥对生成器KeyPairGenerator 生成公钥和私钥
var keyGen = KeyPairGenerator.getInstance(KEY_ALGORITHM)
keyGen.initialize(keySize, SecureRandom(seedStr.toByteArray()))
//使用公钥进行加密,私钥进行解密(也可以反过来使用)
val keyPair = keyGen.generateKeyPair()
publicKey = keyPair.public
privateKey = keyPair.private
var privateEncoded = privateKey?.encoded
var publicEncoded = publicKey?.encoded
//生成公钥和私钥64编码字符串
var publicKey64 = Base64.encodeToString(publicEncoded, Base64.NO_WRAP)
var privateKey64 = Base64.encodeToString(privateEncoded, Base64.NO_WRAP)
LogUtil.instance.d("公钥:" + publicKey64)
LogUtil.instance.d("私钥:" + privateKey64)
publicKeyString = publicKey64
privateKeyString = privateKey64
}
/**
* 保存秘钥到文件进行存储
*/
private fun saveKeyToFile() {
var oosPublic: ObjectOutputStream? = null
var oosPrivate: ObjectOutputStream? = null
try {
oosPublic = ObjectOutputStream(FileOutputStream(PUBLIC_KEY))
oosPrivate = ObjectOutputStream(FileOutputStream(PRIVATE_KEY))
oosPublic.writeObject(publicKey)
oosPrivate.writeObject(privateKey)
} catch (e: Exception) {
e.printStackTrace()
} finally {
oosPublic?.close()
oosPrivate?.close()
}
}
/**
* 根据字符串生成私钥
* @param dataString:Base64转码后的私钥字符串
*/
private fun getPrivateKey(dataString: String): PrivateKey {
val decode = Base64.decode(dataString, Base64.NO_WRAP)
val pkcs8EncodedKeySpec =
PKCS8EncodedKeySpec(decode)
val kf =
KeyFactory.getInstance(KEY_ALGORITHM)
return kf.generatePrivate(pkcs8EncodedKeySpec)
}
/**
* 根据字符串生成公钥
* @param dataString:Base64转码后的公钥字符串
*/
private fun getPublicKey(dataString: String): PublicKey {
val decode = Base64.decode(dataString, Base64.NO_WRAP)
// PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(decode); //java底层 RSA公钥只支持X509EncodedKeySpec这种格式
val x509EncodedKeySpec =
X509EncodedKeySpec(decode)
val kf =
KeyFactory.getInstance(KEY_ALGORITHM)
return kf.generatePublic(x509EncodedKeySpec)
}
//************************加密解密**************************
/**
* 加密
* 使用私钥加密
*/
/**
* 加密
* @param data
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws InvalidKeyException
* @throws IOException
*/
@Throws(
NoSuchAlgorithmException::class,
InvalidKeySpecException::class,
NoSuchPaddingException::class,
IllegalBlockSizeException::class,
BadPaddingException::class,
InvalidKeyException::class,
IOException::class
)
fun encrypt(data: String): String? {
val ci =
Cipher.getInstance(KEY_ALGORITHM)
ci.init(Cipher.ENCRYPT_MODE, privateKey)
val bytes = data.toByteArray()
val inputLen = bytes.size
var offLen = 0 //偏移量
var i = 0
val bops = ByteArrayOutputStream()
while (inputLen - offLen > 0) {
var cache: ByteArray?
cache = if (inputLen - offLen > MAX_ENCRYPT_BLOCK) {
ci.doFinal(bytes, offLen, MAX_ENCRYPT_BLOCK)
} else {
ci.doFinal(bytes, offLen, inputLen - offLen)
}
bops.write(cache)
i++
offLen = MAX_ENCRYPT_BLOCK * i
}
bops.close()
val encryptedData = bops.toByteArray()
// return Base64.getEncoder().encodeToString(encryptedData)
return Base64.encodeToString(encryptedData, Base64.NO_WRAP)
}
/**
* 解密
* 使用公钥解密
* @param data
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
* @throws NoSuchPaddingException
* @throws InvalidKeySpecException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws IOException
*/
@Throws(
NoSuchAlgorithmException::class,
InvalidKeyException::class,
NoSuchPaddingException::class,
InvalidKeySpecException::class,
IllegalBlockSizeException::class,
BadPaddingException::class,
IOException::class
)
fun decrypt(data: String?): String? {
val ci =
Cipher.getInstance(KEY_ALGORITHM)
ci.init(Cipher.DECRYPT_MODE, publicKey)
val bytes = Base64.decode(data, Base64.NO_WRAP)
val inputLen = bytes.size
var offLen = 0
var i = 0
val byteArrayOutputStream = ByteArrayOutputStream()
while (inputLen - offLen > 0) {
var cache: ByteArray? = if (inputLen - offLen > MAX_DECRYPT_BLOCK) {
ci.doFinal(bytes, offLen, MAX_DECRYPT_BLOCK)
} else {
ci.doFinal(bytes, offLen, inputLen - offLen)
}
byteArrayOutputStream.write(cache)
i++
offLen = MAX_DECRYPT_BLOCK * i
}
byteArrayOutputStream.close()
val byteArray = byteArrayOutputStream.toByteArray()
return String(byteArray)
}
companion object {
val instance by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { RSACipherUtil() }
}
}
注意点
1. 一次性加密数据的长度有限
RSA非对称加密内容长度有限制,1024位key的最多只能加密127位数据,否则就会报错
javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
所以RSA中需要有分段加密和解密,如上述示例代码所示。
//一次性加密数据的长度不能大于117 字节
private static final int ENCRYPT_BLOCK_MAX = 117;
//一次性解密的数据长度不能大于128 字节
private static final int DECRYPT_BLOCK_MAX = 128;
2. 加密速度较慢
非对称加密一般不会单独拿来使用,他并不是为了取代对称加密而出现的,非对称加密速度比对称加密慢很多,极端情况下会慢1000 倍,所以一般不会用来加密大量数据,通常我们经常会将对称加密和非对称加密两种技术联合起来使用,例如用非对称加密来给称加密里的秘钥进行加密(即秘钥交换)。
总结
RSA加密代码Demo示例。