import Foundation
import Security
enum RSAKeySize: Int {
case size512 = 512
case size768 = 768
case size1024 = 1024
case size2048 = 2048
}
private let publicKeyIdentifier = "baseModule.publicKey"
private let privateKeyIdentifier = "baseModule.privateKey"
private let publicKeyTag = publicKeyIdentifier.data(using: .utf8)!
private let privateKeyTag = privateKeyIdentifier.data(using: .utf8)!
class RSAUtils {
static let shared = RSAUtils()
var publicSecKey: SecKey?
var privateSecKey: SecKey?
private let keySizeType: RSAKeySize = .size1024
// RSA秘鍵を生成する
// sizes for RSA keys are: 512, 768, 1024, 2048.
func generateRSAKeyPair() {
let keySize = keySizeType.rawValue
publicSecKey = getRSAKeyFromKeychain(isPrivate: false, keySize: keySize)
privateSecKey = getRSAKeyFromKeychain(isPrivate: true, keySize: keySize)
if publicSecKey != nil {
return
}
publicSecKey = nil
privateSecKey = nil
let parameters = [kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: keySize] as [CFString : Any]
let ret = SecKeyGeneratePair(parameters as CFDictionary, &publicSecKey, &privateSecKey)
assert(ret == errSecSuccess, "Error For SecKeyGeneratePair: \(ret)")
saveRSAKeyToKeychain(key: publicSecKey!, keySize: keySize, isPrivate: false)
saveRSAKeyToKeychain(key: privateSecKey!, keySize: keySize, isPrivate: true)
}
@objc public func encrypt(source: String) -> String {
guard !source.isEmpty && self.publicSecKey != nil else {
return ""
}
let data: NSData = (source.data(using: String.Encoding.utf8)! as NSData)
var error: Unmanaged<CFError>?
let resData = SecKeyCreateEncryptedData(self.publicSecKey!, SecKeyAlgorithm.rsaEncryptionPKCS1, data as CFData, &error) as Data?
if error != nil {
DDLogError("res = \(String(describing: error?.takeUnretainedValue().localizedDescription))")
return ""
}
return resData!.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: NSData.Base64EncodingOptions.lineLength64Characters.rawValue))
}
@objc public func decrypt(source: String) -> String {
guard !source.isEmpty && self.privateSecKey != nil else {
return ""
}
//base64 decode
guard let data: Data = NSData(base64Encoded: (source as String), options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) as Data? else {
return ""
}
var error: Unmanaged<CFError>?
let resData = SecKeyCreateDecryptedData(self.privateSecKey!, SecKeyAlgorithm.rsaEncryptionPKCS1, data as CFData, &error) as Data?
if error != nil {
DDLogError("res = \(String(describing: error?.takeUnretainedValue().localizedDescription))")
return ""
}
return String(data: resData!, encoding: String.Encoding.utf8)!
}
private func getKeyDataFrom(secKey: SecKey, tag: Data) -> Data {
var data: Data?
var query = [String: Any]()
query[kSecClass as String] = kSecClassKey
query[kSecAttrApplicationTag as String] = tag
query[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA
var attributes = query
attributes[kSecValueRef as String] = secKey
attributes[kSecReturnData as String] = true
var result: CFTypeRef?
let status = SecItemAdd(attributes as CFDictionary, &result)
if status == errSecSuccess {
data = result as? Data
SecItemDelete(query as CFDictionary)
}
return data!
}
// keySize RSAキーの長さです
// isPrivate 保管されている秘密鍵かどうかを判別します(trueは秘密鍵、falseは公開鍵)
private func saveRSAKeyToKeychain(key: SecKey, keySize: size_t, isPrivate: Bool) {
var saveDictionary = [String: Any]()
let keyClass = isPrivate ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic
// keychain辞書を設定する
saveDictionary[kSecClass as String] = kSecClassKey
saveDictionary[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA
saveDictionary[kSecAttrApplicationTag as String] = isPrivate ? privateKeyTag : publicKeyTag
saveDictionary[kSecAttrKeyClass as String] = keyClass
saveDictionary[kSecValueData as String] = getKeyDataFrom(secKey: key, tag: isPrivate ? privateKeyTag : publicKeyTag)
saveDictionary[kSecAttrKeySizeInBits as String] = keySize
saveDictionary[kSecAttrEffectiveKeySize as String] = SecKeyGetBlockSize(key)
saveDictionary[kSecAttrCanDerive as String] = kCFBooleanFalse
saveDictionary[kSecAttrCanEncrypt as String] = kCFBooleanTrue
saveDictionary[kSecAttrCanDecrypt as String] = kCFBooleanTrue
saveDictionary[kSecAttrCanVerify as String] = kCFBooleanTrue
saveDictionary[kSecAttrCanSign as String] = kCFBooleanFalse
saveDictionary[kSecAttrCanWrap as String] = kCFBooleanTrue
saveDictionary[kSecAttrCanUnwrap as String] = kCFBooleanFalse
saveDictionary[kSecAttrApplicationLabel as String] = isPrivate ? privateKeyIdentifier : publicKeyIdentifier
// 古いデータを削除する
SecItemDelete(saveDictionary as CFDictionary)
let status = SecItemAdd(saveDictionary as CFDictionary, nil)
assert(status == errSecSuccess, "SaveRSAKeyToKeychain Failure")
}
// isPrivate 保管されている秘密鍵かどうかを判別します(trueは秘密鍵、falseは公開鍵)
// swiftlint:disable force_cast
private func getRSAKeyFromKeychain(isPrivate: Bool, keySize: size_t) -> SecKey? {
var queryDictionary = [String: Any]()
let keyClass = isPrivate ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic
queryDictionary[kSecClass as String] = kSecClassKey
queryDictionary[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA
queryDictionary[kSecAttrApplicationTag as String] = isPrivate ? privateKeyTag : publicKeyTag
queryDictionary[kSecAttrKeyClass as String] = keyClass
queryDictionary[kSecReturnRef as String] = kCFBooleanTrue
queryDictionary[kSecAttrApplicationLabel as String] = isPrivate ? privateKeyIdentifier : publicKeyIdentifier
queryDictionary[kSecAttrKeySizeInBits as String] = keySize
var key: CFTypeRef?
let status = SecItemCopyMatching(queryDictionary as CFDictionary, &key)
if status == errSecSuccess {
let result = key as! SecKey
return result
}
return nil
}
// swiftlint:enable force_cast
}
网友评论