RSA密鑰、加密和數字簽名

RSA密鑰

由上一篇文章咱們能夠知道,公鑰是(e,n)、私鑰是(d,n)。而在實際應用中,咱們接觸到到的不是e、d、n,而是特定格式的數據或者文件。html

PKCS

PKCS 全稱是 Public-Key Cryptography Standards(公鑰加密標準),是由 RSA 實驗室與其它安全系統開發商爲促進公鑰密碼的發展而制訂的一系列標準,PKCS 目前共發佈過 15 個標準。其中比較經常使用的有:git

標準 名稱 格式 簡介
PKCS#1 RSA密碼編譯標準 / 定義了RSA的數理基礎、公/私鑰格式,以及加/解密、籤/驗章的流程。
PKCS #7 密碼消息語法標準 / 參見RFC 2315。規範了以公開密鑰基礎設施(PKI)所產生之簽名/密文之格式。其目的同樣是爲了拓展數字證書的應用。
PKCS#8 私鑰消息表示標準 .p8 Apache讀取證書私鑰的標準。
PKCS#10 證書申請標準 .p10 .csr 參見RFC 2986。規範了向證書中心申請證書之CSR(certificate signing request)的格式。
PKCS#12 我的消息交換標準 .p12 .pfx 定義了包含私鑰與公鑰證書(public key certificate)的文件格式。私鑰採密碼(password)保護。

其中.csr或.certSigningRequest是證書請求格式,拿着這個請求文件向CA獲取簽名過的證書。譬如咱們在配置開發證書時候,先經過鑰匙串生成.csr文件,而後上傳,蘋果根據.csr文件爲咱們生成開發證書。github

pfx,p12文件是二進制格式,同時含私鑰和證書,一般有保護密碼。在鑰匙串中因此能夠展開的證書均可以導出p12。算法

X.509

X.509是常見通用的證書格式。全部的證書都符合爲Public Key Infrastructure (PKI) 制定的 ITU-T X509 國際標準。數組

格式 編碼形式
.der ASCII
.pem Base64
.cer 二進制
.crt 二進制
  • .cer/.crt是用於存放證書,它是2進制形式存放的,不含私鑰。
  • pem文件通常是文本格式的,能夠放證書或者私鑰,或者二者都有
  • pem若是隻含私鑰的話,通常用.key擴展名,並且能夠有密碼保護

ASN.1格式

ASN.1格式在RSA密鑰證書中,有舉足輕重的地位。上面咱們提到的因此證書格式p十二、pfx、cer,都是ASN.1格式的。將pem中base64串編碼,獲得的公司鑰實體數據也是ASN.1格式的。安全

在電信和計算機網絡領域,ASN.1(Abstract Syntax Notation One) 是一套標準,是描述數據的表示、編碼、傳輸、解碼的靈活的記法。它提供了一套正式、無歧義和精確的規則以描述獨立於特定計算機硬件的對象結構。bash

關於它的語法數據類型等詳細介紹,請參看這篇文章網絡

咱們來看ASN.1的基本編碼規則。ASN.1編碼的數據大體分爲三個部分,標籤(tag)字段+長度(Length)字段+值(Value)字段數據結構

標籤(tag)字段:關於標籤類別和編碼格式的信息。app

長度(Length)字段:定義內容字段的長度(字節數)。

值(Value)字段:包含實際的數據 。

標籤字段(標頭)表示了不一樣的值的數據類型。常見的標頭有

標籤字段 數據類型 示例
0x01 布爾值 true表示爲 0x01 01 FF;false表示爲 0x01 01 00
0x02 整型 16位整形數的9表示爲 0x02 02 0009
0x03 位串(bit string)
0x04 八位串(octor string)
0x05 空值 nil 表示爲 0x05 00
0x13(19) 可打印的ASCII編碼字符串
0x16(22) ASCII編碼字符串
0x31 數組 [3,5]表示爲 0x31 06 0x02 01 03 0x02 01 05

RSA密鑰的結構

下面咱們來看看,公私鑰匙到底長什麼樣子。n、e、d都是怎樣存放的。

爲了研究方便,咱們先用openssl生成一個1024位的RSA私鑰

openssl genrsa -out private-key-1024.pem 1024
複製代碼

導出公鑰

openssl rsa -in private-key-1024.pem -pubout -out public-key-1024.pem
複製代碼

公鑰數據的結構

pem格式包含的是base64編碼的數據。咱們取出其中字符串。而後取出首尾標識符及回車符,base64反編碼獲得ASN.1格式的二進制數據。

