android 高級之旅 (二十) 應用安全與數據加密總結

應該堅決拒絕 不加密地明文傳輸敏感數據 這對整個安卓生態都是不負責的!

最危險的是直接使用HTTP協議登錄賬戶或交換數據。例如,攻擊者在自己設置的釣魚網絡中配置DNS服務器,將軟件要連接的服務器域名解析至攻擊者的另一臺服務器在,這臺服務器就可以獲得用戶登錄信息,或者充當客戶端與原服務器的中間人,轉發雙方數據。

這類問題的解決方法很顯然-----對敏感數據採用基於SSL/TLS的HTTPS進行傳輸。

Android使用https

HTTPS和HTTP的區別主要如下:

  1. https協議需要到ca申請證書,一般免費證書較少,因而需要一定費用。
  2. http是超文本傳輸協議,信息是明文傳輸,https則是具有安全性的ssl加密傳輸協議。
  3. http的連接很簡單,是無狀態的;HTTPS協議是由SSL+HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比http協議安全。

安卓裏使用https的步驟如下,創建一個HttpsURLConnection並設置屬性,然後去效驗本地證書,接着發起請求,接受結果。

1.創建一個HttpsURLConnection並設置屬性

URL url = new URL("https://baidu.com");
HttpsURLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setConnectTimeout(Common.TIME_OUT);
urlConnection.setReadTimeout(Common.FILE_TIME_OUT);
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
urlConnection.setRequestProperty("Accept-Encoding", "gzip");

2.效驗本地證書有兩種方式,一種是不安全的,什麼證書都相信,另一種則是隻相信指定的證書,對其進行效驗。

效驗要通過SSLSocketFactory創建的 SSLSocket來進行,默認的 SSLSocketFactory校驗服務器的證書時,會信任設備內置的100多個根證書。

private synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
  try {
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, null, null);
    return defaultSslSocketFactory = sslContext.getSocketFactory();
  } catch (GeneralSecurityException e) {
    throw new AssertionError();
  }
}

校驗服務器的證書,其實就是通過TrustManager來操作的,更一般的說是X509TrustManager;

private static synchronized SSLSocketFactory getDefaultSSLSocketFactory() {
    try {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{
                new X509TrustManager() {
                    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    }

                    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    }

                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[0];
                    }
                }
        }, null);
        return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
        throw new AssertionError();
    }
}

這種效驗過程什麼證書都會信任,沒有安全性可言,接下來我們看正確的配置

public static SSLContext getSSLContext() {
    // 從assets中加載證書,取到證書的輸入流
    InputStream is = getApplicationContext().getAssets().open("srca.cer");
    // 證書工廠
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    Certificate ca = cf.generateCertificate(is);

    // 加載證書到密鑰庫中
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null);
    keyStore.setCertificateEntry("cert", ca);

    // 加載密鑰庫到信任管理器
    String algorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
    trustManagerFactory.init(keyStore);
    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();

    // 用 TrustManager 初始化一個 SSLContext
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustManagers, null);
    urlCon.setSSLSocketFactory(sslContext.getSocketFactory());
}

Android P 限制了明文流量的網絡請求,非加密的流量請求都會被系統禁止掉,如果當前應用的請求是http請求,而非 https,這樣就會導系統禁止當前應用進行該請求,如果WebView 的url用http協議,同樣會出現加載失敗,https則不受影響。
所以需要做一個簡單的配置,在manifest的application標籤下增加一行配置,同時增加一個xml文件。

  android:networkSecurityConfig="@xml/network_security_config"
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" overridePins="true"/>
            <certificates src="user" overridePins="true" />
        </trust-anchors>
    </base-config>
</network-security-config>

然後傳遞參數並獲取結果

