I'm trying to convert a block of Java code to Swift but I'm not getting
This is the block that I want to convert:
https://github.com/K4CZP3R/tapo-p100-java-poc/blob/main/src/main/java/KspEncryption.java
public static C658a decodeTapoKey(String key, KspKeyPair keyPair) {
KspDebug.out("Will try to decode the following key: " + key);
try {
byte[] decode = KspB64.decode(key.getBytes("UTF-8"));
byte[] decode2 = KspB64.decode(keyPair.getPrivateKey());
Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding");
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey p = kf.generatePrivate(new PKCS8EncodedKeySpec(decode2));
instance.init(Cipher.DECRYPT_MODE, p);
byte[] doFinal = instance.doFinal(decode);
byte[] bArr = new byte[16];
byte[] bArr2 = new byte[16];
System.arraycopy(doFinal, 0, bArr, 0, 16);
System.arraycopy(doFinal, 16, bArr2, 0, 16);
return new C658a(bArr, bArr2);
}
catch (Exception ex)
{
KspDebug.out("Something went wrong: " + ex.getMessage());
return null;
}
}
Here is the same code but this time in Python: https://github.com/K4CZP3R/tapo-p100-python/blob/21e4bf9b61c08a3eb215293198968f82cd80ab2d/encryption.py
def decode_handshake_key(key: str, key_pair: KeyPair) -> TpLinkCipher:
logger.debug(f"Will decode handshake key (...{key[5:]}) using current key pair")
decode: bytes = base64.b64decode(key.encode("UTF-8"))
decode2: bytes = base64.b64decode(key_pair.get_private_key())
cipher = PKCS1_v1_5.new(RSA.import_key(decode2))
do_final = cipher.decrypt(decode, None)
if do_final is None:
raise ValueError("Decryption failed!")
b_arr:bytearray = bytearray()
b_arr2:bytearray = bytearray()
for i in range(0, 16):
b_arr.insert(i, do_final[i])
for i in range(0, 16):
b_arr2.insert(i, do_final[i + 16])
And this is what I have right now in Swift that is not correct
func decodeTapoKey(key: String, keyPair: KspKeyPair) {
let data = key.data(using: .utf8)!
let b64 = data.base64EncodedString(options: .lineLength76Characters)
let data2 = keyPair.privateKey.data(using: .utf8)!
let b642 = data2.base64EncodedString(options: .lineLength76Characters)
do {
let privateKey = try PrivateKey(pemNamed: keyPair.getPrivateKey())
let encrypted = try EncryptedMessage(base64Encoded: key)
let clear = try encrypted.decrypted(with: privateKey, padding: .PKCS1)
// Then you can use:
let data = clear.data
let base64String = clear.base64String
let string = try clear.string(encoding: .utf8)
} catch {
print("error")
}
I was trying to use the Pod SwiftyRSA.
Can someone help me with this?
It looks as if there is the following scenario:
On client side we could:
The source code for this function could look like this:
static func decodeTapoKey(key: String, keyPair: KspKeyPair) throws -> (Data, Data) {
let keyWithoutWhiteSpaces = String(key.compactMap { $0.isWhitespace ? nil : $0 })
let encrypted = try EncryptedMessage(base64Encoded: keyWithoutWhiteSpaces)
let privateKey = try PrivateKey(base64Encoded: keyPair.privateKey)
let decryptedKey = try encrypted.decrypted(with: privateKey, padding: SecPadding.PKCS1)
let b_arr = decryptedKey.data[0..<16]
let b_arr2 = decryptedKey.data[16..<32]
return (b_arr, b_arr2)
}
Test
One can generate a private and public test key as well as an AES key with 256 bit (= 32 bytes). With this data we should get the same binary output for the key as in the Python script.
For the output of hex data we can use the function hexDescription
from this fine answer: https://stackoverflow.com/a/39075044.
A completely self-contained example:
Decrypt.swift
import Foundation
import SwiftyRSA
struct KspKeyPair {
let privateKey: String
let publicKey: String
}
final class Decrypt {
static func decodeTapoKey(key: String, keyPair: KspKeyPair) throws -> (Data, Data) {
let keyWithoutWhiteSpaces = String(key.compactMap { $0.isWhitespace ? nil : $0 })
let encrypted = try EncryptedMessage(base64Encoded: keyWithoutWhiteSpaces)
let privateKey = try PrivateKey(base64Encoded: keyPair.privateKey)
let decryptedKey = try encrypted.decrypted(with: privateKey, padding: SecPadding.PKCS1)
let b_arr = decryptedKey.data[0..<16]
let b_arr2 = decryptedKey.data[16..<32]
return (b_arr, b_arr2)
}
}
extension Data {
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
ViewController.swift
import UIKit
class ViewController: UIViewController {
let privateKey =
"""
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMY+gDb9hwm9gmYB+iDYxKPngQ3X
Rm3U4tzea9NLtmHgWipM44tE4zWkEeaBawWbFjrnT0lPriTXPn6OvvDRCY7eRejB9wP/aezoNMMr
A2XQ6Du8gcopM1ylUJCWjr9nftTb6wn75wY5IMPUKM2N0uZ+dvfQH4qEQwE05z6Iz1szAgMBAAEC
gYAW5UcHktZSwKlbwKSzwHVNfMJB5/gBXVHqMmH/oEHrIe8n7YNmJUmce1t55L6IgjXaDbbxf5tc
M+PK2A+jXnEc8+2snDbI1PpBepEwg5vzZ8RYnhGZT6P0/PsIqInkTIZnfXDnQ6wBapO1m9xJDe/B
fyWMVRa6JqJtgQ0XpfG76QJBAOgKPSF5wJKC69lCbvyh38fQoeJxEKrD03FGXGBDLLYOHqfijdMT
vcnE415994MI3fTy2dWm/yB8wZ7DvYVwVX8CQQDatuZyi6LhLhU47l5vpFsOnRWGhGrZaIN0o7N/
1v5Vwieujrwy/yW2uCvUeXVnmohJa+sFSr29HO4PEQwWtFxNAkBXSxrKUDJ5K9Wsa0izs/YrBrsQ
JDb/9yHBmJXCBSN57f/sateuE9wvXummr78AxcIyl3YJ4YRTZXu1za+r1qHjAkEAzjkavPKQ18W9
2PpZLOdJvFO9EiMVJH15Rad8/pNXKMFy7RJEvcj6ZHjvSt5jJxb8Xk5VQZ4hnYkDpk0qmtXhGQJB
AIO6TEXkHU3Sn0qTZsPmpu+EfCADLNKG43kiv74+cRzS7Th2A6E1yq5Y/lmbdpYaHqm0mKgvMHb2
ls7DtRTYoXo=
"""
let publicKey =
"""
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGPoA2/YcJvYJmAfog2MSj54EN10Zt1OLc3mvT
S7Zh4FoqTOOLROM1pBHmgWsFmxY6509JT64k1z5+jr7w0QmO3kXowfcD/2ns6DTDKwNl0Og7vIHK
KTNcpVCQlo6/Z37U2+sJ++cGOSDD1CjNjdLmfnb30B+KhEMBNOc+iM9bMwIDAQAB
"""
let secretKey =
"""
YyqsaPrgmVKcjS6UQY5aV0KtOdovqbuwGQMkZMoHtwygZQVy3fiLp0/03B4LKkYtIm0SBPYJw/cV
w2DyZYsPPvX71RbuU9Pp+AJkWTfReEWhSW2vonWv6HIULK94hREHt7S51oBNXo5QP4wn2F4PD3hg
P2DYBAw7guMy9wbGB+4=
"""
override func viewDidLoad() {
super.viewDidLoad()
let keyPair = KspKeyPair(privateKey: privateKey, publicKey: publicKey)
do {
let (b_arr, b_arr2) = try Decrypt.decodeTapoKey(key: secretKey, keyPair: keyPair)
print(b_arr.hexDescription)
print(b_arr2.hexDescription)
} catch {
print("decoding failed: \(error)")
}
}
}
The output is:
4907d77a736619e6338f7e88fbed565f
40decbd392295df33afa817d23ac2824
Python
Based on your Python script also a completely self-contained example:
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
import base64
import logging
from collections import namedtuple
KspKeyPair = namedtuple('KspKeyPair', ['private_key', 'public_key'])
logger = logging.getLogger('root')
def decode_handshake_key(key: str, keyPair: KspKeyPair):
logger.debug(f"Will decode handshake key (...{key[5:]}) using current key pair")
decode: bytes = base64.b64decode(key.encode("UTF-8"))
decode2: bytes = base64.b64decode(keyPair.private_key)
cipher = PKCS1_v1_5.new(RSA.import_key(decode2))
do_final = cipher.decrypt(decode, None)
if do_final is None:
raise ValueError("Decryption failed!")
b_arr: bytearray = bytearray()
b_arr2: bytearray = bytearray()
for i in range(0, 16):
b_arr.insert(i, do_final[i])
for i in range(0, 16):
b_arr2.insert(i, do_final[i + 16])
print(b_arr.hex())
print(b_arr2.hex())
if __name__ == "__main__":
privateKey = """
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMY+gDb9hwm9gmYB+iDYxKPngQ3X
Rm3U4tzea9NLtmHgWipM44tE4zWkEeaBawWbFjrnT0lPriTXPn6OvvDRCY7eRejB9wP/aezoNMMr
A2XQ6Du8gcopM1ylUJCWjr9nftTb6wn75wY5IMPUKM2N0uZ+dvfQH4qEQwE05z6Iz1szAgMBAAEC
gYAW5UcHktZSwKlbwKSzwHVNfMJB5/gBXVHqMmH/oEHrIe8n7YNmJUmce1t55L6IgjXaDbbxf5tc
M+PK2A+jXnEc8+2snDbI1PpBepEwg5vzZ8RYnhGZT6P0/PsIqInkTIZnfXDnQ6wBapO1m9xJDe/B
fyWMVRa6JqJtgQ0XpfG76QJBAOgKPSF5wJKC69lCbvyh38fQoeJxEKrD03FGXGBDLLYOHqfijdMT
vcnE415994MI3fTy2dWm/yB8wZ7DvYVwVX8CQQDatuZyi6LhLhU47l5vpFsOnRWGhGrZaIN0o7N/
1v5Vwieujrwy/yW2uCvUeXVnmohJa+sFSr29HO4PEQwWtFxNAkBXSxrKUDJ5K9Wsa0izs/YrBrsQ
JDb/9yHBmJXCBSN57f/sateuE9wvXummr78AxcIyl3YJ4YRTZXu1za+r1qHjAkEAzjkavPKQ18W9
2PpZLOdJvFO9EiMVJH15Rad8/pNXKMFy7RJEvcj6ZHjvSt5jJxb8Xk5VQZ4hnYkDpk0qmtXhGQJB
AIO6TEXkHU3Sn0qTZsPmpu+EfCADLNKG43kiv74+cRzS7Th2A6E1yq5Y/lmbdpYaHqm0mKgvMHb2
ls7DtRTYoXo=
"""
publicKey = """
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGPoA2/YcJvYJmAfog2MSj54EN10Zt1OLc3mvT
S7Zh4FoqTOOLROM1pBHmgWsFmxY6509JT64k1z5+jr7w0QmO3kXowfcD/2ns6DTDKwNl0Og7vIHK
KTNcpVCQlo6/Z37U2+sJ++cGOSDD1CjNjdLmfnb30B+KhEMBNOc+iM9bMwIDAQAB
"""
secretKey = """
YyqsaPrgmVKcjS6UQY5aV0KtOdovqbuwGQMkZMoHtwygZQVy3fiLp0/03B4LKkYtIm0SBPYJw/cV
w2DyZYsPPvX71RbuU9Pp+AJkWTfReEWhSW2vonWv6HIULK94hREHt7S51oBNXo5QP4wn2F4PD3hg
P2DYBAw7guMy9wbGB+4=
"""
keyPair = KspKeyPair(privateKey, publicKey)
decode_handshake_key(secretKey, keyPair)
And the output of this Python program is exactly the same as that of the iOS Swift program. So the Swift part seems to work correctly.
Java
Also for the Java routine the same output should be given. Let's test it with this self-contained Java example:
package com.software7;
import org.apache.commons.codec.binary.Hex;
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class Main {
static String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMY+gDb9hwm9gmYB+iDYxKPngQ3XRm3U4tzea9NLtmHgWipM44tE4zWkEeaBawWbFjrnT0lPriTXPn6OvvDRCY7eRejB9wP/aezoNMMrA2XQ6Du8gcopM1ylUJCWjr9nftTb6wn75wY5IMPUKM2N0uZ+dvfQH4qEQwE05z6Iz1szAgMBAAECgYAW5UcHktZSwKlbwKSzwHVNfMJB5/gBXVHqMmH/oEHrIe8n7YNmJUmce1t55L6IgjXaDbbxf5tcM+PK2A+jXnEc8+2snDbI1PpBepEwg5vzZ8RYnhGZT6P0/PsIqInkTIZnfXDnQ6wBapO1m9xJDe/BfyWMVRa6JqJtgQ0XpfG76QJBAOgKPSF5wJKC69lCbvyh38fQoeJxEKrD03FGXGBDLLYOHqfijdMTvcnE415994MI3fTy2dWm/yB8wZ7DvYVwVX8CQQDatuZyi6LhLhU47l5vpFsOnRWGhGrZaIN0o7N/1v5Vwieujrwy/yW2uCvUeXVnmohJa+sFSr29HO4PEQwWtFxNAkBXSxrKUDJ5K9Wsa0izs/YrBrsQJDb/9yHBmJXCBSN57f/sateuE9wvXummr78AxcIyl3YJ4YRTZXu1za+r1qHjAkEAzjkavPKQ18W92PpZLOdJvFO9EiMVJH15Rad8/pNXKMFy7RJEvcj6ZHjvSt5jJxb8Xk5VQZ4hnYkDpk0qmtXhGQJBAIO6TEXkHU3Sn0qTZsPmpu+EfCADLNKG43kiv74+cRzS7Th2A6E1yq5Y/lmbdpYaHqm0mKgvMHb2ls7DtRTYoXo=";
static String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGPoA2/YcJvYJmAfog2MSj54EN10Zt1OLc3mvTS7Zh4FoqTOOLROM1pBHmgWsFmxY6509JT64k1z5+jr7w0QmO3kXowfcD/2ns6DTDKwNl0Og7vIHKKTNcpVCQlo6/Z37U2+sJ++cGOSDD1CjNjdLmfnb30B+KhEMBNOc+iM9bMwIDAQAB";
static String secretKey = "YyqsaPrgmVKcjS6UQY5aV0KtOdovqbuwGQMkZMoHtwygZQVy3fiLp0/03B4LKkYtIm0SBPYJw/cVw2DyZYsPPvX71RbuU9Pp+AJkWTfReEWhSW2vonWv6HIULK94hREHt7S51oBNXo5QP4wn2F4PD3hgP2DYBAw7guMy9wbGB+4=";
public static void main(String[] args) {
KspKeyPair keyPair = new KspKeyPair(privateKey, publicKey);
C658a result = decodeTapoKey(secretKey, keyPair);
System.out.println(Hex.encodeHexString(result.bArr));
System.out.println(Hex.encodeHexString(result.bArr2));
}
public static C658a decodeTapoKey(String key, KspKeyPair keyPair) {
try {
byte[] decode = KspB64.decode(key.getBytes("UTF-8"));
byte[] decode2 = KspB64.decode(keyPair.getPrivateKey());
Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding");
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey p = kf.generatePrivate(new PKCS8EncodedKeySpec(decode2));
instance.init(Cipher.DECRYPT_MODE, p);
byte[] doFinal = instance.doFinal(decode);
byte[] bArr = new byte[16];
byte[] bArr2 = new byte[16];
System.arraycopy(doFinal, 0, bArr, 0, 16);
System.arraycopy(doFinal, 16, bArr2, 0, 16);
return new C658a(bArr, bArr2);
} catch (Exception ex) {
System.out.println("Something went wrong: " + ex.getMessage());
return null;
}
}
}
class C658a {
byte[] bArr;
byte[] bArr2;
public C658a(byte[] bArr, byte[] bArr2) {
this.bArr = bArr;
this.bArr2 = bArr2;
}
}
class KspKeyPair {
String privateKey;
String publicKey;
public KspKeyPair(String privateKey, String publicKey) {
this.privateKey = privateKey;
this.publicKey = publicKey;
}
public byte[] getPrivateKey() { return privateKey.getBytes(); }
public byte[] getPublicKey() { return publicKey.getBytes(); }
}
class KspB64 {
public static byte[] decode(byte[] bytes) {
return Base64.getMimeDecoder().decode(bytes);
}
}
And indeed the Java program delivers the same result.