公鑰的ASN.1結構爲

RSAPublicKey :: = SEQUENCE{
	 modulus         INTEGER  n (模長,正整數)
	 publicExponent  INTEGER  e (公鑰指數)
}
複製代碼

咱們取出公鑰字符串,而後base64解碼,獲得34字節數據。他的大體結構以下

0x30 --標頭,0x30表示序列類型
0x81 --內容較長,將用後面1(0x80 - 0b10000000)個字節標識長度
0x9f --包含159個字節長度的內容
   
   0x30  --標頭,0x30表示序列類型
   0x0d  --數據長度,後面包含13個字節數據
   
	   0x06 --標頭,6表示對象標識符
	   0x09 --9個字節
	   // oid值 1.2.840.113549.1.1.1 (rsaEncryption) 
	   0x2a 0x86 0x48 0x86 0xf7 0x0d 0x01 0x01 0x01 
	   
	   0x05 0x00 -- null
 
   0x03 --標頭,03表示bitstring位串
   0x81 
   0x8d --141字節長度
   
   
       0x00  --bitstring開頭
       0x30  --標頭,0x30表示
       0x81  
       0x89  --137個字節長度
    
           0x02  --模長n的標頭,2表示整數
           0x81  
           0x81  --129字節
           
           // 模長n的值,129字節存儲,128個有效字節
           0x00 
           0xa0 0x29 0xbf 0xd0 0x38 0xfc 0xeb 0xbb 
           0xba 0xa9 0x09 0x90 0x7c 0x34 0xeb 0x9b 
           0xd8 0x61 0x73 0x11 0xd1 0x28 0x49 0x39 
           0xb8 0x43 0xe1 0xc2 0x1e 0xa2 0x87 0x20 
           0x19 0x5c 0xf1 0x50 0x88 0xb2 0x63 0xc0 
           0xd5 0x2b 0x68 0x88 0x52 0x75 0xcd 0xd8 
           0x26 0xba 0xb4 0x30 0x69 0xe0 0xa4 0xe9 
           0xe0 0x3d 0xcf 0xbf 0x67 0xa7 0x98 0xb1 
           0xbe 0x20 0x41 0x73 0x5b 0xe6 0xf0 0x7a 
           0x92 0x41 0x1b 0x62 0x57 0x47 0x60 0x25 
           0xbe 0x3b 0x75 0xed 0x46 0x0e 0x61 0x52 
           0x03 0xa5 0x00 0x59 0x1c 0x3c 0x94 0xd2 
           0x94 0x16 0xbc 0x08 0x6d 0x4f 0xba 0x86 
           0xc6 0xfc 0xd2 0x3c 0x79 0xc4 0x99 0x17 
           0xaf 0xf9 0x5c 0x99 0x50 0xe7 0x28 0x2a 
           0x42 0xd9 0xc7 0x2e 0xba 0x17 0x9c 0x23  
 
           0x02            -- e的標頭
           0x03            -- e長度爲3個字節
           0x01 0x00 0x01  -- 公鑰指數e
複製代碼

公鑰的pem數據中,主要包含兩部份內容,第一部分是OID值,第二部分是公鑰數據實體。

OID值(Object Identifier 對象標識符)爲1.2.840.113549.1.1.1,它表示PKCS1公鑰加密標識符。

其中,各個數字按順序表示爲

  • 1 --ISO標準
  • 2 --member-body(成員主體)
  • 840 --US 美國
  • 113549 --RSADSI
  • 1 --PKCS
  • 1 --PKCS的第一個標準,即PKCS#1
  • 1 --rsaEncryption RSA加密

你能夠在這裏這裏查看他的詳細介紹

值得注意的是:

  • 當ASN.1的長度字段較大時,會以多個字節表示長度。在上述數據中,長度字段0x81&0b10000000=1,因此0x81不表示長度,而是其後0x81-0b10000000=1個字節表示長度
  • 當整數類型最高位爲1時,會在最前面補0x00。上述數據中,1024位模長對應128字節的n,而前面的0x00不包含在n當中
  • e的值0x010001(65537),是一個固定值,不管用何種方式生成,模長是多少位的,e的值都是同樣的。這是加密性能和安全的兼顧。同時因爲e同樣,因此模長的變化並不會增長加密的時間複雜度。
  • 實際有效的公鑰數據,是最後bitstring部分的值

私鑰長什麼樣

私鑰的ASN.1結構爲

