常用的加密方式和應(yīng)用場(chǎng)景

日常代碼中,一些網(wǎng)絡(luò)傳輸?shù)膬?nèi)容,接口參數(shù),或者本地存儲(chǔ)數(shù)據(jù)都會(huì)經(jīng)過(guò)加密,不過(guò)一般都是根據(jù)什么樣的數(shù)據(jù)來(lái)使用不同的加密方法。
下面列舉一些常規(guī)的加密方法.
Base64
Base64是網(wǎng)絡(luò)上最常見(jiàn)的用于傳輸8Bit字節(jié)代碼的編碼方式之一,Base64并不是安全領(lǐng)域的加密算法,其實(shí)Base64只能算是一個(gè)編碼算法,對(duì)數(shù)據(jù)內(nèi)容進(jìn)行編碼來(lái)適合傳輸。標(biāo)準(zhǔn)Base64編碼解碼無(wú)需額外信息即完全可逆,即使你自己自定義字符集設(shè)計(jì)一種類Base64的編碼方式用于數(shù)據(jù)加密,在多數(shù)場(chǎng)景下也較容易破解。Base64編碼本質(zhì)上是一種將二進(jìn)制數(shù)據(jù)轉(zhuǎn)成文本數(shù)據(jù)的方案。對(duì)于非二進(jìn)制數(shù)據(jù),是先將其轉(zhuǎn)換成二進(jìn)制形式,然后每連續(xù)6比特(2的6次方=64)計(jì)算其十進(jìn)制值,根據(jù)該值在A--Z,a--z,0--9,+,/ 這64個(gè)字符中找到對(duì)應(yīng)的字符,最終得到一個(gè)文本字符串?;疽?guī)則如下幾點(diǎn):

  1,標(biāo)準(zhǔn)Base64只有64個(gè)字符(英文大小寫、數(shù)字和+、/)以及用作后綴等號(hào);
  2,Base64是把3個(gè)字節(jié)變成4個(gè)可打印字符,所以Base64編碼后的字符串一定能被4整除(不算用作后綴的等號(hào));
  3,等號(hào)一定用作后綴,且數(shù)目一定是0個(gè)、1個(gè)或2個(gè)。這是因?yàn)槿绻拈L(zhǎng)度不能被3整除,Base64要在后面添加\0湊齊3n位。為了正確還原,添加了幾個(gè)\0就加上幾個(gè)等號(hào)。顯然添加等號(hào)的數(shù)目只能是0、1或2;
  4,嚴(yán)格來(lái)說(shuō)Base64不能算是一種加密,只能說(shuō)是編碼轉(zhuǎn)換。

Base64編碼一般用于url的處理

下圖為base64編碼表


1.png

1)字符串進(jìn)行Base64編碼

   String encodedString = Base64.encodeToString("testtest".getBytes(), Base64.DEFAULT);
   Log.e("Base64", "Base64---->" + encodedString);

2)字符串進(jìn)行Base64解碼

    String decodedString =new String(Base64.decode(encodedString,Base64.DEFAULT));
    Log.e("Base64", "Base64---->" + decodedString);

針對(duì)Base64.DEFAULT參數(shù)說(shuō)明

  1)DEFAULT 這個(gè)參數(shù)是默認(rèn),使用默認(rèn)的方法來(lái)加密
  2)NO_PADDING 這個(gè)參數(shù)是略去加密字符串最后的”=”
  3)NO_WRAP 這個(gè)參數(shù)意思是略去所有的換行符(設(shè)置后CRLF就沒(méi)用了)
  4)CRLF 這個(gè)參數(shù)看起來(lái)比較眼熟,它就是Win風(fēng)格的換行符,意思就是使用CR LF這一對(duì)作為一行的結(jié)尾而不是Unix風(fēng)格的LF
  5)URL_SAFE 這個(gè)參數(shù)意思是加密時(shí)不使用對(duì)URL和文件名有特殊意義的字符來(lái)作為加密字符,具體就是以-和_取代+和/

Base64編碼看似簡(jiǎn)單,但是其在實(shí)際開(kāi)發(fā)中使用相當(dāng)廣泛。

