App 常用簽名設(shè)計(jì)

前言

一般來說,客戶端 App 與服務(wù)器端是通過接口進(jìn)行交互,來互相傳遞數(shù)據(jù)的,而為了保證數(shù)據(jù)的安全性,一般都會(huì)專門設(shè)計(jì)一個(gè)簽名規(guī)則。

三種安全強(qiáng)度的簽名設(shè)計(jì)

第一種:客戶端加密(對(duì)稱加密,如 AES),服務(wù)端解密,配合 token 和流水號(hào)

優(yōu)點(diǎn):能夠保持用戶登錄狀態(tài)、區(qū)分用戶,相對(duì)于不返回任何信息的登錄要安全了一些。

缺點(diǎn):如果通過網(wǎng)絡(luò)嗅探器(例如:青花瓷)可以獲取到http鏈接,會(huì)造成信息泄露,并且還能被偽造請(qǐng)求。

第二種:客戶端加密(非對(duì)稱加密,如 RSA),服務(wù)端解密,配合 token 和流水號(hào)

采用 RSA 非對(duì)稱加密。具體流程如下:

  • 客戶端向服務(wù)器第一次發(fā)起登錄請(qǐng)求(不傳輸用戶名和密碼)。

  • 服務(wù)器利用RSA算法產(chǎn)生一對(duì)公鑰和私鑰。并保留私鑰, 將公鑰發(fā)送給客戶端。

  • 客戶端收到公鑰后, 加密用戶密碼, 向服務(wù)器發(fā)起第二次登錄請(qǐng)求(傳輸用戶名和加密后的密碼)。

  • 服務(wù)器利用保留的私鑰對(duì)密文進(jìn)行解密,得到真正的密碼。

第三種:非對(duì)稱加密 + token + 流水號(hào)

前兩種方法如果 token 被截獲了,那么他人完全就能夠模擬出用戶的請(qǐng)求,所以在服務(wù)器向客戶端發(fā)送的 token 數(shù)據(jù),也需要加密。

具體流程如下:

  • 客戶端向服務(wù)器第一次發(fā)起登錄請(qǐng)求(不傳輸用戶名和密碼),服務(wù)器利用RSA算法產(chǎn)生一對(duì)公鑰和私鑰,并保留私鑰,將公鑰發(fā)送給客戶端。

  • 客戶端收到公鑰后,加密用戶密碼,向服務(wù)器發(fā)送用戶名和加密后的用戶密碼; 同時(shí)另外產(chǎn)生一對(duì)公鑰和私鑰,自己保留私鑰, 向服務(wù)器發(fā)送公鑰,于是第二次登錄請(qǐng)求傳輸了用戶名和加密后的密碼以及客戶端生成的公鑰。

  • 服務(wù)器利用保留的私鑰對(duì)密文進(jìn)行解密,得到真正的密碼。經(jīng)過判斷, 確定用戶可以登錄后,生成sessionId和token,同時(shí)利用客戶端發(fā)送的公鑰,對(duì)token進(jìn)行加密。最后將sessionId和加密后的token返還給客戶端。

  • 客戶端利用自己生成的私鑰對(duì)token密文解密,得到真正的token。

實(shí)踐

從簽名強(qiáng)度來看,上面方法第三種 > 第二種 > 第一種,所以此處省略第一二種實(shí)踐方式,主要看看第三種。

流程

具體的實(shí)踐分為登錄(拿 token) 和與用戶信息相關(guān)的接口加密(AES) ,比上文第三種簽名方式,多出一步 AES 加密:

  • 1.客戶端向服務(wù)器第一次發(fā)起登錄請(qǐng)求(注意這里是登錄請(qǐng)求,不是調(diào)登錄接口)。

  • 2.服務(wù)器生成公私鑰對(duì)(公鑰1、私鑰1),將公鑰1發(fā)送給客戶端。

  • 3.客戶端收到公鑰1后,加密用戶密碼,同時(shí)本地生成公私鑰對(duì)(公鑰2、私鑰2),調(diào)用登錄接口,將公鑰2和用公鑰1加密過后的用戶密碼發(fā)給服務(wù)端。

  • 4.服務(wù)器用私鑰1對(duì)密文進(jìn)行解密,得到真正的密碼。經(jīng)過判斷,確定用戶可以登錄后,生成 sessionId 和 token,同時(shí)用客戶端發(fā)送的公鑰2,對(duì) token 加密,最后將 sessionId 和加密后的 token 以及 AES 密鑰返給客戶端。

  • 5.客戶端用私鑰2解密,得到 token 、sessionId、AES 密鑰。

  • 6.登錄流程結(jié)束,本地存儲(chǔ)有公鑰1、公鑰2、私鑰2、AES 密鑰、sessionId 和 token。

