散列算法是把任意長度的輸入(又叫做預映射 pre-image )通過算法變換成固定長度的輸出。散列是信息的提煉病涨,通常其長度要比信息小得多富玷,且為一個固定長度。
特性:
- 高效:可以快速計算出哈希值
- 不可逆:從哈希值不能反向推導出原始數(shù)據(jù)
- 輸入敏感:原始數(shù)據(jù)只要有一點變動既穆,得到的哈希值也會差別很大
- 沖突避免:對于不同的原始數(shù)據(jù)赎懦,哈希值相同的概率非常小
使用場景:
- 安全加密。 例如網(wǎng)絡傳輸密碼幻工。
- 唯一標識励两。例如在海量的圖庫中,搜索一張圖是否存在会钝》ソ可以給每一個圖片取一個唯一標識,或者說信息摘要迁酸,通過這個唯一標識來判定圖片是否在圖庫中先鱼,這樣就可以減少很多工作量
- 數(shù)據(jù)校驗。例如下載文件的完整性
- 負載均衡奸鬓。通過哈希算法焙畔,對客戶端 IP 地址或者會話 ID 計算哈希值,將取得的哈希值與服務器個數(shù)進行取模運算串远,最終得到的值就是應該被路由到的服務器編號
- ……
常見算法:
- MD4 1990年宏多,輸出128位 (已經(jīng)不安全)
- MD5 1991年,輸出128位 (已經(jīng)不安全)
- SHA-0 1993年澡罚,輸出160位 (發(fā)布之后很快就被撤回伸但,是SHA-1的前身)
- SHA-1 1995年,輸出160位 (已經(jīng)不安全)
- SHA-2 包括 SHA-224留搔、SHA-256更胖、SHA-384,和 SHA-512,分別輸出 224却妨、256饵逐、384、512位彪标。 (目前安全)
Android Hash 工具類:
@file:JvmName("HashUtil")
import java.io.File
import java.io.FileInputStream
import java.security.MessageDigest
fun String.md5(upperCase: Boolean = true): String = hash("MD5", this, upperCase)
fun String.sha1(upperCase: Boolean = true): String = hash("SHA-1", this, upperCase)
fun String.sha256(upperCase: Boolean = true): String = hash("SHA-256", this, upperCase)
private fun hash(type: String, input: String, upperCase: Boolean = true): String {
val bytes = MessageDigest.getInstance(type).digest(input.toByteArray())
return bytes.asHex(upperCase)
}
fun ByteArray.asHex(upperCase: Boolean = true): String {
val hexChars = if (upperCase) "0123456789ABCDEF" else "0123456789abcdef"
val result = StringBuilder(size * 2)
forEach {
val octet = it.toInt()
result.append(hexChars[octet shr 4 and 0x0f])
result.append(hexChars[octet and 0x0f])
}
return result.toString()
}
fun File.md5(upperCase: Boolean = true): String {
if (!this.exists()) return ""
var fis: FileInputStream? = null
try {
fis = this.inputStream()
val digest = MessageDigest.getInstance("MD5")
val byteArray = ByteArray(1024 * 10)
var readLength = 0
while (fis.read(byteArray).also { readLength = it } != -1) {
digest.update(byteArray, 0, readLength)
}
return digest.digest().asHex(upperCase)
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
fis?.close()
} catch (e: Exception) {
}
}
return ""
}
iOS Hash 工具類:
extension String {
/// sha1 encoded of string
public var sha1: String {
// https://stackoverflow.com/questions/25761344/how-to-hash-nsstring-with-sha1-in-swift
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
/// md5 encoded of string
var md5: String {
guard let data = self.data(using: .utf8) else {
return self
}
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
#if swift(>=5.0)
_ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest)
}
#else
_ = data.withUnsafeBytes { bytes in
return CC_MD5(bytes, CC_LONG(data.count), &digest)
}
#endif
return digest.map { String(format: "%02x", $0) }.joined().uppercased()
}
/// String encoded in base64 (if applicable).
///
/// "Hello World!".base64Encoded -> Optional("SGVsbG8gV29ybGQh")
///
var base64Encoded: String? {
// https://github.com/Reza-Rg/Base64-Swift-Extension/blob/master/Base64.swift
let plainData = self.data(using: .utf8)
return plainData?.base64EncodedString()
}
}