MD5
項(xiàng)目中無(wú)論是密碼的存儲(chǔ)或者說(shuō)判斷文件是否是同一文件,都會(huì)用到MD5算法。
MD5英文全稱“Message-Digest Algorithm 5”,翻譯過(guò)來(lái)是“消息摘要算法5”,由MD2、MD3、MD4演變過(guò)來(lái)的,是一種單向加密算法,是不可逆的一種的加密方式。

MD5加密有哪些特點(diǎn)?

1) 壓縮性:任意長(zhǎng)度的數(shù)據(jù),算出的MD5值長(zhǎng)度都是固定的。
2)容易計(jì)算:從原數(shù)據(jù)計(jì)算出MD5值很容易。
3)抗修改性:對(duì)原數(shù)據(jù)進(jìn)行任何改動(dòng),哪怕只修改1個(gè)字節(jié),所得到的MD5值都有很大區(qū)別。
4)強(qiáng)抗碰撞:已知原數(shù)據(jù)和其MD5值,想找到一個(gè)具有相同MD5值的數(shù)據(jù)(即偽造數(shù)據(jù))是非常困難的。

MD5應(yīng)用場(chǎng)景:

1)一致性驗(yàn)證(密碼校驗(yàn))
2)數(shù)字簽名
3)安全訪問(wèn)認(rèn)證

算法實(shí)現(xiàn):
1,計(jì)算字符串MD5值

  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());
        String result = "";
        for (byte b : bytes) {
            String temp = Integer.toHexString(b & 0xff);
            if (temp.length() == 1) {
                temp = "0" + temp;
            }
            result += temp;
        }
        return result;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

2,計(jì)算文件的MD5值:

// 計(jì)算文件的 MD5 值
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;
}

雖然說(shuō)MD5加密本身是不可逆的,但并不是不可破譯的,網(wǎng)上有關(guān)MD5解密的網(wǎng)站很多,破解機(jī)制采用窮舉法,也就是把所有可能結(jié)果進(jìn)行一個(gè)一個(gè)檢驗(yàn)。
那么如何增加MD5破解的難度呢?
1)對(duì)字符串多次MD5加密

  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加鹽
加鹽的方式也是多種多樣

    1,string+key(鹽值key)然后進(jìn)行MD5加密
    2,用string明文的hashcode作為鹽,然后進(jìn)行MD5加密
    3,隨機(jī)生成一串字符串作為鹽,然后進(jìn)行MD5加密
  

    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());
        String result = "";
        for (byte b : bytes) {
            String temp = Integer.toHexString(b & 0xff);
            if (temp.length() == 1) {
                temp = "0" + temp;
            }
            result += temp;
        }
        return result;
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

RSA
RSA算法是最流行的公鑰密碼算法,使用長(zhǎng)度可以變化的密鑰。RSA是第一個(gè)既能用于數(shù)據(jù)加密也能用于數(shù)字簽名的算法。
這里有對(duì)RSA算法的詳細(xì)解釋http://bank.hexun.com/2009-06-24/118958531.html

RSA的安全性依賴于大數(shù)分解,小于1024位的N已經(jīng)被證明是不安全的,而且由于RSA算法進(jìn)行的都是大數(shù)計(jì)算,使得RSA最快的情況也比DES慢上倍,這是RSA最大的缺陷,因此通常只能用于加密少量數(shù)據(jù)或者加密密鑰,但RSA仍然不失為一種高強(qiáng)度的算法。

  //幾個(gè)常用變量
 public static final String RSA = "RSA";// 非對(duì)稱加密密鑰算法
  public static final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式
  public static final int DEFAULT_KEY_SIZE = 2048;//秘鑰默認(rèn)長(zhǎng)度
  public static final byte[] DEFAULT_SPLIT = "#PART#".getBytes();    // 當(dāng)要加密的內(nèi)容超過(guò)bufferSize,則采用partSplit進(jìn)行分塊加密
  public static final int DEFAULT_BUFFERSIZE = (DEFAULT_KEY_SIZE / 8) - 11;// 當(dāng)前秘鑰支持加密的最大字節(jié)數(shù)

第一步:首先生成秘鑰對(duì)

    /**
 * 隨機(jī)生成RSA密鑰對(duì)
 *
 * @param keyLength 密鑰長(zhǎng)度,范圍:512~2048
 *                  一般1024
 * @return
 */
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;
    }
}