RSAPrivateKey :: = SEQUENCE{
     version            Version,

     modulus            INTEGER,   ------ n
     publicExponent     INTEGER,   ------ e
     privateExponent    INTEGER,   ------ d
     prime1             INTEGER,   ------ p
     prime2             INTEGER,   ------ q
     exponent1          INTEGER,   ------ d mod (p -1)
     exponent2          INTEGER,   ------ d mod (q -1)
     coefficient        INTEGER,   ------- (inverse of q) mod p
     otherPrimeInfos    OtherPrimeInfos   ------ OPTIONAL(當version爲0時,不存在;當 version爲1時,必須有)
 }

 Version :: = INTEGER{ two-prime(0), multi(1)}
複製代碼

值得注意的是私鑰文件裏邊,不可是包含實際有效私鑰(e,n),他還包含公鑰指數,咱們在密鑰生成中用到的p、q,以及其餘一些信息。這也是咱們能夠經過私鑰導出公鑰的緣由。

咱們將上面的到的私鑰字符串base64反編碼以後,的到的數據結構以下:

0x30 --標頭,序列類型
0x82 --後面2個字節表示長度
0x02 0x5c --數據長度45

    0x02
    0x01
    0x00  --版本號version爲0

    0x02
    0x81
    0x81 --129個字節
    // 模數n
    0x00 
    0xa0 0x29 0xbf 0xd0 0x38 0xfc 0xeb 0xbb 
    0xba 0xa9 0x09 0x90 0x7c 0x34 0xeb 0x9b 
    0xd8 0x61 0x73 0x11 0xd1 0x28 0x49 0x39 
    0xb8 0x43 0xe1 0xc2 0x1e 0xa2 0x87 0x20 
    0x19 0x5c 0xf1 0x50 0x88 0xb2 0x63 0xc0 
    0xd5 0x2b 0x68 0x88 0x52 0x75 0xcd 0xd8 
    0x26 0xba 0xb4 0x30 0x69 0xe0 0xa4 0xe9 
    0xe0 0x3d 0xcf 0xbf 0x67 0xa7 0x98 0xb1 
    0xbe 0x20 0x41 0x73 0x5b 0xe6 0xf0 0x7a 
    0x92 0x41 0x1b 0x62 0x57 0x47 0x60 0x25 
    0xbe 0x3b 0x75 0xed 0x46 0x0e 0x61 0x52 
    0x03 0xa5 0x00 0x59 0x1c 0x3c 0x94 0xd2 
    0x94 0x16 0xbc 0x08 0x6d 0x4f 0xba 0x86 
    0xc6 0xfc 0xd2 0x3c 0x79 0xc4 0x99 0x17 
    0xaf 0xf9 0x5c 0x99 0x50 0xe7 0x28 0x2a 
    0x42 0xd9 0xc7 0x2e 0xba 0x17 0x9c 0x23 
    
    
    0x02
    0x03
    0x01 0x00 0x01   --公鑰指數e
 
    0x02
    0x81
    0x80 --128個字節
    // 私鑰質數d
	0x79 0x69 0xcc 0xb7 0xbb 0x4b 0xb8 0x24 
	0x32 0xc7 0x4b 0xb1 0xd5 0x06 0x85 0x09 
	0x3a 0x49 0xfd 0x62 0x27 0x4d 0x43 0xdd 
	0x56 0x9b 0x56 0xfb 0xc2 0x1f 0x71 0x11 
	0xdb 0x48 0x42 0xc2 0xcb 0x2d 0x78 0x43 
	0x49 0x15 0xc4 0x03 0x7b 0x87 0x44 0x49 
	0x34 0x6a 0xda 0x87 0xcc 0xeb 0x77 0xf8 
	0xb7 0x7e 0x04 0x0b 0xd4 0x37 0x0f 0x9f 
	0x92 0xd6 0x31 0xd7 0x4f 0x90 0xa0 0x8e 
	0x07 0x1a 0xf7 0x0d 0x79 0x25 0xf6 0x1a 
	0x0a 0x83 0x6b 0x00 0x33 0xbd 0x32 0x2c 
	0xb3 0xdd 0x71 0x64 0xb5 0xf8 0xcc 0x9f 
	0x21 0xc3 0x81 0xad 0xab 0xb0 0x1f 0x92 
	0x0b 0xed 0x88 0x76 0x6c 0x95 0xc6 0xe2 
	0xe7 0x28 0x24 0xca 0xa0 0x85 0xc7 0x69 
	0xc2 0x56 0xa2 0x4d 0x70 0x4b 0x59 0xe9 
	
	
    0x02 
    0x41 --65字節
    // 質數p值,有效64字節
    0x00
    0xd5 0x5f 0x27 0xc6 0x84 0xf4 0x37 0xda 
    0xa8 0x10 0x28 0x0f 0x33 0x8f 0x05 0xe7 
    0xa8 0xd3 0x09 0x7f 0xca 0x71 0xfe 0x86 
    0xa0 0x95 0xb3 0x21 0x30 0xb8 0xb4 0xcf 
    0x27 0x89 0x21 0xea 0x6d 0xcd 0xaf 0x34 
    0x2f 0x6d 0x3b 0x64 0xd6 0x41 0x85 0x74 
    0x10 0xd1 0x63 0x29 0xaa 0xf2 0x79 0xc0 
    0x4b 0xed 0x2c 0xf9 0x7b 0x7c 0x43 0x0f

    0x02
    0x41
    // 質數q值,有效64字節
    0x00 
    0xc0 0x29 0x40 0x7a 0x96 0x32 0x89 0xf7 
    0x97 0xbd 0x76 0xa3 0x6c 0xea 0x1b 0x7d 
    0xa4 0x23 0xe3 0x3d 0x4e 0x08 0x1a 0x21 
    0x10 0x48 0x81 0xed 0x29 0x01 0xc5 0xae 
    0xba 0xb9 0x5f 0x98 0x55 0xf4 0x24 0x9c 
    0xb0 0x14 0x97 0xde 0x34 0x07 0x4d 0x5e 
    0x53 0x5b 0x6b 0xc2 0x4d 0xcd 0xaf 0x46 
    0xde 0x9d 0xb8 0x06 0xfd 0x41 0x05 0xad
 
    ......
    
