iOS RSA 加密算法,非对称加密,适用小数据

2020-11-27  本文已影响0人  移动的键盘

/// 支持的RSA keySize 大小有:512,768,1024,2048位
/// 支持的RSA 填充方式有三种:NOPadding,PKCS1,OAEP 三种方式 ,填充方式影响最大分组加密数据块的大小
/// 签名使用的填充方式PKCS1, 支持的签名算法有 sha1,sha256,sha224,sha384,sha512
/// Nopadding填充最大数据块为 接口 SecKeyGetBlockSize 大小;
/// PKCS1 填充方式最大数据为 SecKeyGetBlockSize大小 减去11
/// OAEP 填充方式最大数据为 SecKeyGetBlockSize 大小减去 42
/// RSA加密解密签名,适合小块的数据处理,大量数量需要处理分组逻辑;密码学中推荐使用对称加密进行数据加密,使用RSA来加密对称密钥

生成密钥对

/**
#!/usr/bin/env bash
echo "Generating RSA key pair ..."
echo "1024 RSA key: private_key.pem"
openssl genrsa -out private_key.pem 1024

echo "create certification require file: rsaCertReq.csr"
openssl req -new -key private_key.pem -out rsaCertReq.csr

echo "create certification using x509: rsaCert.crt"
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt

echo "create public_key.der For IOS"
openssl x509 -outform der -in rsaCert.crt -out public_key.der

echo "create private_key.p12 For IOS. Please remember your password. The password will be used in iOS."
openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt

echo "create rsa_public_key.pem For Java"
openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout
echo "create pkcs8_private_key.pem For Java"
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt

echo "finished."
********************************************************************************/

加密算法

import UIKit
import Security

public class ShgRSASwift: NSObject {

/// RSA 加密,公钥加密
/// - Parameters:
///   - aString: 需要加密的字符串
///   - publicKeyPath: 公钥文件路径
/// - Returns: 加密后的字符串
@objc public class func encrypt(_ aString:String,_ publicKeyPath:String) -> String? {
    
    if aString.count == 0 || publicKeyPath.count == 0 {
        return nil
    }
    
    guard let secKey:SecKey = self.publicSecKey(publicKeyPath) else {
        return nil
    }
    
    guard let data:Data = aString.data(using: .utf8) else {
        return nil
    }
    
    //输入数据bytes
    let dataByte = [UInt8](data)
    
    //输入数据长度
    let dataLenght = data.count

    //公钥块长度
    let secKeyBlockSize = SecKeyGetBlockSize(secKey)
    
    //输出buffer
    let output = UnsafeMutablePointer<UInt8>.allocate(capacity: secKeyBlockSize)
    
    //说明:如果填充模式为 PKCS1 ,可加密数据块最大长度为 SecKeyGetBlockSize() - 11
    let maxInputLenght = secKeyBlockSize - 11

    var status:OSStatus = noErr
    
    var outLength:Int = secKeyBlockSize
    
    //RSA加密块数据长度不能大于密钥块长度,当数据大于密钥块长度时,进行分组加密
    if dataLenght > maxInputLenght {
        
        let index:Int = Int(ceilf(Float(dataLenght)/Float(maxInputLenght)))
        
        var rsaStr:String = ""
        
        for i:Int in 0 ..< index {
            
            let sufLength = dataLenght - i * maxInputLenght
            
            guard let range:Range = Range.init(NSMakeRange(i * maxInputLenght, i == index - 1 ? sufLength : maxInputLenght)) else {
                return nil
            }
            
            let subData:Data = data.subdata(in: range)
            
            let subByte = [UInt8](subData)
            
            status = SecKeyEncrypt(secKey, SecPadding.PKCS1, subByte, maxInputLenght, output, &outLength)
            
            if status != noErr {
                return nil
            }
            
            for i:Int in 0 ..< outLength {
                rsaStr = rsaStr.appendingFormat("%02x", _:output[i])
            }
        }
        
        output.deallocate()
        
        return rsaStr
        
    } else {
        
        status = SecKeyEncrypt(secKey, SecPadding.PKCS1, dataByte, dataLenght, output, &outLength)
        
        if status != noErr {
            return nil
        }
        
        var rsaStr:String = ""
        
        for i:Int in 0 ..< outLength {
            rsaStr = rsaStr.appendingFormat("%02x", _:output[i])
        }
        
        output.deallocate()
        
        return rsaStr
    }
}

/// 获取公钥 .der证书
/// - Parameter publicKeyPath: 公钥文件路径
/// - Returns: 公钥
class func publicSecKey(_ publicKeyPath:String) -> SecKey? {
    
    let url:URL = URL.init(fileURLWithPath: publicKeyPath)
    
    var data:Data
    
    do {
        try data = Data.init(contentsOf: url)
    } catch  {
        return nil
    }
    
    let dataByte = [UInt8](data)
    
    let cfData:CFData = CFDataCreate(kCFAllocatorDefault, dataByte, CFIndex(dataByte.count))
    
    guard let cert:SecCertificate = SecCertificateCreateWithData(nil, cfData) else {
        return nil
    }
    
    var trust:SecTrust?
    
    let policy:SecPolicy = SecPolicyCreateBasicX509()
  
    let status:OSStatus = SecTrustCreateWithCertificates(cert, policy, &trust)
    
    if status != noErr {
        return nil
    }
    
    var result:SecTrustResultType = .invalid
    
