国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

android 關(guān)于利用簽名的SHA1進(jìn)行安全校驗(yàn)的方法之一(推薦)

2019-12-12 03:59:18
字體:
供稿:網(wǎng)友

最近做安卓項(xiàng)目中使用到了百度地圖的API,在申請(qǐng)百度地圖key的時(shí)候,需要我們填入“簽名的SHA1”和“客戶端包名”,然后百度為我們生成一個(gè)key。

于是就引發(fā)了思考,百度為何需要我們客戶端簽名的SHA1值呢?

第一想法就是:百度拿我們輸入的參數(shù)SHA1和包名進(jìn)行一些列算法計(jì)算,生成一個(gè)key返回給我們。

為了證明這個(gè)想法,寫了demo進(jìn)行測(cè)試,android獲取包名的方法很簡(jiǎn)單,但是我們還需要從客戶端中獲取keystore的指紋SHA1。

進(jìn)行各種資料的查找和分析,才得出方法。

一、首先,科普一下apk包下的META-INF目錄

我們已經(jīng)知道的是:Android對(duì)每一個(gè)Apk文件都會(huì)進(jìn)行簽名,在Apk文件安裝時(shí),系統(tǒng)會(huì)對(duì)其簽名信息進(jìn)行比對(duì),判斷程序的完整性,從而決定該Apk文件是否可以安裝,在一定程度上達(dá)到安全的目的。

給定一個(gè)Apk文件,解壓,可以看到一個(gè)META-INFO文件夾,在該文件夾下有三個(gè)文件:分別為MANIFEST.MF、CERT.SF和CERT.RSA。這三個(gè)文件分別表征以下含義:

(1)MANIFEST.MF:這是摘要文件。程序遍歷Apk包中的所有文件(entry),對(duì)非文件夾非簽名文件的文件,逐個(gè)用SHA1生成摘要信息,再用Base64進(jìn)行編碼。如果你改變了apk包中的文件,那么在apk安裝校驗(yàn)時(shí),改變后的文件摘要信息與MANIFEST.MF的檢驗(yàn)信息不同,于是程序就不能成功安裝。

說明:如果攻擊者修改了程序的內(nèi)容,有重新生成了新的摘要,那么就可以通過驗(yàn)證,所以這是一個(gè)非常簡(jiǎn)單的驗(yàn)證。

(2)CERT.SF:這是對(duì)摘要的簽名文件。對(duì)前一步生成的MANIFEST.MF,使用SHA1-RSA算法,用開發(fā)者的私鑰進(jìn)行簽名。在安裝時(shí)只能使用公鑰才能解密它。解密之后,將它與未加密的摘要信息(即,MANIFEST.MF文件)進(jìn)行對(duì)比,如果相符,則表明內(nèi)容沒有被異常修改。

說明:在這一步,即使開發(fā)者修改了程序內(nèi)容,并生成了新的摘要文件,但是攻擊者沒有開發(fā)者的私鑰,所以不能生成正確的簽名文件(CERT.SF)。系統(tǒng)在對(duì)程序進(jìn)行驗(yàn)證的時(shí)候,用開發(fā)者公鑰對(duì)不正確的簽名文件進(jìn)行解密,得到的結(jié)果和摘要文件(MANIFEST.MF)對(duì)應(yīng)不起來,所以不能通過檢驗(yàn),不能成功安裝文件。

(3)CERT.RSA文件中保存了公鑰、所采用的加密算法等信息。

說明:系統(tǒng)對(duì)簽名文件進(jìn)行解密,所需要的公鑰就是從這個(gè)文件里取出來的。

結(jié)論:從上面的總結(jié)可以看出,META-INFO里面的說那個(gè)文件環(huán)環(huán)相扣,從而保證Android程序的安全性。(只是防止開發(fā)者的程序不被攻擊者修改,如果開發(fā)者的公私鑰對(duì)對(duì)攻擊者得到或者開發(fā)者開發(fā)出攻擊程序,Android系統(tǒng)都無法檢測(cè)出來。)

我們將apk包解包,然后使用命令 keytool -printcert -file CERT.RSA 查看CERT.RSA,如圖所示:

答案很明顯,CERT.RSA文件中存放了關(guān)于簽名的信息。

當(dāng)我們使用如下代碼獲取證書信息的時(shí)候:

/**	 * 獲取指定包名程序的簽名信息	 * 	 * @param context	 * @param packName	 * @author SHANHY	 */	public static void getSingInfo(Context context, String packName) {		try {			PackageInfo packageInfo = context.getPackageManager().getPackageInfo(packName, PackageManager.GET_SIGNATURES);			Signature[] signs = packageInfo.signatures;			Signature sign = signs[0];			parseSignature(sign.toByteArray());		} catch (Exception e) {			e.printStackTrace();		}	}	public static void parseSignature(byte[] signature) {		try {			CertificateFactory certFactory = CertificateFactory.getInstance("X.509");			X509Certificate cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(signature));			String pubKey = cert.getPublicKey().toString();			String signNumber = cert.getSerialNumber().toString();			Log.d(TAG, "pubKey = " + pubKey);// 輸出的是16進(jìn)制的公鑰			Log.d(TAG, "signNumber = " + signNumber);		} catch (Exception e) {			e.printStackTrace();		}}

查看API可以發(fā)現(xiàn),X509Certificate類并沒有提供直接可以獲取SHA1值的方法。

研究簽名文件的加密算法之后,我們看可以根據(jù)publicKey自己計(jì)算出SHA1的值,計(jì)算方法為:

> 通過 X509Certificate 的 getEncoded() 方法獲得公鑰的字節(jié)數(shù)組。

> 使用 MessageDigest 對(duì)字節(jié)數(shù)組進(jìn)行SHA1的 degest 處理,得到新的字節(jié)數(shù)組。

> 然后使用16進(jìn)制對(duì)得到新字節(jié)數(shù)組進(jìn)行轉(zhuǎn)換,最終得到證書文件的指紋SHA1。

更詳細(xì)的加密算法,大家可以進(jìn)一步去研究 java 簽名證書規(guī)則。

我們還有一個(gè)折中的方法就是,在客戶端啟動(dòng)的時(shí)候,調(diào)用服務(wù)端接口把文件CERT.RSA通過流的方式傳遞給服務(wù)端,服務(wù)端使用上面截圖中命令的方式進(jìn)行處理,得到SHA1值然后返回給客戶端,客戶端將該值記錄在內(nèi)存變量中使用。

注:我們得到的證書指紋SHA1值,建議放在內(nèi)存變量中使用即可,無需存儲(chǔ)到緩存文件中。

下面附上一些其他有關(guān)證書方面的一些代碼

JAVA 獲取私鑰信息的代碼:

public static void main(String[] args) {				try {			// 用證書的私鑰解密 - 該私鑰存在生成該證書的密鑰庫(kù)中			FileInputStream fis2 = new FileInputStream("G://test.keystore");			KeyStore ks = KeyStore.getInstance("JKS"); // 加載證書庫(kù)			char[] kspwd = "test".toCharArray(); // 證書庫(kù)密碼			char[] keypwd = "test".toCharArray(); // 證書密碼			String alias = "test";// 別名			ks.load(fis2, kspwd); // 加載證書			PrivateKey pk2 = (PrivateKey) ks.getKey(alias, keypwd); // 獲取證書私鑰			fis2.close();//			Cipher c2 = Cipher.getInstance("RSA/ECB/PKCS1Padding");//jdk默認(rèn)標(biāo)準(zhǔn)			Cipher c2 = Cipher.getInstance("RSA/ECB/NoPadding");//android默認(rèn)標(biāo)準(zhǔn)			c2.init(Cipher.DECRYPT_MODE, pk2);						String priKey = pk2.toString();			System.out.println("priKey = " + priKey);		} catch (Exception e) {			e.printStackTrace();		}			}

JAVA 獲取公鑰信息的代碼:

public static void main(String[] args) throws Exception {		X509Certificate certificate = readSignatureBlock(new FileInputStream("G:/META-INF/CERT.RSA"));		RSAPublicKey pk = (RSAPublicKey) certificate.getPublicKey();		sun.security.rsa.RSAPublicKeyImpl ppk = (sun.security.rsa.RSAPublicKeyImpl) pk;		System.out.println(ppk.getModulus());// 十進(jìn)制公鑰		System.out.println(ppk.getModulus().toString(16));// 十六進(jìn)制公鑰		System.out.println(ppk.getPublicExponent());// 65537		System.out.println(ppk.getAlgorithm());		System.out.println(ppk.getFormat());		System.out.println(ppk.getAlgorithmId());		System.out.println(ppk.getPublicExponent().toString(16));// 10001		System.out.println(new BigInteger(ppk.getEncoded()).toString(16));		// 其中new		// BigInteger(ppk.getEncoded()).toString(16)的字符串包含字符串ppk.getModulus().toString(16)和字符串ppk.getPublicExponent().toString(16)。		// 也可以讀取整個(gè)RSA文件的字節(jié)流,從中獲取公鑰及Modulus,序列號(hào)等信息。		InputStream in2 = new FileInputStream("G:/META-INF/CERT.RSA");		byte[] bs = new byte[2048];		int len = in2.read(bs);		byte[] bs2 = new byte[len];		System.arraycopy(bs, 0, bs2, 0, len);		System.out.println(new BigInteger(bs2).toString(16));// 公鑰的16進(jìn)制字符串也在其中。	}

SHA1加密代碼:

public class SHA1 {  private final int[] abcde = {      0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0     };   // 摘要數(shù)據(jù)存儲(chǔ)數(shù)組   private int[] digestInt = new int[5];   // 計(jì)算過程中的臨時(shí)數(shù)據(jù)存儲(chǔ)數(shù)組   private int[] tmpData = new int[80];   // 計(jì)算sha-1摘要   private int process_input_bytes(byte[] bytedata) {     // 初試化常量     System.arraycopy(abcde, 0, digestInt, 0, abcde.length);     // 格式化輸入字節(jié)數(shù)組,補(bǔ)10及長(zhǎng)度數(shù)據(jù)     byte[] newbyte = byteArrayFormatData(bytedata);     // 獲取數(shù)據(jù)摘要計(jì)算的數(shù)據(jù)單元個(gè)數(shù)     int MCount = newbyte.length / 64;     // 循環(huán)對(duì)每個(gè)數(shù)據(jù)單元進(jìn)行摘要計(jì)算     for (int pos = 0; pos < MCount; pos++) {       // 將每個(gè)單元的數(shù)據(jù)轉(zhuǎn)換成16個(gè)整型數(shù)據(jù),并保存到tmpData的前16個(gè)數(shù)組元素中       for (int j = 0; j < 16; j++) {         tmpData[j] = byteArrayToInt(newbyte, (pos * 64) + (j * 4));       }       // 摘要計(jì)算函數(shù)       encrypt();     }     return 20;   }   // 格式化輸入字節(jié)數(shù)組格式   private byte[] byteArrayFormatData(byte[] bytedata) {     // 補(bǔ)0數(shù)量     int zeros = 0;     // 補(bǔ)位后總位數(shù)     int size = 0;     // 原始數(shù)據(jù)長(zhǎng)度     int n = bytedata.length;     // 模64后的剩余位數(shù)     int m = n % 64;     // 計(jì)算添加0的個(gè)數(shù)以及添加10后的總長(zhǎng)度     if (m < 56) {       zeros = 55 - m;       size = n - m + 64;     } else if (m == 56) {       zeros = 63;       size = n + 8 + 64;     } else {       zeros = 63 - m + 56;       size = (n + 64) - m + 64;     }     // 補(bǔ)位后生成的新數(shù)組內(nèi)容     byte[] newbyte = new byte[size];     // 復(fù)制數(shù)組的前面部分     System.arraycopy(bytedata, 0, newbyte, 0, n);     // 獲得數(shù)組Append數(shù)據(jù)元素的位置     int l = n;     // 補(bǔ)1操作     newbyte[l++] = (byte) 0x80;     // 補(bǔ)0操作     for (int i = 0; i < zeros; i++) {       newbyte[l++] = (byte) 0x00;     }     // 計(jì)算數(shù)據(jù)長(zhǎng)度,補(bǔ)數(shù)據(jù)長(zhǎng)度位共8字節(jié),長(zhǎng)整型     long N = (long) n * 8;     byte h8 = (byte) (N & 0xFF);     byte h7 = (byte) ((N >> 8) & 0xFF);     byte h6 = (byte) ((N >> 16) & 0xFF);     byte h5 = (byte) ((N >> 24) & 0xFF);     byte h4 = (byte) ((N >> 32) & 0xFF);     byte h3 = (byte) ((N >> 40) & 0xFF);     byte h2 = (byte) ((N >> 48) & 0xFF);     byte h1 = (byte) (N >> 56);     newbyte[l++] = h1;     newbyte[l++] = h2;     newbyte[l++] = h3;     newbyte[l++] = h4;     newbyte[l++] = h5;     newbyte[l++] = h6;     newbyte[l++] = h7;     newbyte[l++] = h8;     return newbyte;   }   private int f1(int x, int y, int z) {     return (x & y) | (~x & z);   }   private int f2(int x, int y, int z) {     return x ^ y ^ z;   }   private int f3(int x, int y, int z) {     return (x & y) | (x & z) | (y & z);   }   private int f4(int x, int y) {     return (x << y) | x >>> (32 - y);   }   // 單元摘要計(jì)算函數(shù)   private void encrypt() {     for (int i = 16; i <= 79; i++) {       tmpData[i] = f4(tmpData[i - 3] ^ tmpData[i - 8] ^ tmpData[i - 14] ^           tmpData[i - 16], 1);     }     int[] tmpabcde = new int[5];     for (int i1 = 0; i1 < tmpabcde.length; i1++) {       tmpabcde[i1] = digestInt[i1];     }     for (int j = 0; j <= 19; j++) {       int tmp = f4(tmpabcde[0], 5) +         f1(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +         tmpData[j] + 0x5a827999;       tmpabcde[4] = tmpabcde[3];       tmpabcde[3] = tmpabcde[2];       tmpabcde[2] = f4(tmpabcde[1], 30);       tmpabcde[1] = tmpabcde[0];       tmpabcde[0] = tmp;     }     for (int k = 20; k <= 39; k++) {       int tmp = f4(tmpabcde[0], 5) +         f2(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +         tmpData[k] + 0x6ed9eba1;       tmpabcde[4] = tmpabcde[3];       tmpabcde[3] = tmpabcde[2];       tmpabcde[2] = f4(tmpabcde[1], 30);       tmpabcde[1] = tmpabcde[0];       tmpabcde[0] = tmp;     }     for (int l = 40; l <= 59; l++) {       int tmp = f4(tmpabcde[0], 5) +         f3(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +         tmpData[l] + 0x8f1bbcdc;       tmpabcde[4] = tmpabcde[3];       tmpabcde[3] = tmpabcde[2];       tmpabcde[2] = f4(tmpabcde[1], 30);       tmpabcde[1] = tmpabcde[0];       tmpabcde[0] = tmp;     }     for (int m = 60; m <= 79; m++) {       int tmp = f4(tmpabcde[0], 5) +         f2(tmpabcde[1], tmpabcde[2], tmpabcde[3]) + tmpabcde[4] +         tmpData[m] + 0xca62c1d6;       tmpabcde[4] = tmpabcde[3];       tmpabcde[3] = tmpabcde[2];       tmpabcde[2] = f4(tmpabcde[1], 30);       tmpabcde[1] = tmpabcde[0];       tmpabcde[0] = tmp;     }     for (int i2 = 0; i2 < tmpabcde.length; i2++) {       digestInt[i2] = digestInt[i2] + tmpabcde[i2];     }     for (int n = 0; n < tmpData.length; n++) {       tmpData[n] = 0;     }   }   // 4字節(jié)數(shù)組轉(zhuǎn)換為整數(shù)   private int byteArrayToInt(byte[] bytedata, int i) {     return ((bytedata[i] & 0xff) << 24) | ((bytedata[i + 1] & 0xff) << 16) |     ((bytedata[i + 2] & 0xff) << 8) | (bytedata[i + 3] & 0xff);   }   // 整數(shù)轉(zhuǎn)換為4字節(jié)數(shù)組   private void intToByteArray(int intValue, byte[] byteData, int i) {     byteData[i] = (byte) (intValue >>> 24);     byteData[i + 1] = (byte) (intValue >>> 16);     byteData[i + 2] = (byte) (intValue >>> 8);     byteData[i + 3] = (byte) intValue;   }   // 將字節(jié)轉(zhuǎn)換為十六進(jìn)制字符串   private static String byteToHexString(byte ib) {     char[] Digit = {         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',         'D', 'E', 'F'       };     char[] ob = new char[2];     ob[0] = Digit[(ib >>> 4) & 0X0F];     ob[1] = Digit[ib & 0X0F];     String s = new String(ob);     return s;   }   // 將字節(jié)數(shù)組轉(zhuǎn)換為十六進(jìn)制字符串   private static String byteArrayToHexString(byte[] bytearray) {     String strDigest = "";     for (int i = 0; i < bytearray.length; i++) {       strDigest += byteToHexString(bytearray[i]);     }     return strDigest;   }   // 計(jì)算sha-1摘要,返回相應(yīng)的字節(jié)數(shù)組   public byte[] getDigestOfBytes(byte[] byteData) {     process_input_bytes(byteData);     byte[] digest = new byte[20];     for (int i = 0; i < digestInt.length; i++) {       intToByteArray(digestInt[i], digest, i * 4);     }     return digest;   }   // 計(jì)算sha-1摘要,返回相應(yīng)的十六進(jìn)制字符串   public String getDigestOfString(byte[] byteData) {     return byteArrayToHexString(getDigestOfBytes(byteData));   }   public static void main(String[] args) {     String data = "1234556";     System.out.println(data);    String digest = new SHA1().getDigestOfString(data.getBytes());     System.out.println(digest);        // System.out.println( ToMD5.convertSHA1(data).toUpperCase());  } } 

以上這篇android 關(guān)于利用簽名的SHA1進(jìn)行安全校驗(yàn)的方法之一(推薦)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 平泉县| 来安县| 奈曼旗| 株洲市| 石城县| 五常市| 山阴县| 河津市| 白水县| 股票| 太保市| 丰原市| 荃湾区| 青岛市| 尖扎县| 萝北县| 小金县| 福海县| 宜兰市| 龙里县| 定日县| 双桥区| 湛江市| 郁南县| 巫溪县| 涞源县| 凤城市| 唐河县| 宜章县| 拉孜县| 博客| 大方县| 泰和县| 池州市| 徐汇区| 吉木萨尔县| 宁城县| 建瓯市| 洪湖市| 蓬溪县| 古蔺县|