Iterator<Entry<String, Object>> it = params.entrySet().iterator();
    while (it.hasNext()) {
        Entry<String, Object> element = (Entry<String, Object>) it.next();
        sb2.append(element.getKey());
        sb2.append("=");
        sb2.append(element.getValue());
        sb2.append("&");
    }
    if (sb2.length() > 0) {
        sb2.deleteCharAt(sb2.length() - 1);
    }
    os = urlCon.getOutputStream();
    os.write(sb2.toString().getBytes());
    os.flush();

    int statusCode = urlCon.getResponseCode();
    if (statusCode == HttpsURLConnection.HTTP_OK) {
        is = urlCon.getInputStream();
        String contentEncoding = urlCon.getContentEncoding();
        if ((contentEncoding != null) && contentEncoding.contains("gzip")) {
            is = new GZIPInputStream(new BufferedInputStream(is));
        }
        // 創建字節輸出流對象
        baos = new ByteArrayOutputStream();
        // 定義讀取的長度
        int len = 0;
        // 定義緩衝區
        byte[] buffer = new byte[1024];
        // 按照緩衝區的大小,循環讀取
        while ((len = is.read(buffer)) != -1) {
            // 根據讀取的長度寫入到os對象中
            baos.write(buffer, 0, len);
        }
        // 返回字符串
        String result = new String(baos.toByteArray(), "utf-8");
    }

https比較消耗流量,所以可以採用http配合高強度加密的方式來進行網絡傳輸

Android中使用 加解密方式

常見的加解密方式:
Base64加密, 單向加密(MD5和SHA), 對稱加密(DES和AES), 非對稱加密(RSA), 非數字簽名等。

  • Base64算法
    Base64是一種基於64個基本字符,加密後的內容只包含這64個字符,加密後長度會變大。它是最簡單的一種算法,一般用於加密URL,還有在進行對稱和非對稱加解密前也會進行一下Base64轉換,保證沒有特殊字符,從而進行網絡傳輸。
// 需要引入包:java.util.Base64
    // Base64加密
    private static String encode(String str) {
        byte[] encodeBytes = Base64.getEncoder().encode(str.getBytes());
        return new String(encodeBytes);
    }

    // Base64解密
    private static String decode(String str) {
        byte[] decodeBytes = Base64.getDecoder().decode(str.getBytes());
        return new String(decodeBytes);
    }
  • MD5
    計算字符串MD5值
