原理:
將兩個(gè)大素?cái)?shù)相乘十分容易,但是想要對其乘積進(jìn)行因式分解卻極其困難,因此可以將乘積公開作為加密密鑰。
RSA 正是這個(gè)原因, 生成的密鑰對其實(shí)也是可以互相替換的,A 作為公鑰, B 就可以作為私鑰解密, B作為公鑰,A 就可以作為私鑰解密。
但是生成的AB 長度差異較大。通常用短的作為公鑰,以減少加密的算法成本,相對而言解密的成本變高。
私鑰如果太小,敵人可以在 logn 的時(shí)間內(nèi)破解 畢竟窮舉就是 2^n
使用方式, 公鑰就是在外部傳播的密鑰串, 私鑰是所有人持有的。
加密傳輸時(shí),發(fā)送人用公鑰加密數(shù)據(jù),發(fā)送給所有人,所有人通過私鑰解密獲得數(shù)據(jù)。
簽名認(rèn)證時(shí),所有人用私鑰加密數(shù)據(jù),廣播告知,其他人用公鑰嘗試解密校驗(yàn),校驗(yàn)一致則密文未被篡改且是對應(yīng)所有人發(fā)布的。實(shí)際操作時(shí),通常對摘要信息進(jìn)行處理完成對應(yīng)的工作。 比如對發(fā)布的信息取 md5(SHA1 或SHA 256), 然后用私鑰對相應(yīng)的 md5 加密(生成簽名)。 其他人接受到后,先生成摘要,然后與解密后的字串對比 來判斷是否被篡改和一致性。
- 事實(shí)上Android apk 的簽名也是與此一致。
Android 的簽名信息主要保存在。 MANIFEST.MF、CERT.SF和CERT.RSA 中
流程是遍歷除了簽名文件意外的entry, 生成摘要(SHA1)。保存在 MANIFEST.MF
對MANIFEST.MF 進(jìn)行rsa 私鑰簽名 防止篡改 并記錄在CERT.SF中
CERT.RSA文件中保存了公鑰、所采用的加密算法等信息
因此框架中讀取公鑰的算法也是通過這個(gè)文件來獲取。
讀取代碼如下,
含義即 私鑰 信息協(xié)議是PSCS7 公鑰信息協(xié)議是 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 });
- 編碼加密解密都是針對字節(jié)流,完成之后要對字節(jié)流編碼。通常的方式是。 Base64.encode(bytes, Base64.DEFAULT) 然后 new String(code , “UTF-8”);
對應(yīng)的解碼時(shí)也要按照base 64 解碼
客戶端和服務(wù)端代碼案例:
生成密鑰對代碼
/**
* 生成密鑰對
*
* @return
*/
public Map<String, byte[]> 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<String, byte[]> keyMap = new HashMap<String, byte[]>();
keyMap.put(PUBLIC_KEY, publicKey.getEncoded());
keyMap.put(PRIVATE_KEY, privateKey.getEncoded());
return keyMap;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
服務(wù)端簽名
/**
* 還原私鑰
*
* @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));
}
客戶端驗(yàn)證
/**
* 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 "";
}
}
/**
* 進(jìn)行轉(zhuǎn)換
*/
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 "";
}
}