複製代碼

值得注意的是,p和q都是模長的一半,64字節,512位。私鑰質數d,長度和模長一致,都是128字節,1024位。因爲私鑰指數d很大,因此解密時耗費的計算力是比較大的。

公私鑰的導入

在加密或簽名以前,咱們須要將上面所說的密鑰文件轉化爲咱們的密鑰對象。咱們一般採用系統的Security框架進行加密,與之對應的。咱們須要讀取密鑰文件並生成SecKey

pem文件的導入

pem是咱們最爲常見的存儲RSA密鑰的文件格式。

導入pem密鑰時咱們須要取出pem中的開始結束標識,再進行base64解密獲得密鑰data。

而後經過data生成SecKey

let keyClass = type == .public ? kSecAttrKeyClassPublic : kSecAttrKeyClassPrivate
let sizeInBits = data.count * 8
let keyDict: [CFString: Any] = [
    kSecAttrKeyType: kSecAttrKeyTypeRSA,
    kSecAttrKeyClass: keyClass,
    kSecAttrKeySizeInBits: NSNumber(value: sizeInBits),
    kSecReturnPersistentRef: true
]
    
var error: Unmanaged<CFError>?
guard let key = SecKeyCreateWithData(data as CFData, keyDict as CFDictionary, &error) else {
    print(error?.takeRetainedValue() ?? "unkown error")
    return nil
}
複製代碼

公私鑰惟一區別是,KeyClass,公鑰時傳kSecAttrKeyClassPublic,而私鑰是kSecAttrKeyClassPrivate

p12/pfx文件導入

咱們從文件讀取p12文件,獲得數據data,而後再用data建立SecKey

var item = CFArrayCreate(nil, nil, 0,nil)
let options = pwd != nil ? [kSecImportExportPassphrase:pwd] : [:]
let status = SecPKCS12Import(data as CFData,options as CFDictionary,&item)
if status != noErr {
    return nil
}
    
guard  let itemArr = item as? [Any],
    let dict = itemArr.first as? [String:Any],
    let secIdentity = dict[kSecImportItemIdentity as String]   else{
    return nil
}
    
let secIdentityRef = secIdentity as! SecIdentity
var keyRef : SecKey?
SecIdentityCopyPrivateKey(secIdentityRef,&keyRef)
複製代碼

上述代碼中的keyRef就是咱們獲取到的私鑰對象。

由於私鑰中包含了公鑰的因此信息,咱們也能夠經過私鑰keyRef導出公鑰

let pubKey1 = SecKeyCopyPublicKey(keyRef)
複製代碼