具體加密實(shí)現(xiàn):
公鑰加密

  /**
 * 用公鑰對(duì)字符串進(jìn)行加密
 *
 * @param data 原文
 */
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);
    // 加密數(shù)據(jù)
    Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
    cp.init(Cipher.ENCRYPT_MODE, keyPublic);
    return cp.doFinal(data);
}

私鑰加密

  /**
 * 私鑰加密
 *
 * @param data       待加密數(shù)據(jù)
 * @param privateKey 密鑰
 * @return byte[] 加密數(shù)據(jù)
 */
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);
    // 數(shù)據(jù)加密
    Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
    cipher.init(Cipher.ENCRYPT_MODE, keyPrivate);
    return cipher.doFinal(data);
}

公鑰解密

/**
 * 公鑰解密
 *
 * @param data      待解密數(shù)據(jù)
 * @param publicKey 密鑰
 * @return byte[] 解密數(shù)據(jù)
 */
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);
    // 數(shù)據(jù)解密
    Cipher cipher = Cipher.getInstance(ECB_PKCS1_PADDING);
    cipher.init(Cipher.DECRYPT_MODE, keyPublic);
    return cipher.doFinal(data);
}

私鑰解密

/**
 * 使用私鑰進(jìn)行解密
 */
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);

    // 解密數(shù)據(jù)
    Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING);
    cp.init(Cipher.DECRYPT_MODE, keyPrivate);
    byte[] arr = cp.doFinal(encrypted);
    return arr;
}

但是這里需要注意的是android系統(tǒng)的RSA實(shí)現(xiàn)是"RSA/None/NoPadding",而標(biāo)準(zhǔn)JDK實(shí)現(xiàn)是"RSA/None/PKCS1Padding" ,這造成了在android機(jī)上加密后無(wú)法在服務(wù)器上解密的原因,所以在實(shí)現(xiàn)的時(shí)候這個(gè)一定要注意。
RSA非對(duì)稱加密內(nèi)容長(zhǎng)度有限制,1024位key的最多只能加密127位數(shù)據(jù),否則就會(huì)報(bào)錯(cuò)(javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes) , RSA 是常用的非對(duì)稱加密算法。研究發(fā)現(xiàn)是由于待加密的數(shù)據(jù)超長(zhǎng)所致。RSA 算法規(guī)定:待加密的字節(jié)數(shù)不能超過(guò)密鑰的長(zhǎng)度值除以 8 再減去 11(即:KeySize / 8 - 11),而加密后得到密文的字節(jié)數(shù),正好是密鑰的長(zhǎng)度值除以 8(即:KeySize / 8)。

公鑰分段加密

/**
 * 用公鑰對(duì)字符串進(jìn)行分段加密
 *
 */
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;
}

私鑰分段加密

/**
 * 分段加密
 *
 * @param data       要加密的原始數(shù)據(jù)
 * @param privateKey 秘鑰
 */
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;
}

公鑰分段解密

/**
 * 公鑰分段解密
 *
 * @param encrypted 待解密數(shù)據(jù)
 * @param publicKey 密鑰
 */
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]) {
            // 這個(gè)是以split[0]開(kāi)頭
            if (splitLen > 1) {
                if (i + splitLen < dataLen) {
                    // 沒(méi)有超出data的范圍
                    for (int j = 1; j < splitLen; j++) {
                        if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                            break;
                        }
                        if (j == splitLen - 1) {
                            // 驗(yàn)證到split的最后一位,都沒(méi)有break,則表明已經(jīng)確認(rèn)是split段
                            isMatchSplit = true;
                        }
                    }
                }
            } else {
                // split只有一位,則已經(jīng)匹配了
                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;
}

私鑰分段解密

/**
 * 使用私鑰分段解密
 *
 */
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]) {
            // 這個(gè)是以split[0]開(kāi)頭
            if (splitLen > 1) {
                if (i + splitLen < dataLen) {
                    // 沒(méi)有超出data的范圍
                    for (int j = 1; j < splitLen; j++) {
                        if (DEFAULT_SPLIT[j] != encrypted[i + j]) {
                            break;
                        }
                        if (j == splitLen - 1) {
                            // 驗(yàn)證到split的最后一位,都沒(méi)有break,則表明已經(jīng)確認(rèn)是split段
                            isMatchSplit = true;
                        }
                    }
                }
            } else {
                // split只有一位,則已經(jīng)匹配了
                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;
}

以上就是對(duì)常用加密方法的整體和理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容