// // KMRSAUtils.swift // PDF Reader Pro // // Created by User-Tangchao on 2024/9/2. // import Cocoa class KMRSAUtils: NSObject { // Configuration keys struct Config { /// Determines whether to add key hash to the keychain path when searching for a key /// or when adding a key to keychain static var useKeyHashes = true } // Base64 encode a block of data static fileprivate func base64Encode(_ data: Data) -> String { return data.base64EncodedString(options: []) } // Base64 decode a base64-ed string static fileprivate func base64Decode(_ strBase64: String) -> Data { let data = Data(base64Encoded: strBase64, options: []) return data! } // Encrypts data with a RSA key static public func encryptWithRSAKey(_ data: Data, rsaKeyRef: SecKey, padding: SecPadding) -> Data? { let blockSize = SecKeyGetBlockSize(rsaKeyRef) let maxChunkSize = blockSize - 11 var decryptedDataAsArray = [UInt8](repeating: 0, count: data.count / MemoryLayout.size) (data as NSData).getBytes(&decryptedDataAsArray, length: data.count) var encryptedData = [UInt8](repeating: 0, count: 0) var idx = 0 while (idx < decryptedDataAsArray.count ) { var idxEnd = idx + maxChunkSize if ( idxEnd > decryptedDataAsArray.count ) { idxEnd = decryptedDataAsArray.count } var chunkData = [UInt8](repeating: 0, count: maxChunkSize) for i in idx..(encryptedData), count: encryptedData.count) } // Decrypt an encrypted data with a RSA key static public func decryptWithRSAKey(_ encryptedData: Data, rsaKeyRef: SecKey, padding: SecPadding) -> Data? { let blockSize = SecKeyGetBlockSize(rsaKeyRef) var encryptedDataAsArray = [UInt8](repeating: 0, count: encryptedData.count / MemoryLayout.size) (encryptedData as NSData).getBytes(&encryptedDataAsArray, length: encryptedData.count) var decryptedData = [UInt8](repeating: 0, count: 0) var idx = 0 while (idx < encryptedDataAsArray.count ) { var idxEnd = idx + blockSize if ( idxEnd > encryptedDataAsArray.count ) { idxEnd = encryptedDataAsArray.count } var chunkData = [UInt8](repeating: 0, count: blockSize) for i in idx..(decryptedData), count: decryptedData.count) } static fileprivate func removePadding(_ data: [UInt8]) -> [UInt8] { var idxFirstZero = -1 var idxNextZero = data.count for i in 0.. Data? { if ( pubkey.count == 0 ) { return nil } var keyAsArray = [UInt8](repeating: 0, count: pubkey.count / MemoryLayout.size) (pubkey as NSData).getBytes(&keyAsArray, length: pubkey.count) var idx = 0 if (keyAsArray[idx] != 0x30) { return nil } idx += 1 if (keyAsArray[idx] > 0x80) { idx += Int(keyAsArray[idx]) - 0x80 + 1 } else { idx += 1 } let seqiod = [UInt8](arrayLiteral: 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00) for i in idx.. 0x80) { idx += Int(keyAsArray[idx]) - 0x80 + 1; } else { idx += 1 } if (keyAsArray[idx] != 0x00) { return nil } idx += 1 //return pubkey.subdata(in: idx.. Data? { if ( privkey.count == 0 ) { return nil } var keyAsArray = [UInt8](repeating: 0, count: privkey.count / MemoryLayout.size) (privkey as NSData).getBytes(&keyAsArray, length: privkey.count) //magic byte at offset 22, check if it's actually ASN.1 var idx = 22 if ( keyAsArray[idx] != 0x04 ) { return nil } idx += 1 //now we need to find out how long the key is, so we can extract the correct hunk //of bytes from the buffer. var len = Int(keyAsArray[idx]) idx += 1 let det = len & 0x80 //check if the high bit set if (det == 0) { //no? then the length of the key is a number that fits in one byte, (< 128) len = len & 0x7f } else { //otherwise, the length of the key is a number that doesn't fit in one byte (> 127) var byteCount = Int(len & 0x7f) if (byteCount + idx > privkey.count) { return nil } //so we need to snip off byteCount bytes from the front, and reverse their order var accum: UInt = 0 var idx2 = idx idx += byteCount while (byteCount > 0) { //after each byte, we shove it over, accumulating the value into accum accum = (accum << 8) + UInt(keyAsArray[idx2]) idx2 += 1 byteCount -= 1 } // now we have read all the bytes of the key length, and converted them to a number, // which is the number of bytes in the actual key. we use this below to extract the // key bytes and operate on them len = Int(accum) } //return privkey.subdata(in: idx.. SecKey? { let queryFilter: [String: AnyObject] = [ String(kSecClass) : kSecClassKey, String(kSecAttrKeyType) : kSecAttrKeyTypeRSA, String(kSecAttrApplicationTag): tagName as AnyObject, //String(kSecAttrAccessible) : kSecAttrAccessibleWhenUnlocked, String(kSecReturnRef) : true as AnyObject ] var keyPtr: AnyObject? let result = SecItemCopyMatching(queryFilter as CFDictionary, &keyPtr) if ( result != noErr || keyPtr == nil ) { return nil } return keyPtr as! SecKey? } // Add a RSA private key to keychain and return its SecKeyRef // privkeyBase64: RSA private key in base64 (data between "-----BEGIN RSA PRIVATE KEY-----" and "-----END RSA PRIVATE KEY-----") static public func addRSAPrivateKey(_ privkeyBase64: String, tagName: String) -> SecKey? { return addRSAPrivateKey(privkey: base64Decode(privkeyBase64), tagName: tagName) } static fileprivate func addRSAPrivateKey(privkey: Data, tagName: String) -> SecKey? { // Delete any old lingering key with the same tag deleteRSAKeyFromKeychain(tagName) let privkeyData = stripPrivateKeyHeader(privkey) if ( privkeyData == nil ) { return nil } // Add persistent version of the key to system keychain // var prt: AnyObject? let queryFilter = [ // String(kSecClass) : kSecClassKey, // String(kSecAttrKeyType) : kSecAttrKeyTypeRSA, // String(kSecAttrApplicationTag) : tagName, //String(kSecAttrAccessible) : kSecAttrAccessibleWhenUnlocked, String(kSecValueData) : privkeyData!, String(kSecAttrKeyClass) : kSecAttrKeyClassPrivate, // String(kSecReturnPersistentRef): true ] as [String : Any] let result = SecItemAdd(queryFilter as CFDictionary, nil) if ((result != noErr) && (result != errSecDuplicateItem)) { return nil } return getRSAKeyFromKeychain(tagName) } // Add a RSA pubic key to keychain and return its SecKeyRef // pubkeyBase64: RSA public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----") static public func addRSAPublicKey(_ pubkeyBase64: String, tagName: String) -> SecKey? { return addRSAPublicKey(pubkey: base64Decode(pubkeyBase64), tagName: tagName) } static fileprivate func addRSAPublicKey(pubkey: Data, tagName: String) -> SecKey? { // Delete any old lingering key with the same tag deleteRSAKeyFromKeychain(tagName) let pubkeyData = stripPublicKeyHeader(pubkey) if ( pubkeyData == nil ) { return nil } // Add persistent version of the key to system keychain //var prt1: Unmanaged? let queryFilter = [ String(kSecClass) : kSecClassKey, String(kSecAttrKeyType) : kSecAttrKeyTypeRSA, String(kSecAttrApplicationTag) : tagName, String(kSecValueData) : pubkeyData!, String(kSecAttrKeyClass) : kSecAttrKeyClassPublic, String(kSecReturnPersistentRef): true ] as [String : Any] let result = SecItemAdd(queryFilter as CFDictionary, nil) if ((result != noErr) && (result != errSecDuplicateItem)) { return nil } return getRSAKeyFromKeychain(tagName) } // Encrypt data with a RSA private key // privkeyBase64: RSA private key in base64 (data between "-----BEGIN RSA PRIVATE KEY-----" and "-----END RSA PRIVATE KEY-----") // NOT WORKING YET! static public func encryptWithRSAPrivateKey(_ data: Data, privkeyBase64: String, keychainTag: String) -> Data? { let myKeychainTag = keychainTag + (Config.useKeyHashes ? "-" + String(privkeyBase64.hashValue) : "") var keyRef = getRSAKeyFromKeychain(myKeychainTag) if ( keyRef == nil ) { keyRef = addRSAPrivateKey(privkeyBase64, tagName: myKeychainTag) } if ( keyRef == nil ) { return nil } return encryptWithRSAKey(data, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1) } // Encrypt data with a RSA public key // pubkeyBase64: RSA public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----") static public func encryptWithRSAPublicKey(_ data: Data, pubkeyBase64: String, keychainTag: String) -> Data? { let myKeychainTag = keychainTag + (Config.useKeyHashes ? "-" + String(pubkeyBase64.hashValue) : "") var keyRef = getRSAKeyFromKeychain(myKeychainTag) if ( keyRef == nil ) { keyRef = addRSAPublicKey(pubkeyBase64, tagName: myKeychainTag) } if ( keyRef == nil ) { return nil } return encryptWithRSAKey(data, rsaKeyRef: keyRef!, padding: SecPadding.PKCS1) } // Decrypt an encrypted data with a RSA private key // privkeyBase64: RSA private key in base64 (data between "-----BEGIN RSA PRIVATE KEY-----" and "-----END RSA PRIVATE KEY-----") static public func decryptWithRSAPrivateKey(_ encryptedData: Data, privkeyBase64: String, keychainTag: String) -> Data? { let myKeychainTag = keychainTag + (Config.useKeyHashes ? "-" + String(privkeyBase64.hashValue) : "") var keyRef = getRSAKeyFromKeychain(myKeychainTag) if ( keyRef == nil ) { keyRef = addRSAPrivateKey(privkeyBase64, tagName: myKeychainTag) } if ( keyRef == nil ) { return nil } return decryptWithRSAKey(encryptedData, rsaKeyRef: keyRef!, padding: SecPadding()) } // Decrypt an encrypted data with a RSA public key // pubkeyBase64: RSA public key in base64 (data between "-----BEGIN PUBLIC KEY-----" and "-----END PUBLIC KEY-----") static public func decryptWithRSAPublicKey(_ encryptedData: Data, pubkeyBase64: String, keychainTag: String) -> Data? { let myKeychainTag = keychainTag + (Config.useKeyHashes ? "-" + String(pubkeyBase64.hashValue) : "") var keyRef = getRSAKeyFromKeychain(myKeychainTag) if ( keyRef == nil ) { keyRef = addRSAPublicKey(pubkeyBase64, tagName: myKeychainTag) } if ( keyRef == nil ) { return nil } return decryptWithRSAKey(encryptedData, rsaKeyRef: keyRef!, padding: SecPadding()) } }