但這樣作是毫無心義的,由於當咱們拿到p12/pfx時,就意味着咱們拿到的是私鑰。對於客戶端來講是要拿來最數據簽名的。若是要作數據加密,咱們拿到得將是指包含公鑰的pem文件。

RSA加密與解密

Padding

在進行RSA加密以前,咱們還須要理解一個重要的概念:padding

爲了提升RSA加密的安全性,加密以前每每會在明文前面加上一段包含隨機數的padding。加入padding以後的數據結構以下:

EM = 0x00 || 0x02 || PS || 0x00 || M.
複製代碼
  • PS(padding string),隨機數
  • M,明文

咱們知道RSA是分塊加密的,而若是有padding,每塊還必須減去一部分長度

padding 方式 模長(字節) 每段明文最大長度(字節) 每段密文長度
no padding n n n
PKCS1 n n-11 n
OAEP n n-42 n

加密的實現

先獲取到模長,根據padding計算分塊最大長度

// 模長
let blockSize = SecKeyGetBlockSize(key)

// 數據分塊的最大長度
var maxChunkSize : Int
switch padding {
case .PKCS1:
    maxChunkSize = blockSize - 11
case .OAEP:
    maxChunkSize = blockSize - 42
case []: // no padding
    maxChunkSize = blockSize
default: // default PKCS1
    maxChunkSize = blockSize - 11
}
複製代碼

對數據進行分塊加密

var retData = Data()
var idx = 0

while idx < data.count {
    let endIdx = min(idx+maxChunkSize,data.count)
    var chunkData = [UInt8](data[idx..<endIdx])
    var outLen    = blockSize;
    let outBuf    = UnsafeMutablePointer<UInt8>.allocate(capacity:outLen)
    defer { outBuf.deallocate() }
    
    var status = noErr;
    status = SecKeyEncrypt(key,
                           padding,
                           &chunkData,
                           chunkData.count,
                           outBuf,
                           &outLen)
    guard  status == noErr else {
        print("SecKeyEncrypt fail. Error Code: \(status)")
        return nil;
    }
    
    retData.append(UnsafeBufferPointer(start:outBuf, count:outLen))
    idx += maxChunkSize
}
複製代碼

其中,核心方法是SecKeyEncrypt。輸入公鑰key,padding類型,以及當前分塊數據chunkData,輸出outBuf

須要注意的是,須要同時知足如下三點,不然加密失敗。

  • key必須是公鑰;
  • chunkData長度必須與padding匹配不能過長;
  • outLen雖然是inout類型,但必須等於模長;

還有就是爲了傳輸方便,通常會將data轉化爲base64字符串

let ret = retData.base64EncodedString()
複製代碼

值得注意的是,因爲padding的存在,咱們對同一數據進行屢次加密,每次加密獲得的結果都是不同的。可是這並不會影響解密的結果,由於padding後的數據結構是固定的,成功解密以後會自動去除無效的數據。

解密的實現

咱們拿到的加密數據,通常是base64字符串。咱們須要先將其轉化爲data再base64解碼

let data = Data(base64Encoded:string, options:.ignoreUnknownCharacters)
複製代碼

跟加密相似的,解碼咱們用到SecKeyDecrypt方法,具體實現以下:

let blockSize = SecKeyGetBlockSize(key)
var retData = Data()
var idx = 0
while idx < data.count {
    let endIdx = min(idx+blockSize,data.count)
    var chunkData = [UInt8](data[idx..<endIdx])
    var outLen    = blockSize;
    let outBuf    = UnsafeMutablePointer<UInt8>.allocate(capacity:outLen)
    defer { outBuf.deallocate() }
    
    var status = noErr;
    status = SecKeyDecrypt(key,
                           padding,
                           &chunkData,
                           chunkData.count,
                           outBuf,
                           &outLen)
    guard  status == noErr else {
        print("SecKey decrypt fail. Error Code: \(status)")
        return nil;
    }
    
    let ret1 = UnsafeBufferPointer(start:outBuf, count:outLen)
    retData.append(ret1)
    idx += blockSize
}
複製代碼

值得注意的是:

  • key必須是私鑰
  • 解密也須要分塊進行,每塊長度都等於模長
  • outLen雖然是inout的值,解碼完以後會變成實際獲得的明文長度。但它的初始值不能小於明文長度,不然解密失敗。咱們取模長值是最穩妥的作法。

RSA簽名和認證

通常在作數字簽名是,每每不是直接用私鑰對明文進行簽名。而是將明文進行模中散列函數運算後,對消息摘要進行簽名。