密鑰傳遞與處理

首先要明確 RSA 公私鑰密文格式,這里建議客戶端和服務(wù)端統(tǒng)一使用字符串,即公私鑰在客戶端是以字符串的形式傳遞的。

其次,本地存儲(chǔ)的公鑰1、公鑰2、私鑰2、AES 密鑰、sessionId 和 token,建議統(tǒng)一放到 keychain 中去,防止泄露。

密鑰生成

利用 OpenSSL 在客戶端生成 RSA 密鑰對(duì)時(shí),并沒找到方法將密鑰(SecKeyRef)轉(zhuǎn)成字符串(若您找到了,請(qǐng)告知于我,不勝感激),所以在這用了一個(gè)取巧的方法:將公私鑰保存為本地文件,然后再從這個(gè)文件中讀取出來。

#pragma mark - 本地生成 RSA 公私鑰對(duì)
+ (NSArray *)generateKeys {
    RSA *rsa = NULL;
    rsa = RSA_new();
    rsa = RSA_generate_key(1024,0x10001,NULL,NULL);
    // 路徑
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)objectAtIndex:0];
    //獲取公鑰字符串
    NSString *pubPath = [documentsPath stringByAppendingPathComponent:@"PubFile.txt"];
    FILE *pubWrite = NULL;
    pubWrite = fopen([pubPath UTF8String],"wb");
    if(pubWrite == NULL) {
        NSLog(@"Read Filed.");
    }else{
        PEM_write_RSA_PUBKEY(pubWrite,rsa);
        fclose(pubWrite);
    }
    NSString *publicStr = [NSString stringWithContentsOfFile:pubPath encoding:NSUTF8StringEncoding error:nil];
    publicStr = [publicStr stringByReplacingOccurrencesOfString:@"-----BEGIN PUBLIC KEY-----"withString:@""];
    publicStr = [publicStr stringByReplacingOccurrencesOfString:@"-----END PUBLIC KEY-----"withString:@""];
    publicStr = [publicStr stringByReplacingOccurrencesOfString:@"\n"withString:@""];
    //獲取私鑰字符串
    NSString *priPath = [documentsPath stringByAppendingPathComponent:@"PriFile.txt"];
    FILE *priWtire = NULL;
    priWtire = fopen([priPath UTF8String],"wb");
    EVP_PKEY *pkey = NULL;
    if(priWtire == NULL) {
        NSLog(@"Read Filed.");
    }else{
        pkey =EVP_PKEY_new();
        EVP_PKEY_assign_RSA(pkey, rsa);
        PEM_write_PKCS8PrivateKey(priWtire, pkey,NULL,NULL,0,0,NULL);
        fclose(priWtire);
    }
    NSString *privateStr = [NSString stringWithContentsOfFile:priPath encoding:NSUTF8StringEncoding error:nil];
    privateStr = [privateStr stringByReplacingOccurrencesOfString:@"-----BEGIN PRIVATE KEY-----"withString:@""];
    privateStr = [privateStr stringByReplacingOccurrencesOfString:@"-----END PRIVATE KEY-----"withString:@""];
    privateStr = [privateStr stringByReplacingOccurrencesOfString:@"\n"withString:@""];
    
    return @[publicStr,privateStr];
}

最后

以上只是較常用三種安全強(qiáng)度的簽名設(shè)計(jì),真正 App 簽名設(shè)計(jì)遠(yuǎn)遠(yuǎn)不止這三種,比這些安全強(qiáng)度高的也有許多,繼續(xù)學(xué)習(xí)。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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