// // KMVerificationRSA.swift // PDF Reader Pro // // Created by Niehaoyu on 2024/8/9. // import Cocoa import CryptoKit import Security import CommonCrypto import Foundation @objc class KMVerificationRSA: NSObject { @objc func active(uniquesn: String, cdkey: String, model: String, os: String, language: String, appversion: String) -> String? { let dictionary: [String: Any] = ["subscription": ["app_code":"com.imyfone.pdf", "cdkey":cdkey], "device": ["unique_sn":uniquesn, "os":os, "platform":"DMG", "time_zone":"UTC", "language":language, "app_version":appversion]] if let jsonData = try? JSONSerialization.data(withJSONObject: dictionary, options:[]), let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) if let publicKey = loadPublicKey() { let encryptedData = encrypt(plainText: jsonString, publicKey: publicKey) print("Encrypted data: \(encryptedData?.base64EncodedString() ?? "nil")") let encryptedString = encryptedData?.base64EncodedString(); return encryptedString //Test // let dataToEncrypt = jsonString.data(using: .utf8)! // // if let encryptedDataChunks = encryptDataInChunks(data: dataToEncrypt, publicKey: publicKey) { // print("Encrypted data chunks:") // for chunk in encryptedDataChunks { // print(chunk.base64EncodedString()) // 输出每个加密块 // } // print("111") // } // // // let message = jsonString // if let messageData = message.data(using: .utf8) { // if let publicKey = loadPublicKey() { // if let encryptedChunks = encrypt(data: messageData, publicKey: publicKey) { // let encryptedString = encryptedChunks.map { $0.base64EncodedString() } // print("Encrypted data: \(encryptedString)") // return encryptedString.first // } // } // } } } return nil } @objc func verify(uniquesn: String, model: String, os: String, language: String, appversion: String) -> String? { let dictionary: [String: Any] = ["subscription": ["app_code":"com.imyfone.pdf"], "device": ["unique_sn":uniquesn, "os":os, "platform":"DMG", "time_zone":"UTC", "language":language, "app_version":appversion]] if let jsonData = try? JSONSerialization.data(withJSONObject: dictionary, options:[]), let jsonString = String(data: jsonData, encoding: .utf8) { print(jsonString) if let publicKey = loadPublicKey() { let encryptedData = encrypt(plainText: jsonString, publicKey: publicKey) print("Encrypted data: \(encryptedData?.base64EncodedString() ?? "nil")") let encryptedString = encryptedData?.base64EncodedString(); return encryptedString } //Test let message = jsonString if let messageData = message.data(using: .utf8) { if let publicKey = loadPublicKey() { if let encryptedChunks = encrypt(data: messageData, publicKey: publicKey) { let encryptedString = encryptedChunks.map { $0.base64EncodedString() } print("Encrypted data: \(encryptedString)") return encryptedString.first } } } } return nil } func encrypt(plainText: String, publicKey: SecKey) -> Data? { guard let data = plainText.data(using: .utf8) else { return nil } var error: Unmanaged? let encryptedData = SecKeyCreateEncryptedData(publicKey, .rsaEncryptionPKCS1, data as CFData, &error) if let error = error?.takeRetainedValue() { print("Error encrypting data: \(error)") return nil } return encryptedData as Data? } func encrypt(data: Data, publicKey: SecKey) -> [Data]? { let maxChunkSize = 214 // 对于 RSA-2048,214 是最大加密长度 var chunks = [Data]() var offset = 0 while offset < data.count { let chunkSize = min(maxChunkSize, data.count - offset) let chunk = data.subdata(in: offset.. SecKey? { // let publicKeyString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqYKtu5pbT3bhbOfZ7XFhw1IEiqGvx/z3YwvcrLvG6I+EBbim/YuDfSTqpcTZSjbMeLz8nCzkAsMakoimzI6XpNQOZN35cDCFkjn0vicpnfla2JPMxREwddblAz7u/EMdx71ElcY+UYfSu1QM0Lepc2QPWw9oaD/cNktH6xE6eogLEH0k8ZYP8YIzTW02og7mNtLVO1ssKQYUCIQ5LkKA7zypQul5upajE51rq49vdCoA98y2zBRTMXGM7tpa2rbXQ9fDMn5heCLVCXCHNXDwBMxNhURm7fEfxZPwq7DUmH8EWKXCKKhu+GP0c/eom50FzMxfN2wpQSgNfyNQ7bBgwIDAQAB" let publicKeyString = Self.RsaPublic guard let data = Data(base64Encoded: publicKeyString) else { return nil } let options: [String: Any] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeyClass as String: kSecAttrKeyClassPublic, kSecAttrKeySizeInBits as String: 4096 ] var error: Unmanaged? let publicKey = SecKeyCreateWithData(data as CFData, options as CFDictionary, &error) if let error = error?.takeRetainedValue() { print("Error loading public key: \(error)") return nil } return publicKey } func base64Encode(string: String) -> String? { // 将字符串转换为 Data guard let data = string.data(using: .utf8) else { print("Error converting string to Data.") return nil } // 使用 Data 的 base64EncodedString() 方法进行编码 let base64String = data.base64EncodedString() return base64String } // RSA 分块加密 func encryptDataInChunks(data: Data, publicKey: SecKey) -> [Data]? { let blockSize = SecKeyGetBlockSize(publicKey) - 11 // PKCS#1 v1.5 填充需要额外字节 var encryptedChunks: [Data] = [] var offset = 0 while offset < data.count { let size = min(data.count - offset, blockSize) // 计算当前块大小 let chunk = data.subdata(in: offset..? let encryptedData = SecKeyCreateEncryptedData(publicKey, .rsaEncryptionPKCS1, data as CFData, &error) guard let successData = encryptedData else { if let error = error?.takeRetainedValue() { print("Encryption failed: \(error)") } return nil } encryptedChunks.append(successData as Data) offset += size } return encryptedChunks } func encrypt22(data: Data, publicKey: SecKey) -> [Data]? { let keySizeInBytes = 256 // 对于 RSA-2048,密钥大小为 256 字节 let paddingSize = 11 // PKCS#1 填充大小 let maxChunkSize = keySizeInBytes - paddingSize // 最大可加密数据大小 var chunks = [Data]() var offset = 0 while offset < data.count { let chunkSize = min(maxChunkSize, data.count - offset) let chunk = data.subdata(in: offset..? guard let encryptedData = SecKeyCreateEncryptedData(publicKey, .rsaEncryptionPKCS1, chunk as CFData, &error) else { print("Encryption error: \(error!.takeRetainedValue() as Error)") return nil } chunks.append(encryptedData as Data) offset += chunkSize } return chunks } //https://www.bejson.com/enc/rsa/网站生成的密钥要保留\r\n,去除前缀后缀 //pem格式公钥 用途加密 // public static let RsaPublic = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh3U54ETmA3anGcL3D4uAa5vNI0S6rVoM\r\nCe9MhhP3MLRSW1gMaEHJE+vsVLN8ChFytkiI60CInYC91bk2x2FaurIIGEwowGz4lifDNsQFeWEd\r\nxs33U3fGM3+wGXC7sfYJrtmriqqmmM9eTqxvVWARD1EvnSUWseBRquCuSJ3rkQHxm0E9n88SAaM4\r\nopogLrAyz82NlWQSE55Yf0wiNNNh+HdiQOpTBIes5blBcxbai9KVQU5dqlkKNceg7rdDxANFlm49\r\nTn4yNkrS80w75IQL1xLzYfvWSlWn/falJR4jNBfBg7aTyJduGvWytTgphYVTPheJN/fEDRMR0XTV\r\nlnkHZQIDAQAB"; public static let RsaPublic = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh3U54ETmA3anGcL3D4uAa5vNI0S6rVoMCe9MhhP3MLRSW1gMaEHJE+vsVLN8ChFytkiI60CInYC91bk2x2FaurIIGEwowGz4lifDNsQFeWEdxs33U3fGM3+wGXC7sfYJrtmriqqmmM9eTqxvVWARD1EvnSUWseBRquCuSJ3rkQHxm0E9n88SAaM4opogLrAyz82NlWQSE55Yf0wiNNNh+HdiQOpTBIes5blBcxbai9KVQU5dqlkKNceg7rdDxANFlm49Tn4yNkrS80w75IQL1xLzYfvWSlWn/falJR4jNBfBg7aTyJduGvWytTgphYVTPheJN/fEDRMR0XTVlnkHZQIDAQAB"; //pem格式私钥 用途解密 // public static let RsaPrivate = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCHdTngROYDdqcZwvcPi4Brm80j\r\nRLqtWgwJ70yGE/cwtFJbWAxoQckT6+xUs3wKEXK2SIjrQIidgL3VuTbHYVq6sggYTCjAbPiWJ8M2\r\nxAV5YR3GzfdTd8Yzf7AZcLux9gmu2auKqqaYz15OrG9VYBEPUS+dJRax4FGq4K5IneuRAfGbQT2f\r\nzxIBoziimiAusDLPzY2VZBITnlh/TCI002H4d2JA6lMEh6zluUFzFtqL0pVBTl2qWQo1x6Dut0PE\r\nA0WWbj1OfjI2StLzTDvkhAvXEvNh+9ZKVaf99qUlHiM0F8GDtpPIl24a9bK1OCmFhVM+F4k398QN\r\nExHRdNWWeQdlAgMBAAECggEAA7uZQrLjW8kTRcR3pngDq/N5LbWhJ1HJ7yQnCbLXJwJxGo9MmDrn\r\n3aKsupDPpD9i198b1s/Kc/yuPouCFPB73ZU2X+UJYnQeTZT+7i1ssTGZ7naSkDxGu/iS08Cli3vq\r\nPGVHMuUM/j0IrkZWMxYYokFsfHofK/uwhHL4mouviClpCUoSy/OId0epVo81auFZLuAEK3/n9nG0\r\n9zRstRqOIxe4iRFEN45uj4+FZFBR5nDSRqztbVvdPJJip58IyN6ZRPbFJI9RUwzBl/WWvBJ6sXg1\r\n3SJAkvnJoO4q1ECbUL1kFELIEjWhsqM7QdGAaAYfUSYklVq6agwI9LL2Jk00wQKBgQDoPTW5hwqf\r\nlyA56hjZuHSgjOd3Tw8F3FrDanREc1GhZKrzVFmjC8s6o+dmKoRPnzjAJXtLtcJwEItJkkebZYA+\r\nWuY+y4c+oS+fp69UtgneqLn6knJHqZHhVG4wVzSUuLU0vnAeywHQZ7wE41V6coGwp0kb07N8kAWQ\r\nxTI64IafOQKBgQCVUSFVUNqAMToMTCF0qN7bp/AlFdfRMsZb+ANBDCv1pEL9IQpo+umBkTAgexOB\r\nLWuFO7B3+YIbE23HoDyR/IIodsbqemT3N0GqQ4HfSqe2Hlfuspqbx0T3UocSp2+2jEt4aHLrWlS1\r\nMUA24oEg0ybh6aotZF1CjyIOsY1T8Lj9jQKBgQDP/loKgjZyfgA/vjhpbfkN/YQkZ6r1vceQMtxU\r\nnc9jM2yjp0zsaNin+Tl6V+POB7Tk6ezF/tBYW4jT6G23cC1uKy0A2nmTDvs/CwhNXwfx4b1G/Dfb\r\nlLPxUhOpucB+3fp0dYanHXvPcciDvDLHCpy6YOhqQq0Ch15krSfycTpAiQKBgGmrnNdv/PIz5EzH\r\no/WXfQAefYkBSFa4hECZ6FOkdYfF09KoC2H4EECtq02RTGBi8HC9qUl+vmDDAzH/aF44QTS5ulQ+\r\ngi2iYUpJtCN+BeqQ1tIKe/g6scgGE6lT7W9XIiH4aTu+FayVSkiNS60bQa0nXFP7bzSnbwbPCKGa\r\n/pARAoGBAMbpl+vPULiJ4IrGbNQgA1hKNKhSerwBBKRsiM4MlcDVkjlpLd+bQ9POQy2y0r7GZQjW\r\nmt0XF3tb3Fp20U/ETUxKI4BktTX2A9hrmj5R3Uq+40KocQonjmIMEHF9GxnWF1YfzKtUD2DrDVp/\r\n1YMHv7Is7IVCtyIbLXVuMVXvGeab"; public static let RsaPrivate = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCHdTngROYDdqcZwvcPi4Brm80jRLqtWgwJ70yGE/cwtFJbWAxoQckT6+xUs3wKEXK2SIjrQIidgL3VuTbHYVq6sggYTCjAbPiWJ8M2xAV5YR3GzfdTd8Yzf7AZcLux9gmu2auKqqaYz15OrG9VYBEPUS+dJRax4FGq4K5IneuRAfGbQT2fzxIBoziimiAusDLPzY2VZBITnlh/TCI002H4d2JA6lMEh6zluUFzFtqL0pVBTl2qWQo1x6Dut0PEA0WWbj1OfjI2StLzTDvkhAvXEvNh+9ZKVaf99qUlHiM0F8GDtpPIl24a9bK1OCmFhVM+F4k398QNExHRdNWWeQdlAgMBAAECggEAA7uZQrLjW8kTRcR3pngDq/N5LbWhJ1HJ7yQnCbLXJwJxGo9MmDrn3aKsupDPpD9i198b1s/Kc/yuPouCFPB73ZU2X+UJYnQeTZT+7i1ssTGZ7naSkDxGu/iS08Cli3vqPGVHMuUM/j0IrkZWMxYYokFsfHofK/uwhHL4mouviClpCUoSy/OId0epVo81auFZLuAEK3/n9nG09zRstRqOIxe4iRFEN45uj4+FZFBR5nDSRqztbVvdPJJip58IyN6ZRPbFJI9RUwzBl/WWvBJ6sXg13SJAkvnJoO4q1ECbUL1kFELIEjWhsqM7QdGAaAYfUSYklVq6agwI9LL2Jk00wQKBgQDoPTW5hwqflyA56hjZuHSgjOd3Tw8F3FrDanREc1GhZKrzVFmjC8s6o+dmKoRPnzjAJXtLtcJwEItJkkebZYA+WuY+y4c+oS+fp69UtgneqLn6knJHqZHhVG4wVzSUuLU0vnAeywHQZ7wE41V6coGwp0kb07N8kAWQxTI64IafOQKBgQCVUSFVUNqAMToMTCF0qN7bp/AlFdfRMsZb+ANBDCv1pEL9IQpo+umBkTAgexOBLWuFO7B3+YIbE23HoDyR/IIodsbqemT3N0GqQ4HfSqe2Hlfuspqbx0T3UocSp2+2jEt4aHLrWlS1MUA24oEg0ybh6aotZF1CjyIOsY1T8Lj9jQKBgQDP/loKgjZyfgA/vjhpbfkN/YQkZ6r1vceQMtxUnc9jM2yjp0zsaNin+Tl6V+POB7Tk6ezF/tBYW4jT6G23cC1uKy0A2nmTDvs/CwhNXwfx4b1G/DfblLPxUhOpucB+3fp0dYanHXvPcciDvDLHCpy6YOhqQq0Ch15krSfycTpAiQKBgGmrnNdv/PIz5EzHo/WXfQAefYkBSFa4hECZ6FOkdYfF09KoC2H4EECtq02RTGBi8HC9qUl+vmDDAzH/aF44QTS5ulQ+gi2iYUpJtCN+BeqQ1tIKe/g6scgGE6lT7W9XIiH4aTu+FayVSkiNS60bQa0nXFP7bzSnbwbPCKGa/pARAoGBAMbpl+vPULiJ4IrGbNQgA1hKNKhSerwBBKRsiM4MlcDVkjlpLd+bQ9POQy2y0r7GZQjWmt0XF3tb3Fp20U/ETUxKI4BktTX2A9hrmj5R3Uq+40KocQonjmIMEHF9GxnWF1YfzKtUD2DrDVp/1YMHv7Is7IVCtyIbLXVuMVXvGeab"; func getPrivateKeyFromString(key: String) -> SecKey? { // guard let data = key.data(using: .utf8) else { return nil } guard let data = Data(base64Encoded: key) else { return nil } let parameters: [CFString: Any] = [ kSecAttrKeyType: kSecAttrKeyTypeRSA, kSecAttrKeyClass: kSecAttrKeyClassPrivate, kSecAttrKeySizeInBits: 2048, kSecReturnPersistentRef: true ] var error: Unmanaged? guard let privkeyData = Self.stripPrivateKeyHeader(data) else { return nil } if let privateKey = SecKeyCreateWithData(privkeyData as CFData, parameters as CFDictionary, &error) { return privateKey } else { print("Error creating private key: \(error!.takeRetainedValue())") return nil } } @objc public func decrypt(source: NSString) -> NSString? { guard let data = Data(base64Encoded: source as String), data.isEmpty == false else { KMPrint("需要解密的数据为空.") return nil } guard let key = self.getPrivateKeyFromString(key: Self.RsaPrivate) else { KMPrint("私钥生成失败.") return nil } // let data: Data = (NSData(base64Encoded: (source as String), options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)! as Data) // let data = Data(base64Encoded: source as String) if let deData = SecKeyCreateDecryptedData(key, .rsaEncryptionPKCS1, data as CFData, nil) { return String(data: deData as Data, encoding: .utf8) as NSString? } return nil } // Verify that the supplied key is in fact a PEM RSA private key key and strip the header // On disk, a PEM RSA private key file starts with string "-----BEGIN RSA PRIVATE KEY-----", // and ends with string "-----END RSA PRIVATE KEY-----" static fileprivate func stripPrivateKeyHeader(_ privkey: Data) -> 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..