    if SecTrustEvaluate(trust!, &result) != noErr {
        return nil
    }
    
    guard let secKey:SecKey = SecTrustCopyPublicKey(trust!) else {
        return nil
    }
    
    return secKey
}
}

/// RSA 解密,私钥解密
/// - Parameters:
///   - data: 需要解密的data
///   - privateKeyPath: 私钥文件路径
/// - Returns: 解密后的字符串
@objc public class func decrypt(_ data:Data,_ privateKeyPath:String) -> String? {
    
    if data.count == 0 || privateKeyPath.count == 0 {
        return nil
    }
    
    guard let secKey:SecKey = self.privateSecKey(privateKeyPath) else {
        return nil
    }
    
    //输入数据bytes
    let dataByte = [UInt8](data)
    
    //输入数据长度
    let dataLenght = data.count

    //私钥块长度
    let secKeyBlockSize = SecKeyGetBlockSize(secKey)
    
    //输出buffer
    let output = UnsafeMutablePointer<UInt8>.allocate(capacity: secKeyBlockSize)
    
    let maxInputLenght = secKeyBlockSize

    var status:OSStatus = noErr
    
    var outLength:Int = secKeyBlockSize
    
    //RSA解密块数据长度不能大于密钥块长度,当数据大于密钥块长度时,进行分组解密
    if dataLenght > maxInputLenght {
        
        let index:Int = Int(ceilf(Float(dataLenght)/Float(maxInputLenght)))
        
        var outData:Data = Data()
     
        for i:Int in 0 ..< index {
            
            let sufLength = dataLenght - i * maxInputLenght
            
            guard let range:Range = Range.init(NSMakeRange(i * maxInputLenght, i == index - 1 ? sufLength : maxInputLenght)) else {
                return nil
            }
            
            let subData:Data = data.subdata(in: range)
            
            let subByte = [UInt8](subData)
            
            status = SecKeyDecrypt(secKey, SecPadding.init(rawValue: 0), subByte, subData.count, output, &outLength)
            
            if status != noErr {
                return nil
            }
            
            var idxFirstZero:Int = -1
            
            var idxNextZero:Int = outLength
            
            for i:Int in 0 ..< outLength {
                if output[i] == 0 {
                    if idxFirstZero < 0 {
                        idxFirstZero = i
                    } else {
                        idxNextZero = i
                        break
                    }
                }
            }
            
            outData.append(&output[idxFirstZero + 1], count: idxNextZero - idxFirstZero - 1)
            
        }
        
        output.deallocate()
        
        let rsaStr:String? = String.init(data: outData, encoding: .utf8)
        
        return rsaStr
        
    } else {
        
        status = SecKeyDecrypt(secKey, SecPadding.PKCS1, dataByte, dataLenght, output, &outLength)
        
        if status != noErr {
            return nil
        }
        
        var idxFirstZero:Int = -1
        
        var idxNextZero:Int = outLength
        
        for i:Int in 0 ..< outLength {
            if output[i] == 0 {
                if idxFirstZero < 0 {
                    idxFirstZero = i
                } else {
                    idxNextZero = i
                    break
                }
            }
        }
        
        var outData:Data = Data()
        
        outData.append(&output[idxFirstZero + 1], count: idxNextZero - idxFirstZero - 1)
        
        output.deallocate()
        
        let rsaStr:String? = String.init(data: outData, encoding: .utf8)
        
        return rsaStr
    }
}

/// 获取私钥 .p12证书
/// - Parameter privateKeyPath: 私钥文件路径
/// - Returns: 私钥
class func privateSecKey(_ privateKeyPath:String) -> SecKey? {
    
    let url:URL = URL.init(fileURLWithPath: privateKeyPath)
    
    var data:CFData
    
    do {
        try data = Data.init(contentsOf: url) as CFData
    } catch  {
        return nil
    }
    
    let options:CFMutableDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, CFIndex(0), nil, nil)
    
    // 对象转 UnsafeRawPointer
    let key = Unmanaged.passRetained(kSecImportExportPassphrase as NSString).autorelease().toOpaque()
    
    let value = Unmanaged.passRetained("1" as NSString).autorelease().toOpaque()
    
    CFDictionaryAddValue(options, key, value)
    
    var items:CFArray? = CFArrayCreate(nil, UnsafeMutablePointer<UnsafeRawPointer?>(bitPattern: 0), 0, nil)
    
    let securityError:OSStatus = SecPKCS12Import(data, options, &items)
    
    if securityError == noErr && CFArrayGetCount(items) > 0 {
        
        let identityDict = CFArrayGetValueAtIndex(items, 0)
        
        // UnsafeRawPointer 转 任意类型
        let cfDic:CFDictionary = Unmanaged.fromOpaque(identityDict!).takeUnretainedValue()
        
        if CFDictionaryGetCount(cfDic) == 0 {
            return nil
        }
        
        let identityKey = Unmanaged.passRetained(kSecImportItemIdentity as NSString).autorelease().toOpaque()
        
        let identityPriv = CFDictionaryGetValue(cfDic, identityKey)
        
        let identity:SecIdentity = Unmanaged.fromOpaque(identityPriv!).takeUnretainedValue()
        
        var secKey:SecKey?
        
        let status = SecIdentityCopyPrivateKey(identity, &secKey)
        
        if status == noErr {
            return secKey
        }
    }
    
    return nil
}
上一篇 下一篇

猜你喜欢

热点阅读