在驗證的時候,若是用公鑰可以對簽名進行解密,說明發送者身份沒有被仿冒。而後對明文進行散列函數運算獲得的摘要與解密的摘要對比,若是一致證實消息和消息摘要在傳送過程當中都沒有被串改。

不一樣的散列函數,對應不一樣的padding值

散列函數 簽名算法 pading
MD5 MD5WithRSA PKCS1MD5
SHA1 SHA1WithRSA PKCS1SHA1
SHA224 SHA224WithRSA PKCS1SHA224
SHA256 SHA256WithRSA PKCS1SHA256
SHA384 SHA384WithRSA PKCS1SHA384
SHA512 SHA512WithRSA PKCS1SHA512

簽名的實現

先對原始數據進行散列函數運算

var digestData : Data
switch pading {
case .PKCS1MD5:
    digestData = DigestUtil.md5(data:data)
case .PKCS1SHA1:
    digestData = DigestUtil.sha1(data:data)
case .PKCS1SHA1:
    digestData = DigestUtil.sha1(data:data)
case .PKCS1SHA224:
    digestData = DigestUtil.sha224(data:data)
case .PKCS1SHA256:
    digestData = DigestUtil.sha256(data:data)
case .PKCS1SHA384:
    digestData = DigestUtil.sha384(data:data)
case .PKCS1SHA512:
    digestData = DigestUtil.sha512(data:data)
default:
    digestData = data
}
複製代碼

對消息摘要進行簽名

let blockSize = SecKeyGetBlockSize(key)
var maxChunkSize : Int = blockSize - 11
var retData = Data()
var idx = 0
while idx < digestData.count {
    let endIdx = min(idx+maxChunkSize,digestData.count)
    var chunkData = [UInt8](digestData[idx..<endIdx])
    var outLen = SecKeyGetBlockSize(key);
    let outBuf = UnsafeMutablePointer<UInt8>.allocate(capacity:outLen)
    defer { outBuf.deallocate() }
    var status = noErr;
    status = SecKeyRawSign(key,
                           pading,
                           &chunkData,
                           chunkData.count,
                           outBuf,
                           &outLen)
    if status == noErr {
        let ret1 = UnsafeBufferPointer(start:outBuf, count:outLen)
        retData.append(ret1)
    }else {
        print("SecKey sign fail. Error Code: \(status)")
        return nil;
    }
    idx += maxChunkSize
}
複製代碼

能夠看到它和加密的實現是很像的,並且還採用了和PKCS1相似的padding

standard ASN.1 padding will be done, as well as PKCS1 padding

能夠發現還有兩種SecPadding咱們沒有提到,sigRawPKCS1MD2sigRaw是DSA算法的,使用的不多。PKCS1MD2安全性很低,基本已沒人使用。

須要注意的是,因爲散列運算以後的結果都是一致的,而即使是長度最大的SHA512也只有64個字節,遠遠小於RSA簽名117字節的最大塊長度。因此咱們獲得的結果,都是128字節。而且屢次簽名獲得的結果都是一致的。

驗證的實現

驗證以前咱們先對原始數據進行與簽名相同散列運算,獲得摘要digestData

var digestData : Data
switch pading {
case .PKCS1MD5:
    digestData = DigestUtil.md5(data:data)
case .PKCS1SHA1:
    digestData = DigestUtil.sha1(data:data)
case .PKCS1SHA1:
    digestData = DigestUtil.sha1(data:data)
case .PKCS1SHA224:
    digestData = DigestUtil.sha224(data:data)
case .PKCS1SHA256:
    digestData = DigestUtil.sha256(data:data)
case .PKCS1SHA384:
    digestData = DigestUtil.sha384(data:data)
case .PKCS1SHA512:
    digestData = DigestUtil.sha512(data:data)
default:
    digestData = data
}
複製代碼

而後咱們輸入公鑰、padding、明文摘要、簽名,獲得驗證是否成功的結果。

var digestBuf = [UInt8](digestData)
let signBuf   = [UInt8](signData)
var status = noErr;
status = SecKeyRawVerify(key,
                         pading,
                         &digestBuf,
                         digestBuf.count,
                         signBuf,
                         signBuf.count)
                         
if status == errSecSuccess {
    return true
} else {
    return false
}
複製代碼

能夠看到,代碼中不存在循環語句。由於簽名數據、摘要數據都是固定長度,而且小於等於模長。因此沒有分段驗證的說法。

若是想看完整的實現,請看這裏

參考資料

相關文章
相關標籤/搜索