@NonNull
    public static String md5(String string) {
        if (TextUtils.isEmpty(string)) {
            return "";
        }
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest(string.getBytes());
            StringBuilder result = new StringBuilder();
            for (byte b : bytes) {
                String temp = Integer.toHexString(b & 0xff);
                if (temp.length() == 1) {
                    temp = "0" + temp;
                }
                result.append(temp);
            }
            return result.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

計算文件的MD5值

@NonNull
    public static String md5(File file) {
        if (file == null || !file.isFile() || !file.exists()) {
            return "";
        }
        FileInputStream in = null;
        String result = "";
        byte buffer[] = new byte[8192];
        int len;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            in = new FileInputStream(file);
            while ((len = in.read(buffer)) != -1) {
                md5.update(buffer, 0, len);
            }
            byte[] bytes = md5.digest();
            for (byte b : bytes) {
                String temp = Integer.toHexString(b & 0xff);
                if (temp.length() == 1) {
                    temp = "0" + temp;
                }
                result += temp;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
MD5加密安全性

雖然MD5加密本身是不可逆的,但並不是不可破譯的,常見破解機制爲窮舉法,即 跑字典,一些常見的密碼很容易在大型數據庫中匹配到相同的MD5值,所以我們要想辦法增加加密安全性。
1、對字符串多次MD5加密

@NonNull
    public static String md5(String string, int times) {
        if (TextUtils.isEmpty(string)) {
            return "";
        }
        String md5 = md5(string);
        for (int i = 0; i < times - 1; i++) {
            md5 = md5(md5);
        }
        return md5(md5);
    }

2、MD5加鹽
加鹽就是使用一個額外的鹽值與原字符串一起加密,通常鹽值可以使用用戶名、string明文的hascode或是隨機生成的字符串。

@NonNull
    public static String md5(String string, String slat) {
        if (TextUtils.isEmpty(string)) {
            return "";
        }
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest((string + slat).getBytes());
            StringBuilder result = new StringBuilder();
            for (byte b : bytes) {
                String temp = Integer.toHexString(b & 0xff);
                if (temp.length() == 1) {
                    temp = "0" + temp;
                }
                result.append(temp);
            }
            return result.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
  • 對稱加密算法
    對稱加密是指加解密使用同一個祕鑰,這需要雙方事前都知道該祕鑰。主要分爲DES和AES,一般會在加解密前進行Base64轉換,去除特殊字符,從而進行網絡傳輸。
    1.DES:數據標準加密
    DES算法經過16論迭代,使用56比特長度密鑰加密64比特長度(分組長度)的明文獲得64比特的密文。
/**
 * 數據標準加密
 * DES算法經過16論迭代,使用56比特長度密鑰加密64比特長度(分組長度)的明文獲得64比特的密文。
 */
public class DESUtil {
    // 初始化向量
    private static byte[] iv = { 'a', 'b', 'c', 'd', 'e', 1, 2, '*' };

    // DES加密
    // encryptText爲原文
    // encryptKey爲密匙
    private static String encryptDES(String encryptText, String encryptKey)
            throws Exception {
        // 實例化IvParameterSpec對象,使用指定的初始化向量
        IvParameterSpec spec = new IvParameterSpec(iv);
        // 實例化SecretKeySpec類,根據字節數組來構造SecretKeySpec
        SecretKeySpec key = new SecretKeySpec(encryptKey.getBytes(), "DES");
        // 創建密碼器
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        // 用密碼初始化Cipher對象
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
        // 執行加密操作
        byte[] encryptData = cipher.doFinal(encryptText.getBytes());
        // 返回加密後的數據
        return Base64.getEncoder().encodeToString(encryptData);
    }

    // 解密
    private static String decryptDES(String decryptString, String decryptKey)
            throws Exception {
        // 先使用Base64解密
        byte[] base64byte = Base64.getDecoder().decode(decryptString);
        // 實例化IvParameterSpec對象,使用指定的初始化向量
        IvParameterSpec spec = new IvParameterSpec(iv);
        // 實例化SecretKeySpec類,根據字節數組來構造SecretKeySpec
        SecretKeySpec key = new SecretKeySpec(decryptKey.getBytes(), "DES");
        // 創建密碼器
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        // 用密碼初始化Cipher對象
        cipher.init(Cipher.DECRYPT_MODE, key, spec);
        // 獲取解密後的數據
        byte decryptedData[] = cipher.doFinal(base64byte);
        // 將解密後數據轉換爲字符串輸出
        return new String(decryptedData);
    }

}

2.AES:高級加密標準
AES算法用於替代DES,保護敏感信息,AES算法的分組長度爲128比特,其密鑰長度分別爲128比特,192比特,256比特。


/**
 * AES:高級加密標準
 * AES算法用於替代DES,保護敏感信息,AES算法的分組長度爲128比特,其密鑰長度分別爲128比特,192比特,256比特。
 */
public class AESUtil {
    // 採用對稱分組密碼體制,密鑰長度的最少支持爲128、192、256
    String key = "abcdefghijklmnop";
    // 初始化向量參數,AES 爲16bytes. DES 爲8bytes, 16*8=128
    String initVector = "0000000000000000";
    IvParameterSpec iv;
    SecretKeySpec skeySpec;
    Cipher cipher;

    private static class HOLDER {
        private static AESUtil instance = new AESUtil();
    }

    public static AESUtil getInstance() {
        return HOLDER.instance;
    }

    private AESUtil() {
        try {
            iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
            // 這是CBC模式
            // cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            // 默認就是ECB模式
            cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public String encrypt(String value) {
        try {
            // CBC模式需要傳入向量,ECB模式不需要
            // cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(value.getBytes());
            return Base64.encodeToString(encrypted, Base64.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public String decrypt(String encrypted) {
        try {
            // CBC模式需要傳入向量,ECB模式不需要
            // cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] original = cipher.doFinal(Base64.decode(encrypted, Base64.DEFAULT));
            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
}

AES和DES在SecretKeySpec和Cipher創建的時候有不同,Cipher傳入的參數"AES/ECB/PKCS5PADDING"分別是“算法/工作模式/填充模式”,工作模式默認是ECB,不需要偏移,也就不需要傳入向量,而CBC和OFB是帶偏移的,需要傳入向量。

  • 非對稱加密算法
    非對稱加密是指加解密使用不同的祕鑰,即公鑰和私鑰。私鑰只能由一方安全保管,不能外泄,而公鑰則可以發給任何請求它的人。非對稱加密使用這對密鑰中的一個進行加密,而解密則需要另一個密鑰。比如你向銀行請求將公鑰公鑰發給你,並使用公鑰對消息加密,那麼只有持有私鑰的銀行才能對你的消息解密。與對稱加密不同的是,私鑰不需要通過網絡發送出去,因此安全性大大提高。
    非對稱加密主要是RSA算法,RSA加解密本身對明文或者密文的長度有限制,這裏我們的加密算法可以支持分段加解密。

/**
 * 非對稱加密算法
 */
public class RSAUtil {

    public static final String RSA = "RSA";
    public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";
    // 祕鑰默認長度
    public static final int DEFAULT_KEY_SIZE = 2048;
    // 當要加密的內容超過bufferSize,則採用partSplit進行分塊加密
    public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes();
    // 當前祕鑰支持加密的最大字節數
    public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;

    // 隨機生成RSA密鑰對,密鑰長度,範圍:512~2048
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 私鑰加密
     * @param data       待加密數據
     * @param privateKey 密鑰
     * @return byte[] 加密數據
     */
    public static byte[] encryptByPrivateKey(byte[] data, byte[] privateKey) throws Exception {
        // 得到私鑰
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PrivateKey keyPrivate = kf.generatePrivate(keySpec);
        // 數據加密
        Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
        cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);
        return cipher.doFinal(data);
    }

    // 使用私鑰進行解密
    public static byte[] decryptByPrivateKey(byte[] encrypted, byte[] privateKey) throws Exception {
        // 得到私鑰
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PrivateKey keyPrivate = kf.generatePrivate(keySpec);
        // 解密數據
        Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
        cp.init(Cipher.DECRYPT_MODE, keyPrivate);
        byte[] arr = cp.doFinal(encrypted);
        return arr;
    }

    // 用公鑰對字符串進行加密
    public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
        // 得到公鑰
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PublicKey keyPublic = kf.generatePublic(keySpec);
        // 加密數據
        Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
        cp.init(Cipher.ENCRYPT_MODE, keyPublic);
        return cp.doFinal(data);
    }

    /**
     * 公鑰解密
     * @param data      待解密數據
     * @param publicKey 密鑰
     * @return byte[] 解密數據
     */
    public static byte[] decryptByPublicKey(byte[] data, byte[] publicKey) throws Exception {
        // 得到公鑰
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
        KeyFactory kf = KeyFactory.getInstance(RSA);
        PublicKey keyPublic = kf.generatePublic(keySpec);
        // 數據解密
        Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
        cipher.init(Cipher.DECRYPT_MODE, keyPublic);
        return cipher.doFinal(data);
    }

    // 以下開始分段解密
    // 使用私鑰分段解密
    public static byte[] decryptByPrivateKeyForSpilt(byte[] encrypted, byte[] privateKey) throws Exception {
        int splitLen = DEFAULT_SPLIT.length;
        if (splitLen <= 0) {
            return decryptByPrivateKey(encrypted, privateKey);
        }
        int dataLen = encrypted.length;
        List<Byte> allBytes = new ArrayList<Byte>(1024);
        int latestStartIndex = 0;
        for (int i = 0; i < dataLen; i++) {
            byte bt = encrypted[i];
            boolean isMatchSplit = false;
            if (i == dataLen - 1) {
                // 到data的最後了
                byte[] part = new byte[dataLen - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPrivateKey(part, privateKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            } else if (bt == DEFAULT_SPLIT[0]) {
                // 這個是以split[0]開頭
                if (splitLen > 1) {
                    if (i + splitLen < dataLen) {
                        // 沒有超出data的範圍
                        for (int j = 1; j < splitLen; j++) {
                            if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                                break;
                            }
                            if (j == splitLen - 1) {
                                // 驗證到split的最後一位,都沒有break,則表明已經確認是split段
                                isMatchSplit = true;
                            }
                        }
                    }
                } else {
                    // split只有一位,則已經匹配了
                    isMatchSplit = true;
                }
            }
            if (isMatchSplit) {
                byte[] part = new byte[i - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPrivateKey(part, privateKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }

    // 私鑰分段加密
    public static byte[] encryptByPrivateKeyForSpilt(byte[] data, byte[] privateKey) throws Exception {
        int dataLen = data.length;
        if (dataLen <= DEFAULT_BUFFERSIZE) {
            return encryptByPrivateKey(data, privateKey);
        }
        List<Byte> allBytes = new ArrayList<Byte>(2048);
        int bufIndex = 0;
        int subDataLoop = 0;
        byte[] buf = new byte[DEFAULT_BUFFERSIZE];
        for (int i = 0; i < dataLen; i++) {
            buf[bufIndex] = data[i];
            if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
                subDataLoop++;
                if (subDataLoop != 1) {
                    for (byte b : DEFAULT_SPLIT) {
                        allBytes.add(b);
                    }
                }
                byte[] encryptBytes = encryptByPrivateKey(buf, privateKey);
                for (byte b : encryptBytes) {
                    allBytes.add(b);
                }
                bufIndex = 0;
                if (i == dataLen - 1) {
                    buf = null;
                } else {
                    buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
                }
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }

    // 用公鑰對字符串進行分段加密
    public static byte[] encryptByPublicKeyForSpilt(byte[] data, byte[] publicKey) throws Exception {
        int dataLen = data.length;
        if (dataLen <= DEFAULT_BUFFERSIZE) {
            return encryptByPublicKey(data, publicKey);
        }
        List<Byte> allBytes = new ArrayList<Byte>(2048);
        int bufIndex = 0;
        int subDataLoop = 0;
        byte[] buf = new byte[DEFAULT_BUFFERSIZE];
        for (int i = 0; i < dataLen; i++) {
            buf[bufIndex] = data[i];
            if (++bufIndex == DEFAULT_BUFFERSIZE || i == dataLen - 1) {
                subDataLoop++;
                if (subDataLoop != 1) {
                    for (byte b : DEFAULT_SPLIT) {
                        allBytes.add(b);
                    }
                }
                byte[] encryptBytes = encryptByPublicKey(buf, publicKey);
                for (byte b : encryptBytes) {
                    allBytes.add(b);
                }
                bufIndex = 0;
                if (i == dataLen - 1) {
                    buf = null;
                } else {
                    buf = new byte[Math.min(DEFAULT_BUFFERSIZE, dataLen - i - 1)];
                }
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }

    // 公鑰分段解密
    public static byte[] decryptByPublicKeyForSpilt(byte[] encrypted, byte[] publicKey) throws Exception {
        int splitLen = DEFAULT_SPLIT.length;
        if (splitLen <= 0) {
            return decryptByPublicKey(encrypted, publicKey);
        }
        int dataLen = encrypted.length;
        List<Byte> allBytes = new ArrayList<Byte>(1024);
        int latestStartIndex = 0;
        for (int i = 0; i < dataLen; i++) {
            byte bt = encrypted[i];
            boolean isMatchSplit = false;
            if (i == dataLen - 1) {
                // 到data的最後了
                byte[] part = new byte[dataLen - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPublicKey(part, publicKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            } else if (bt == DEFAULT_SPLIT[0]) {
                // 這個是以split[0]開頭
                if (splitLen > 1) {
                    if (i + splitLen < dataLen) {
                        // 沒有超出data的範圍
                        for (int j = 1; j < splitLen; j++) {
                            if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                                break;
                            }
                            if (j == splitLen - 1) {
                                // 驗證到split的最後一位,都沒有break,則表明已經確認是split段
                                isMatchSplit = true;
                            }
                        }
                    }
                } else {
                    // split只有一位,則已經匹配了
                    isMatchSplit = true;
                }
            }
            if (isMatchSplit) {
                byte[] part = new byte[i - latestStartIndex];
                System.arraycopy(encrypted, latestStartIndex, part, 0, part.length);
                byte[] decryptPart = decryptByPublicKey(part, publicKey);
                for (byte b : decryptPart) {
                    allBytes.add(b);
                }
                latestStartIndex = i + splitLen;
                i = latestStartIndex - 1;
            }
        }
        byte[] bytes = new byte[allBytes.size()];
        {
            int i = 0;
            for (Byte b : allBytes) {
                bytes[i++] = b.byteValue();
            }
        }
        return bytes;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章