原理:
将两个大素数相乘十分容易,但是想要对其乘积进行因式分解却极其困难,因此可以将乘积公开作为加密密钥。
RSA 正是这个原因, 生成的密钥对其实也是可以互相替换的,A 作为公钥, B 就可以作为私钥解密, B作为公钥,A 就可以作为私钥解密。
但是生成的AB 长度差异较大。通常用短的作为公钥,以减少加密的算法成本,相对而言解密的成本变高。
私钥如果太小,敌人可以在 logn 的时间内破解 毕竟穷举就是 2^n
使用方式, 公钥就是在外部传播的密钥串, 私钥是所有人持有的。
加密传输时,发送人用公钥加密数据,发送给所有人,所有人通过私钥解密获得数据。
签名认证时,所有人用私钥加密数据,广播告知,其他人用公钥尝试解密校验,校验一致则密文未被篡改且是对应所有人发布的。实际操作时,通常对摘要信息进行处理完成对应的工作。 比如对发布的信息取 md5(SHA1 或SHA 256), 然后用私钥对相应的 md5 加密(生成签名)。 其他人接受到后,先生成摘要,然后与解密后的字串对比 来判断是否被篡改和一致性。
- 事实上Android apk 的签名也是与此一致。
Android 的签名信息主要保存在。 MANIFEST.MF、CERT.SF和CERT.RSA 中
流程是遍历除了签名文件意外的entry, 生成摘要(SHA1)。保存在 MANIFEST.MF
对MANIFEST.MF 进行rsa 私钥签名 防止篡改 并记录在CERT.SF中
CERT.RSA文件中保存了公钥、所采用的加密算法等信息
因此框架中读取公钥的算法也是通过这个文件来获取。
读取代码如下,
含义即 私钥 信息协议是PSCS7 公钥信息协议是 X509 签名算法是SHA1
Signature signature, X509Certificate publicKey, OutputStream out)
throws IOException, GeneralSecurityException {
SignerInfo signerInfo = new SignerInfo(
new X500Name(publicKey.getIssuerX500Principal().getName()),
publicKey.getSerialNumber(),
AlgorithmId.get("SHA1"),
AlgorithmId.get("RSA"),
signature.sign());
PKCS7 pkcs7 = new PKCS7(
new AlgorithmId[] { AlgorithmId.get("SHA1") },
new ContentInfo(ContentInfo.DATA_OID, null),
new X509Certificate[] { publicKey },
new SignerInfo[] { signerInfo });
- 编码加密解密都是针对字节流,完成之后要对字节流编码。通常的方式是。 Base64.encode(bytes, Base64.DEFAULT) 然后 new String(code , “UTF-8”);
对应的解码时也要按照base 64 解码
客户端和服务端代码案例:
生成密钥对代码
/**
* 生成密钥对
*
* @return
*/
public Map generateKeyBytes() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator
.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(KEY_SIZE);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
Map keyMap = new HashMap();
keyMap.put(PUBLIC_KEY, publicKey.getEncoded());
keyMap.put(PRIVATE_KEY, privateKey.getEncoded());
return keyMap;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
服务端签名
/**
* 还原私钥
*
* @param keyBytes
* @return
*/
public PrivateKey restorePrivateKey(byte[] keyBytes) {
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
keyBytes);
try {
KeyFactory factory = KeyFactory.getInstance(KEY_ALGORITHM);
PrivateKey privateKey = factory
.generatePrivate(pkcs8EncodedKeySpec);
return privateKey;
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
/**
* 签名
*
* @param privateKey 私钥
* @param plain_text 明文
* @return
*/
public byte[] sign(PrivateKey privateKey, String plain_text) {
MessageDigest messageDigest;
byte[] signed = null;
try {
messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
messageDigest.update(plain_text.getBytes());
byte[] outputDigest_sign = messageDigest.digest();
//System.out.println("SHA-256加密后-----》" +bytesToHexString(outputDigest_sign));
Signature Sign = Signature.getInstance(SIGNATURE_ALGORITHM);
Sign.initSign(privateKey);
Sign.update(outputDigest_sign);
signed = Sign.sign();
// System.out.println("SHA256withRSA签名后-----》" + bytesToHexString(signed));
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
e.printStackTrace();
}
return signed;
}
public String base64Signed(PrivateKey privateKey, String plain_text) {
return Base64.encodeBase64String(sign(privateKey, plain_text));
}
客户端验证
/**
* Generates a PublicKey instance from a string containing the
* Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IllegalArgumentException if encodedPublicKey is invalid
*/
static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey, Base64.DEFAULT);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
}
}
/**
* Verifies that the signature from the server matches the computed
* signature on the data. Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
static boolean verify(PublicKey publicKey, String signedData, String signature) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance(ENCODE_ALGORITHM);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
Log.e(TAG, "digest generate failed");
return false;
}
messageDigest.update(signedData.getBytes());
byte[] digestBytes = messageDigest.digest();
try {
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(digestBytes);
byte[] decodedSignature = Base64.decode(signature, Base64.DEFAULT);
if (!sig.verify(decodedSignature)) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
}
return false;
}
Android. 获取签名
/**
* 获取签名公钥
*
* @param mContext
* @return
*/
@Nullable
public static String getSignInfo(Context mContext, String packageName) {
if (TextUtils.isEmpty(packageName)) return "";
String signCode = "";
try {
PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(
packageName, PackageManager.GET_SIGNATURES);
Signature[] signs = packageInfo.signatures;
Signature sign = signs[0];
// String sha1 = signatureSHA1(signs);
String sha256 = signatureSHA256(signs);
// signCode = parseSignature(sign.toByteArray());
signCode = sha256;
} catch (Exception e) {
}
return signCode;
}
/**
* SHA1
*/
public static String signatureSHA1(Signature[] signatures) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
if (signatures != null) {
for (Signature s : signatures)
digest.update(s.toByteArray());
}
return toHexString(digest.digest());
} catch (Exception e) {
return "";
}
}
/**
* 进行转换
*/
public static String toHexString(byte[] bData) {
StringBuilder sb = new StringBuilder(bData.length * 2);
for (int i = 0; i < bData.length; i++) {
sb.append(HEX_DIGITS[(bData[i] & 0xf0) >>> 4]);
sb.append(HEX_DIGITS[bData[i] & 0x0f]);
}
return sb.toString();
}
/**
* SHA256
*/
public static String signatureSHA256(Signature[] signatures) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
if (signatures != null) {
for (Signature s : signatures)
digest.update(s.toByteArray());
}
return toHexString(digest.digest());
} catch (Exception e) {
return "";
}
}