歡迎來到「我是真的狗雜談世界」娶桦,關(guān)注不迷路
背景
最近做的項目多次遇到了分享邀請的需求點,即需要在接受邀請時能識別到邀請者的信息,又需要考慮信息敏感性啦逆,沒找到成熟的三方實現(xiàn),于是自己思考實現(xiàn)了兩套齐媒。
思路方案
不能直接將邀請信息用于傳遞巷帝,需要對信息(一般是字符串,不是字符串也可以轉(zhuǎn)換為字符串)進(jìn)行加密處理研侣,或者說編碼處理谱邪。但同時需要滿足一下要求:
要求
- 可逆:加密(編碼)后的密文應(yīng)當(dāng)能通過解密(解碼)獲得原文,否則就無法獲得邀請者信息了庶诡;
- 長度:加密(編碼)后的密文應(yīng)該盡可能與原文長度相當(dāng)惦银,可以略多(如果能更少也好,不過那需要涉及壓縮了末誓,不是今天的重點)扯俱;
- 內(nèi)容:加密(編碼)后的密文應(yīng)當(dāng)是可預(yù)知的字符集合雄坪,如果可設(shè)置更好湖笨;
- 算法可公開(意味著存在額外依賴):不單單依靠一個固定算法,即便算法公開仍舊能保證安全性张漂,否則算法一旦被破解也就沒什么了晴玖;
- 復(fù)雜度適當(dāng):太復(fù)雜的一般對計算要求很高读存,對開發(fā)(本人)成本也高,能滿足目前的項目需求即可(我絕對不會說我懶)呕屎;
常見算法
常見的一些加密解密让簿、編碼解碼算法:
- 單向:md系列、sha1榨惰;
- 對稱:des拜英、aes;
- 非對稱:rsa琅催、dsa居凶;
- 簡單編碼:base64虫给;
移位法
本質(zhì)
- 維護(hù)全量字符可能的順序,對給定字符串的每個字符按照其位置進(jìn)行移位轉(zhuǎn)換侠碧,得到結(jié)果抹估;
- 位置到字符移位偏移量通過一套外部輸入的規(guī)則來指定;
- 上述兩步相當(dāng)于將字符枚舉弄兜、順序药蜻、偏移量規(guī)則三套作為算法變量交由用戶控制,在不能完全知道三個信息的情況下替饿,即便知道算法语泽,也能保障密文安全性;
防篡改
- 雖然用移位法可以保障用戶無法通過密文還原原文视卢,但用戶可以用大量無序密文來攻擊踱卵,造成大量垃圾數(shù)據(jù)或暴力猜測到一些信息,因此還需要支持能校驗密文是否合法的功能据过,也就是防篡改惋砂。
- 通用的思路是額外加一點校驗數(shù)據(jù),由原文構(gòu)造而成加入到密文中绳锅,解密(解碼)時校驗一遍作為驗證即可西饵,比如網(wǎng)絡(luò)IP和TCP層的累加和就是這種思路。
實現(xiàn)
public function encrypt(string $value): string
{
if (0 == $this->sortCount) {
throw new EncrypterException('未設(shè)置字符排布陣列');
}
if (0 == $this->shiftingCount) {
throw new EncrypterException('未設(shè)置編碼密鑰');
}
$encrypted = '';
for ($i = 0; $i < strlen($value); $i++) {
if (!key_exists($value[$i], $this->sortList)) {
throw new EncrypterException('發(fā)現(xiàn)不期待的字符');
}
$index = $this->sortList[$value[$i]];
$index += $this->shiftingList[$i % $this->shiftingCount];
$index %= $this->sortCount;
$encrypted .= $this->sort[$index];
}
// 添加校驗位
if ($this->checkSum) {
$sum = 0;
for ($i = 0; $i < strlen($value); $i++) {
$sum += ord($value[$i]);
}
$sum %= $this->sortCount;
$start = $this->sort[$sum];
$end = $this->sort[$this->sortCount - $sum - 1];
$encrypted = $start . $encrypted . $end;
}
return $encrypted;
}
public function decrypt(string $value): string
{
if (0 == $this->sortCount) {
throw new EncrypterException('未設(shè)置字符排布陣列');
}
if (0 == $this->shiftingCount) {
throw new EncrypterException('未設(shè)置編碼密鑰');
}
// 拿出校驗位
if ($this->checkSum) {
if (strlen($value) < 2) {
throw new EncrypterException('解密校驗失敗');
}
$start = $value[0];
$end = $value[strlen($value) - 1];
$value = substr($value, 1, -1);
}
$decrypted = '';
for ($i = 0; $i < strlen($value); $i++) {
if (!key_exists($value[$i], $this->sortList)) {
throw new EncrypterException('發(fā)現(xiàn)不期待的字符');
}
$index = $this->sortList[$value[$i]];
$index -= $this->shiftingList[$i % $this->shiftingCount];
while ($index < 0) {
$index += $this->sortCount;
}
$decrypted .= $this->sort[$index];
}
// 校驗校驗位
if ($this->checkSum) {
$sum = 0;
for ($i = 0; $i < strlen($decrypted); $i++) {
$sum += ord($decrypted[$i]);
}
$sum %= $this->sortCount;
if ($start != $this->sort[$sum] || $end != $this->sort[$this->sortCount - $sum - 1]) {
throw new EncrypterException('解密校驗失敗');
}
}
return $decrypted;
}
特點分析
- 需要知道原文全部字符構(gòu)成可能鳞芙;
- 原文和密文的字符構(gòu)成是同一個集合眷柔;
- 密文與原文長度相等,即使加入校驗位也僅多2位积蜻;
- 適用于前端闯割、用戶可見可感知的傳遞場景;
異或法
本質(zhì)
- 利用abb=a竿拆,也就是兩次異或復(fù)位的特性(字符可轉(zhuǎn)換成一個數(shù)值也就是一個多位的二進(jìn)制宙拉,單位異或的特性在多位場景下同樣成立)
- 同樣與原文、密文每個字符進(jìn)行異或操作的字符應(yīng)該與其位置規(guī)則有關(guān)丙笋,同移位法相關(guān)規(guī)則
可讀性
由于異或后的值可能超過輸入原值谢澈,字符轉(zhuǎn)換時可能轉(zhuǎn)換為非常用字符影響閱讀,因此可以選擇嵌套一層base64加解密方便閱讀御板。
實現(xiàn)
public function encrypt(string $value): string
{
$value = $this->handle($value);
if ($this->base64) {
$value = base64_encode($value);
}
return $value;
}
public function decrypt(string $value): string
{
if ($this->base64) {
$value = base64_decode($value);
}
return $this->handle($value);
}
protected function handle(string $string): string
{
$result = '';
for ($i = 0; $i < strlen($string); $i++) {
$t = $this->secret[$i % $this->secretCount];
$result .= chr(ord($string[$i]) ^ ord($t));
}
return $result;
}
分析
- 即便不知道原文字符全部可能也可以使用這套算法锥忿;
- 但原文和密文的字符構(gòu)成不是同一個集合;
- base64后密文一般較原文長怠肋,具體見base64編碼算法規(guī)則敬鬓;
- 適用于接口間、服務(wù)間數(shù)據(jù)傳輸場景;