How to read string from printwriter from java server to swift client through stream - swift5

i am new in swift i want read string from java server through stream in swift but its giving me -1
here is my server code in java
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(tempClientSocket.getOutputStream())),
true);
} catch (IOException e) {
e.printStackTrace();
}
out.println("hello world");
here is my client code in swift
let data ="hello".data(using: .utf8)!
let dataCount = data.count
let bytesWritten = data.withUnsafeBytes { (p: UnsafePointer<UInt8>) -> Int in
return streams.outputStream.write(p, maxLength: dataCount)
}
but its reading i think in bytes thats why byteswritten is coming -1 i want to see "hello world" in byteswritten

Related

Swift NWConnection with Discord IPC (protocol error)

I try to connect with Discord using IPC, but whatever i try, the error message stays the same Optional("\u{02}\0\0\0(\0\0\0{"code":1003,"message":"protocol error"}")
I started with the encode function, but that is UInt8 because Swift apparently does not support an UInt32 data object.
Then i tried to create an UInt32 (memory) array, to convert that to Data, that failed as well.
can someone point me into the direction where I make a mistake.
import Foundation
import Cocoa
import Network
class Discord {
enum opcode: UInt32 {
case handshake = 0
case frame = 1
case close = 2
case ping = 3
case pong = 4
}
var appID: String
private var connection: NWConnection?
private let endpoint: String = NSTemporaryDirectory() + "discord-ipc-0"
init(appID: String) {
self.appID = appID
}
func connect() {
print("Connecting to \(endpoint)")
connection = NWConnection(
to: NWEndpoint.unix(path: endpoint),
using: .tcp
)
connection?.stateUpdateHandler = { state in
switch state {
case .setup:
print("Setting up...")
case .preparing:
print("Prepairing...")
case .waiting(let error):
print("Waiting: \(error)")
case .ready:
print("Ready...")
case .failed(let error):
print("Failed: \(error)")
case .cancelled:
print("Cancelled :'(")
default:
break
}
}
connection?.receiveMessage { completeContent, contentContext, isComplete, error in
print(
String(data: completeContent ?? Data(), encoding: .utf8),
error
)
}
connection?.start(queue: .global())
}
func uint32encode(opcode: opcode, message string: String) -> Data {
let payload = string.data(using: .utf8)!
var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 8 + payload.count, alignment: 0)
defer { buffer.deallocate() }
buffer.copyBytes(from: payload)
buffer[8...] = buffer[..<payload.count]
buffer.storeBytes(of: opcode.rawValue, as: UInt32.self)
buffer.storeBytes(of: UInt32(payload.count), toByteOffset: 4, as: UInt32.self)
let uIntData = Data(bytes: &buffer, count: 8 + payload.count)
return uIntData
}
func encode(opcode: opcode, message string: String) -> Data {
let jsondata = string.data(using: .utf8)!
var data = Data()
data.append(UInt8(opcode.rawValue))
data.append(UInt8(jsondata.count))
data.append(contentsOf: [UInt8](jsondata))
/*
uint32 opcode (0 or 1)
uint32 length (length)
byte[length] jsonData (??)
*/
return data
}
func handshake() {
connect()
// We should say "hello", with opcode handshake
let hello = encode(opcode: .handshake, message: "{\"v\":1,\"client_id\":\"\(appID)\"}")
print("Sending \(String.init(data: hello, encoding: .utf8))")
connection?.send(
content: hello,
completion: .contentProcessed({ error in
print("Error:", error?.localizedDescription)
})
)
}
func handshakev2() {
connect()
// We should say "hello", with opcode handshake
let hello = uint32encode(opcode: .handshake, message: "{\"v\":1,\"client_id\":\"\(appID)\"}")
print("Sending (V2) \(String.init(data: hello, encoding: .utf8))")
connection?.send(
content: hello,
completion: .contentProcessed({ error in
print("Error (V2):", error?.localizedDescription)
})
)
}
}

How to convert message received by Swift socket communication to String type

I want to do TCP communication using a library called SwiftSocket.
Below is the sample code of SwiftSocket.
func echoService(client: TCPClient) {
print("Newclient from:\(client.address)[\(client.port)]")
var d = client.read(1024*10)
client.send(data: d!)
client.close()
}
func testServer() {
let server = TCPServer(address: "127.0.0.1", port: 8080)
switch server.listen() {
case .success:
while true {
if var client = server.accept() {
echoService(client: client)
} else {
print("accept error")
}
}
case .failure(let error):
print(error)
}
}
I want to convert the received message to String in the third line of the above code
var d = client.read(1024*10)
How can I do that?
Try this;
if let string = String(bytes: d, encoding: .utf8)
{
print(string)
} else
{
print("not a valid UTF-8 sequence")
}

Swift RSA Encryption Public Key to Java Server is failing

I am trying to create public base64 key from RSA Private key using Security framework. Here is snippet.
let tag = "com.example.keys.mykey"
public extension SecKey {
static func generateBase64Encoded2048BitRSAKey() throws -> (private: String, public: String) {
let type = kSecAttrKeyTypeRSA
let attributes: [String: Any] =
[kSecAttrKeyType as String: type,
kSecAttrKeySizeInBits as String: 2048
]
var error: Unmanaged<CFError>?
guard let key = SecKeyCreateRandomKey(attributes as CFDictionary, &error),
let data = SecKeyCopyExternalRepresentation(key, &error) as Data?,
let publicKey = SecKeyCopyPublicKey(key),
let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
throw error!.takeRetainedValue() as Error
}
return (private: data.base64EncodedString(), public: publicKeyData.base64EncodedString())
}
}
do {
let (pvtKey, pubKey) = try SecKey.generateBase64Encoded2048BitRSAKey()
print(pubKey)
} catch let error {
print(error)
}
This is the output
MIIBCgKCAQEA1ZafTYboquQbCTZMEb1IqHKIr8wiDjdn6e0toRajZCQo9W5zuTlEuctrjJJQ08HcOuK3BPFRaFTUP1RBFvnba/T2S1Mc6WVX81b0DmKS8aPJ83TvvQlH3bZjVqFzndXJHJatcXRkZKlbidNQYxV9OYFCRLwgR5PBoJ1P5tp8f8735vIADOBL/93nFywODSjAWLXcyG5tUyRlRGX7eDodL7jqVOFxVMB7K9UOJehPuJQiheykyPSbBSLE6raZbpCHlranTLdihWYFs2tYbxzNrVbXzgKIxDDjrhDLVFvo3beudKQcLQkSO+m2LJIDT91zAnxVQ075AIn80ZHh5kdyQQIDAQAB
But this public key is not getting accepted by our Java server. It is throwing exception for the same.
Here is java snippet
public static void main(String[] args) {
String pubKey = "MIIBCgKCAQEA1ZafTYboquQbCTZMEb1IqHKIr8wiDjdn6e0toRajZCQo9W5zuTlEuctrjJJQ08HcOuK3BPFRaFTUP1RBFvnba/T2S1Mc6WVX81b0DmKS8aPJ83TvvQlH3bZjVqFzndXJHJatcXRkZKlbidNQYxV9OYFCRLwgR5PBoJ1P5tp8f8735vIADOBL/93nFywODSjAWLXcyG5tUyRlRGX7eDodL7jqVOFxVMB7K9UOJehPuJQiheykyPSbBSLE6raZbpCHlranTLdihWYFs2tYbxzNrVbXzgKIxDDjrhDLVFvo3beudKQcLQkSO+m2LJIDT91zAnxVQ075AIn80ZHh5kdyQQIDAQAB";
PublicKey key = getPublic(pubKey);
}
public static PublicKey getPublic(String key) {
PublicKey pbKey = null;
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
System.out.println(keyBytes.length);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
pbKey = factory.generatePublic(spec);
} catch (Exception e) {
e.printStackTrace();
}
return pbKey;
}
Here is the exception
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at Main.getPublic(Main.java:40)
at Main.main(Main.java:28)
But the online PEM parser website - https://8gwifi.org/PemParserFunctions.jsp is accepting this public key, which is using bouncycastle library in the background to validate this base64 encoded public key.
The exception is thrown because the ASN.1 DER encoding of an RSA public key generated on iOS is represented with the RSAPublicKey type as defined by PKCS#1, while Java (and many other languages and tools) expect the DER encoding to be represented with the SubjectPublicKeyInfo type as defined by X.509. There are of course two sides where this problem can be solved. And if you choose to convert the DER encoding of the RSA public key at the iOS side, you could use this project I recently published on GitHub. The structure you may be interested in is RSAPublicKeyExporter, which uses the SimpleASN1Writer for converting the DER encoding. The code snippet below shows how to use it:
import RSAPublicKeyExporter
let publicKeyData = ... // Get external representation of RSA public key some how
let x509EncodedKeyData = RSAPublicKeyExporter().toSubjectPublicKeyInfo(publicKeyData)
The answer I posted here contains some information that may be useful in case the exported key is fetched from the keychain.
Thanks guys. Due to some issues with bouncycastle library, we did not used it in backend service. So in iOS, we are including ASN1 header.
struct ASN1 {
let type: UInt8
let length: Int
let data: Data
init?(type: UInt8, arbitraryData data: Data) {
guard data.count > 4 else {
return nil
}
var result = data
let byteArray = [UInt8](result)
for (_, v) in byteArray.enumerated() {
if v == type { // ASN1 SEQUENCE Type
break
}
result = Data(result.dropFirst())
}
guard result.count > 4 else {
return nil
}
guard
let first = result.advanced(by: 0).first, // advanced start from 7.0
let second = result.advanced(by: 1).first,
let third = result.advanced(by: 2).first,
let fourth = result.advanced(by: 3).first
else {
return nil
}
var length = 0
switch second {
case 0x82:
length = ((Int(third) << 8) | Int(fourth)) + 4
break
case 0x81:
length = Int(third) + 3
break
default:
length = Int(second) + 2
break
}
guard result.startIndex + length <= result.endIndex else { // startIndex, endIndex start from 7.0
return nil
}
result = result[result.startIndex..<result.startIndex + length]
self.data = result
self.length = length
self.type = first
}
var last: ASN1? {
get {
var result: Data?
var dataToFetch = self.data
while let fetched = ASN1(type: self.type, arbitraryData: dataToFetch) {
if let range = data.range(of: fetched.data) {
if range.upperBound == data.count {
result = fetched.data
dataToFetch = Data(fetched.data.dropFirst())
} else {
dataToFetch = Data(data.dropFirst(range.upperBound))
}
} else {
break
}
}
return ASN1(type: type, arbitraryData: result!)
}
}
static func wrap(type: UInt8, followingData: Data) -> Data {
var adjustedFollowingData = followingData
if type == 0x03 {
adjustedFollowingData = Data([0]) + followingData // add prefix 0
}
let lengthOfAdjustedFollowingData = adjustedFollowingData.count
let first: UInt8 = type
var bytes = [UInt8]()
if lengthOfAdjustedFollowingData <= 0x80 {
let second: UInt8 = UInt8(lengthOfAdjustedFollowingData)
bytes = [first, second]
} else if lengthOfAdjustedFollowingData > 0x80 && lengthOfAdjustedFollowingData <= 0xFF {
let second: UInt8 = UInt8(0x81)
let third: UInt8 = UInt8(lengthOfAdjustedFollowingData)
bytes = [first, second, third]
} else {
let second: UInt8 = UInt8(0x82)
let third: UInt8 = UInt8(lengthOfAdjustedFollowingData >> 8)
let fourth: UInt8 = UInt8(lengthOfAdjustedFollowingData & 0xFF)
bytes = [first, second, third, fourth]
}
return Data(bytes) + adjustedFollowingData
}
static func rsaOID() -> Data {
var bytes = [UInt8]()
bytes = [0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00]
return Data(bytes)
}
}
Then called this during generating public key of RSA in swift.
class func RSAPublicKeyBitsFromKey(_ secKey:SecKey) -> Data? {
var queryPublicKey:[String:AnyObject] = [:]
queryPublicKey[kSecClass as String] = kSecClassKey as NSString
queryPublicKey[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA as NSString
if let publicKeyData = SwiftCrypto.publicKeyInData(queryPublicKey, secKey: secKey) {
let bitstringSequence = ASN1.wrap(type: 0x03, followingData: publicKeyData)
let oidData = ASN1.rsaOID()
let oidSequence = ASN1.wrap(type: 0x30, followingData: oidData)
let X509Sequence = ASN1.wrap(type: 0x30, followingData: oidSequence + bitstringSequence)
return X509Sequence
}
return nil
}
So, in this way, I had fixed this issue.

how to use AES with passpharse only

i'm working on app and after the android version done now i have started the ios version using swift 5. i'm using a function in my android version wish is written in java, and using this function i do encrypt/decrypt plain text using aes-256-cbc algorithm.
How can i use this method in CryptoSwift?
// Java Code
public class EncryptionDecryption {
String strResult;
public String Encrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
Log.v("GET Result from final:", results.toString());
strResult = Base64.encodeToString(results, 1);
return strResult;
}
public String Decrypt(String text, String key) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes = new byte[16];
byte[] b = key.getBytes("UTF-8");
int len = b.length;
if (len > keyBytes.length)
len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] results = new byte[text.length()];
//BASE64Decoder decoder = new BASE64Decoder();
try {
results = cipher.doFinal(Base64.decode(text, Base64.DEFAULT));
} catch (Exception e) {
Log.i("Error in Decryption", e.toString());
}
return new String(results, "UTF-8");
}
}
here is the solution
import UIKit
import CryptoSwift
class AES : NSObject {
public static func encrypt(text:String) -> String{
var salted = Data.init(count: 0)
var dx = Data.init(count: 0)
let salt = self.randomData(ofLength: 8)
let passpharse = "passpharse".bytes
let passAndSalt = passpharse + salt
while( salted.count < 48){
dx = MD5(messageData: dx + Data.init(_: passAndSalt))
salted += dx
}
let iv = salted.subdata(in: 32..<48)
let key = salted.subdata(in: 0..<32)
let bytesIV = [UInt8](iv as Data)
let bytesKey = [UInt8](key as Data)
do {
let encrypted = try AES(key: bytesKey, blockMode: CBC(iv: bytesIV), padding: .pkcs5)
let cipherText = try! encrypted.encrypt(text.bytes)
let saltData = "Salted__".bytes
let finalData = saltData + salt + cipherText
return finalData.toBase64()!
} catch {
return ""
}
}
private static func randomData(ofLength length: Int) -> Array<UInt8> {
var bytes = [UInt8](repeating: 0, count: length)
let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
if status == errSecSuccess {
return bytes
}
return Data.init(count: 0).bytes
}
private static func MD5(messageData: Data) -> Data {
let length = Int(CC_MD5_DIGEST_LENGTH)
//let messageData = string.data(using:.utf8)!
var digestData = Data(count: length)
_ = digestData.withUnsafeMutableBytes { digestBytes -> UInt8 in
messageData.withUnsafeBytes { messageBytes -> UInt8 in
if let messageBytesBaseAddress = messageBytes.baseAddress, let digestBytesBlindMemory = digestBytes.bindMemory(to: UInt8.self).baseAddress {
let messageLength = CC_LONG(messageData.count)
CC_MD5(messageBytesBaseAddress, messageLength, digestBytesBlindMemory)
}
return 0
}
}
return digestData
}
}

Reading an InputStream into a Data object

In Swift 3.x, we usually handle binary data using Data; from it you can generate most other important types, and there are useful functions on it.
But how do I create a Data from an InputStream? Is there a nice way?
I could not find a nice way. We can create a nice-ish wrapper around the unsafe stuff:
extension Data {
init(reading input: InputStream) throws {
self.init()
input.open()
defer {
input.close()
}
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer {
buffer.deallocate()
}
while input.hasBytesAvailable {
let read = input.read(buffer, maxLength: bufferSize)
if read < 0 {
//Stream error occured
throw input.streamError!
} else if read == 0 {
//EOF
break
}
self.append(buffer, count: read)
}
}
}
This is for Swift 5. Find full code with test (and a variant that reads only some of the stream) here.
above the code, It can be infinite loop.
When I convert httpbodyInpustream to data, it happend.
So I add a condition.
extension Data {
init(reading input: InputStream) {
self.init()
input.open()
let bufferSize = 1024
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
while input.hasBytesAvailable {
let read = input.read(buffer, maxLength: bufferSize)
if (read == 0) {
break // added
}
self.append(buffer, count: read)
}
buffer.deallocate(capacity: bufferSize)
input.close()
}
}