Signature Version 4 Issue on Swift - swift

I really try hard to solve this problem on my project. But I can't.
I try to sign URLRequest headers and body by Amazon AWS Signature Version 4. And send it to server with Alamofire SDK.
But only headers without body work correctly.
I dunno why I get the response "403 forbidden" from server when I put httpBody into URLRequest.
Here is my source.
...
var request = URLRequest(url: convUrl)
if let jsonString = String(data: jsonData, encoding: .utf8) {
AWS4Signer().aws4Sign(request: &request, method: httpMethodType, payload: jsonString, query: [:], path: url.replacingOccurrences(of: self.url.host, with: ""))
}
AF.request(request).responseData { response in
}
func aws4Sign(request: inout URLRequest, method: HttpMethodType, payload: String?, query: [String:String]?, path: String) {
var headers = [String:String]()
let requested_date_time = self.getAmzDate(date: Date())
headers["x-amz-date"] = requested_date_time
// x-amz-content-sha256
var payload_hash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // empty
if let payload {
let utf8Data = payload.data(using: .utf8) ?? Data()
payload_hash = SHA256.hash(data: utf8Data).hexDigest()
if !payload.isEmpty {
headers["x-amz-content-sha256"] = payload_hash
request.httpBody = utf8Data
}
}
let canonical_querystring = ""
headers["host"] = "hosthost.com"
let sortedHeaders = headers.filter { ["host", "x-amz-date", "x-amz-content-sha256"].contains($0.key) }
.sorted { $0.key < $1.key }
let canonical_headers = sortedHeaders.map { "\($0.key.lowercased()):\($0.value)" }.joined(separator: "\n") + "\n"
let signed_headers = sortedHeaders.map { $0.key.lowercased() }.joined(separator: ";")
let canonical_uri = path
let canonical_request = "\(method.rawValue)\n\(canonical_uri)\n\(canonical_querystring)\n\(canonical_headers)\n\(signed_headers)\n\(payload_hash)"
let hashed_canonical_request = SHA256.hash(data: canonical_request.data(using: .utf8)!).hexDigest()
let algorithm = "AWS4-HMAC-SHA256"
let nowDate = Date().getString(format: "YYYYMMdd", timeZone: TimeZone(identifier: "UTC")!)
let credential_scope = "\(nowDate)/\(Const.key.awsRegion)/\(Const.key.awsServiceType)/aws4_request"
let string_to_sign = "\(algorithm)\n\(requested_date_time)\n\(credential_scope)\n\(hashed_canonical_request)"
let signing_key = self.getSignatureKey(key: Const.key.awsSecretAccessKey, dateStamp: nowDate, regionName: Const.key.awsRegion, serviceName: Const.key.awsServiceType)
let signature = self.sign(key: signing_key, msg: string_to_sign).hexDigest()
let authorization_header = "\(algorithm) Credential=\(Const.key.awsAccessKeyID)/\(credential_scope), SignedHeaders=\(signed_headers), Signature=\(signature)"
request.addValue(requested_date_time, forHTTPHeaderField: "X-amz-date")
request.addValue(authorization_header, forHTTPHeaderField: "Authorization")
}
func getSignatureKey(key: String, dateStamp: String, regionName: String, serviceName: String) -> Data {
let keyData = Data("AWS4\(key)".utf8)
let dateStampData = Data(dateStamp.utf8)
let regionNameData = Data(regionName.utf8)
let serviceNameData = Data(serviceName.utf8)
let signingData = Data("aws4_request".utf8)
var symmetricKey = SymmetricKey(data: keyData)
let dateSHA256 = HMAC<SHA256>.authenticationCode(for: dateStampData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(dateSHA256))
let regionSHA256 = HMAC<SHA256>.authenticationCode(for: regionNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(regionSHA256))
let serviceNameSHA256 = HMAC<SHA256>.authenticationCode(for: serviceNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(serviceNameSHA256))
let signingSHA256 = HMAC<SHA256>.authenticationCode(for: signingData, using: symmetricKey)
let skeyString = keyData.map { String(format: "%02hhx", $0) }.joined()
let kDateString = Data(dateSHA256).map { String(format: "%02hhx", $0) }.joined()
let kRegionString = Data(regionSHA256).map { String(format: "%02hhx", $0) }.joined()
let kServiceString = Data(serviceNameSHA256).map { String(format: "%02hhx", $0) }.joined()
let kSigningString = Data(signingSHA256).map { String(format: "%02hhx", $0) }.joined()
return Data(signingSHA256)
}
func sign(key: Data, msg: String) -> Data {
let hmac = HMAC<SHA256>.authenticationCode(for: msg.data(using: .utf8)!, using: SymmetricKey(data: key))
return Data(hmac)
}
func getAmzDate(date: Date) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
formatter.timeZone = TimeZone(identifier: "UTC")
return formatter.string(from: date)
}
func getCanonicalQuerystring(query: [String: String]) -> String {
let sortedParameters = query.sorted { $0.0 < $1.0 }
let encodedQueryParameters = sortedParameters.map { (key, value) in
return "\(key.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")=\(value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")"
}
let canonicalQuerystring = encodedQueryParameters.joined(separator: "&")
return canonicalQuerystring
}
The "Authorization" data has it.
"AWS4-HMAC-SHA256 Credential=ACCESS_KEY_ID/20230213/ap-northeast-2/execute-api/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=cfa667aa18472d3d5a419f67aa2c321977428abac391a33f9f3e31839bcb4665"
ACCESS_KEY_ID is my access_key_id of AWS.
So far, only the POST method is tested.

Related

Derive An AWS Signing Key For Signature Version 4

I am attempting to generate a signing key for AWS according to AWS's documentation here for an iOS application. The documentation is pretty good however, it doesn't provide an example using Swift. Apple provides CryptoKit which should be the right framework, but I haven't been able to puzzle it out.
Ruby Example
def getSignatureKey key, dateStamp, regionName, serviceName
kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + key, dateStamp)
kRegion = OpenSSL::HMAC.digest('sha256', kDate, regionName)
kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
kSigning
end
Sample Inputs from AWS Docs
key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'
Should output
kSecret = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'
My attempt (note you have to append AWS to the key according to the docs)
import Foundation
import CryptoKit
let key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
let dateStamp = "20120215"
let regionName = "us-east-1"
let serviceName = "iam"
let keyData = Data("AWS\(key)".utf8)
let symmetricKey = SymmetricKey(data: keyData)
let dateStampData = Data(dateStamp.utf8)
let signature = HMAC<SHA256>.authenticationCode(for: dateStampData, using: symmetricKey)
let skeyString = keyData.map { String(format: "%02hhx", $0) }.joined()
print("kSecret \t= \(skeyString)")
let kDateString = Data(signature).map { String(format: "%02hhx", $0) }.joined()
print("kDate \t\t= \(kDateString)")
The first one is correct, so it seems I have the initial key correct, but when trying to apply it to dateStamp it doesn't match.
Outputs
kSecret = 415753774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559
kDate = 2226579f8b317a03ec325a8c8b3d27cf465ce52787455e1880039824b4ba0e25
Of course the moment I post the question I find the issue. The original issue was I was appending AWS instead of AWS4 the string appeared to be correct for kSecret because I was looking at the first set and last set of digits. Here is the solution for anyone looking to do the same.
import Foundation
import CryptoKit
let key = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
let dateStamp = "20120215"
let regionName = "us-east-1"
let serviceName = "iam"
let keyData = Data("AWS4\(key)".utf8)
let dateStampData = Data(dateStamp.utf8)
let regionNameData = Data(regionName.utf8)
let serviceNameData = Data(serviceName.utf8)
let signingData = Data("aws4_request".utf8)
var symmetricKey = SymmetricKey(data: keyData)
let dateSHA256 = HMAC<SHA256>.authenticationCode(for: dateStampData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(dateSHA256))
let regionSHA256 = HMAC<SHA256>.authenticationCode(for: regionNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(regionSHA256))
let serviceNameSHA256 = HMAC<SHA256>.authenticationCode(for: serviceNameData, using: symmetricKey)
symmetricKey = SymmetricKey(data: Data(serviceNameSHA256))
let signingSHA256 = HMAC<SHA256>.authenticationCode(for: signingData, using: symmetricKey)
let skeyString = keyData.map { String(format: "%02hhx", $0) }.joined()
print("kSecret \t= \(skeyString)")
let kDateString = Data(dateSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kDate \t\t= \(kDateString)")
let kRegionString = Data(regionSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kRegion \t= \(kRegionString)")
let kServiceString = Data(serviceNameSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kService \t= \(kServiceString)")
let kSigningString = Data(signingSHA256).map { String(format: "%02hhx", $0) }.joined()
print("kSigning \t= \(kSigningString)")
Outputs
kSecret = 41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559
kDate = 969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d
kRegion = 69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c
kService = f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa
kSigning = f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d

WEBRTC: Local streams are visible on OPENVIDU WEB(other Participant) end But Remote Streams are coming NIL on my end locally swift iOS

I am using OpenVidu for Video Chat, Using this Repo https://github.com/OpenVidu/openvidu-ios-app I know this has bugs, but I have to use this as This is working fine at Android and WEB. I am able to make it working where my Local video can be seen on OpenVidu Web but Remote or other person who has joined session from Web whose video(Video Stream and Audio Stream) is not coming at my end. however I can see Remote Participant ID and Name at my end when a user joined the session.
Attached Image is the screen shot showing Remote streams are nil.
Below is the WebSocketListener Class I am using, I have updated pods so have to update delegates as well.
//
// WebSocketListener.swift
// WebRTCapp
//
// Created by Sergio Paniego Blanco on 01/05/2018.
// Copyright © 2018 Sergio Paniego Blanco. All rights reserved.
//
import Foundation
import Starscream
import WebRTC
class WebSocketListener: WebSocketDelegate {
let JSON_RPCVERSION = "2.0"
let useSSL = true
var socket: WebSocket
var helloWorldTimer: Timer?
var id = 0
var url: String
var sessionName: String
var participantName: String
var localOfferParams: [String: String]?
var iceCandidatesParams: [[String:String]]?
var userId: String?
var remoteParticipantId: String?
var participants: [String: RemoteParticipant]
var localPeer: RTCPeerConnection?
var peersManager: PeersManager
var token: String
var views: [UIView]!
var names: [UILabel]!
var key: String?
init(url: String, sessionName: String, participantName: String, peersManager: PeersManager, token: String, views: [UIView], names: [UILabel]) {
self.url = url
self.sessionName = sessionName
self.participantName = participantName
self.participants = [String: RemoteParticipant]()
self.peersManager = peersManager
self.token = token
var request = URLRequest(url: URL(string: url)!)
request.timeoutInterval = 20
socket = WebSocket(request: request)
socket.delegate = self
socket.connect()
self.localPeer = self.peersManager.localPeer
self.iceCandidatesParams = []
self.views = views
self.names = names
}
// func websocketDidConnect(socket: WebSocketClient) {
// print("Connected")
// pingMessageHandler()
// var joinRoomParams: [String: String] = [:]
// joinRoomParams["recorder"] = "false"
// joinRoomParams["platform"] = "iOS"
// joinRoomParams[JSONConstants.Metadata] = "{\"clientData\": \"" + "iOSUser" + "\"}"
// joinRoomParams["secret"] = "MY_SECRET"
// joinRoomParams["session"] = sessionName
// joinRoomParams["token"] = token
// sendJson(method: "joinRoom", params: joinRoomParams)
// if localOfferParams != nil {
// sendJson(method: "publishVideo",params: localOfferParams!)
// }
// }
func pingMessageHandler() {
helloWorldTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(WebSocketListener.doPing), userInfo: nil, repeats: true)
doPing()
}
#objc func doPing() {
var pingParams: [String: String] = [:]
pingParams["interval"] = "5000"
sendJson(method: "ping", params: pingParams)
socket.write(ping: Data())
}
var isConnected = false
func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
print("Disconnect: " + error.debugDescription)
}
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(let headers):
isConnected = true
print("websocket is connected: \(headers)")
pingMessageHandler()
var joinRoomParams: [String: String] = [:]
joinRoomParams["recorder"] = "false"
joinRoomParams["platform"] = "iOS"
joinRoomParams[JSONConstants.Metadata] = "{\"clientData\": \"\(self.participantName)\"}"
joinRoomParams["secret"] = ""
joinRoomParams["session"] = sessionName
joinRoomParams["token"] = token
sendJson(method: "joinRoom", params: joinRoomParams)
if localOfferParams != nil {
sendJson(method: "publishVideo",params: localOfferParams!)
}
case .disconnected(let reason, let code):
isConnected = false
print("websocket is disconnected: \(reason) with code: \(code)")
case .text(let string):
print("Received text: \(string)")
let data = string.data(using: .utf8)!
do {
let json: [String: Any] = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as! [String : Any]
if json[JSONConstants.Result] != nil {
handleResult(json: json)
} else {
handleMethod(json: json)
}
} catch let error as NSError {
print("ERROR parsing JSON: ", error)
}
case .binary(let data):
print("Received data: \(data.count)")
case .ping(_):
break
case .pong(_):
break
case .viabilityChanged(_):
break
case .reconnectSuggested(_):
break
case .cancelled:
isConnected = false
case .error(let error):
isConnected = false
print(error.debugDescription)
}
}
// func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
// print("Recieved message: " + text)
// let data = text.data(using: .utf8)!
// do {
// let json: [String: Any] = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as! [String : Any]
//
// if json[JSONConstants.Result] != nil {
// handleResult(json: json)
// } else {
// handleMethod(json: json)
// }
//
// } catch let error as NSError {
// print("ERROR parsing JSON: ", error)
// }
// }
func handleResult(json: [String: Any]) {
let result: [String: Any] = json[JSONConstants.Result] as! [String: Any]
if result[JSONConstants.SdpAnswer] != nil {
saveAnswer(json: result)
} else if result[JSONConstants.SessionId] != nil {
if result[JSONConstants.Value] != nil {
let value = result[JSONConstants.Value] as! [[String:Any]]
if !value.isEmpty {
addParticipantsAlreadyInRoom(result: result)
}
self.userId = result[JSONConstants.Id] as? String
for var iceCandidate in iceCandidatesParams! {
iceCandidate["endpointName"] = self.userId
sendJson(method: "onIceCandidate", params: iceCandidate)
}
}
} else if result[JSONConstants.Value] != nil {
print("pong")
} else {
print("Unrecognized")
}
}
func addParticipantsAlreadyInRoom(result: [String: Any]) {
let values = result[JSONConstants.Value] as! [[String: Any]]
for participant in values {
print(participant[JSONConstants.Id]!)
self.remoteParticipantId = participant[JSONConstants.Id]! as? String
let remoteParticipant = RemoteParticipant()
remoteParticipant.id = participant[JSONConstants.Id] as? String
let metadataString = participant[JSONConstants.Metadata] as! String
let data = metadataString.data(using: .utf8)!
do {
if let metadata = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? Dictionary<String,Any>
{
remoteParticipant.participantName = metadata["clientData"] as? String
}
} catch let error as NSError {
print(error)
}
self.participants[remoteParticipant.id!] = remoteParticipant
self.peersManager.createRemotePeerConnection(remoteParticipant: remoteParticipant)
let mandatoryConstraints = ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "true"]
let sdpConstraints = RTCMediaConstraints(mandatoryConstraints: mandatoryConstraints, optionalConstraints: nil)
remoteParticipant.peerConnection!.offer(for: sdpConstraints, completionHandler: {(sessionDescription, error) in
print("Remote Offer: " + error.debugDescription)
self.participants[remoteParticipant.id!]!.peerConnection!.setLocalDescription(sessionDescription!, completionHandler: {(error) in
print("Remote Peer Local Description set " + error.debugDescription)
})
var remoteOfferParams: [String:String] = [:]
remoteOfferParams["sdpOffer"] = sessionDescription!.sdp
remoteOfferParams["sender"] = self.remoteParticipantId! + "_CAMERA"
self.sendJson(method: "receiveVideoFrom", params: remoteOfferParams)
})
self.peersManager.remotePeer!.delegate = self.peersManager
}
}
func saveAnswer(json: [String:Any]) {
let sessionDescription = RTCSessionDescription(type: RTCSdpType.answer, sdp: json["sdpAnswer"] as! String)
if localPeer == nil {
self.localPeer = self.peersManager.localPeer
}
if (localPeer!.remoteDescription != nil) {
participants[remoteParticipantId!]!.peerConnection!.setRemoteDescription(sessionDescription, completionHandler: {(error) in
print("Remote Peer Remote Description set: " + error.debugDescription)
if self.peersManager.remoteStreams.count >= self.participants.count {
DispatchQueue.main.async {
print("Count: " + self.participants.count.description)
if UIDevice().userInterfaceIdiom == .phone && UIScreen.main.nativeBounds.height == 2436 {
let renderer = RTCEAGLVideoView(frame: self.views[self.participants.count-1].frame)
let videoTrack = self.peersManager.remoteStreams[self.participants.count-1].videoTracks[0]
videoTrack.add(renderer)
// Add the view and name to the first free space available
var index = 0
while (index < 2 && !(self.names[index].text?.isEmpty)!) {
index += 1
}
if index < 2 {
self.names[index].text = self.participants[self.remoteParticipantId!]?.participantName
self.names[index].backgroundColor = UIColor.black
self.names[index].textColor = UIColor.white
self.embedView(renderer, into: self.views[index])
self.participants[self.remoteParticipantId!]?.index = index
self.views[index].bringSubview(toFront: self.names[index])
}
}else
{
#if arch(arm64)
let renderer = RTCMTLVideoView(frame: self.views[self.participants.count-1].frame)
#else
let renderer = RTCEAGLVideoView(frame: self.views[self.participants.count-1].frame)
#endif
let videoTrack = self.peersManager.remoteStreams[self.participants.count-1].videoTracks[0]
videoTrack.add(renderer)
// Add the view and name to the first free space available
var index = 0
while (index < 2 && !(self.names[index].text?.isEmpty)!) {
index += 1
}
if index < 2 {
self.names[index].text = self.participants[self.remoteParticipantId!]?.participantName
self.names[index].backgroundColor = UIColor.black
self.names[index].textColor = UIColor.white
self.embedView(renderer, into: self.views[index])
self.participants[self.remoteParticipantId!]?.index = index
self.views[index].bringSubview(toFront: self.names[index])
}
}
}
}
})
} else {
localPeer!.setRemoteDescription(sessionDescription, completionHandler: {(error) in
print("Local Peer Remote Description set: " + error.debugDescription)
})
}
}
func handleMethod(json: Dictionary<String,Any>) {
if json[JSONConstants.Params] != nil {
let method = json[JSONConstants.Method] as! String
let params = json[JSONConstants.Params] as! Dictionary<String, Any>
switch method {
case JSONConstants.IceCandidate:
iceCandidateMethod(params: params)
case JSONConstants.ParticipantJoined:
participantJoinedMethod(params: params)
case JSONConstants.ParticipantPublished:
participantPublished(params: params)
case JSONConstants.ParticipantLeft:
participantLeft(params: params)
default:
print("Error handleMethod, " + "method '" + method + "' is not implemented")
}
}
}
func iceCandidateMethod(params: Dictionary<String, Any>) {
// if (params["endpointName"] as? String == userId) {
// saveIceCandidate(json: params, endPointName: nil)
// } else {
// saveIceCandidate(json: params, endPointName: params["endpointName"] as? String)
// }
DispatchQueue.main.async {
if params["senderConnectionId"] != nil {
self.key = "senderConnectionId"
} else {
self.key = "endpointName"
}
if (params[self.key ?? ""] as? String == self.userId) {
self.saveIceCandidate(json: params, endPointName: params["endpointName"] as? String)
} else {
self.saveIceCandidate(json: params, endPointName: params[self.key ?? ""] as? String)
}
}
}
// func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
// print("Received data: " + data.description)
// }
func participantJoinedMethod(params: Dictionary<String, Any>) {
let remoteParticipant = RemoteParticipant()
remoteParticipant.id = params[JSONConstants.Id] as? String
self.participants[params[JSONConstants.Id] as! String] = remoteParticipant
let metadataString = params[JSONConstants.Metadata] as! String
let data = metadataString.data(using: .utf8)!
do {
if let metadata = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? Dictionary<String,Any>
{
remoteParticipant.participantName = metadata["clientData"] as? String
self.peersManager.createRemotePeerConnection(remoteParticipant: remoteParticipant)
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
participantPublished(params: params)
}
func participantPublished(params: Dictionary<String, Any>) {
self.remoteParticipantId = params[JSONConstants.Id] as? String
print("ID: " + remoteParticipantId!)
let remoteParticipantPublished = participants[remoteParticipantId!]!
let mandatoryConstraints = ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "true"]
remoteParticipantPublished.peerConnection!.offer(for: RTCMediaConstraints.init(mandatoryConstraints: mandatoryConstraints, optionalConstraints: nil), completionHandler: { (sessionDescription, error) in
remoteParticipantPublished.peerConnection!.setLocalDescription(sessionDescription!, completionHandler: {(error) in
print("Remote Peer Local Description set")
})
var remoteOfferParams: [String: String] = [:]
remoteOfferParams["sdpOffer"] = sessionDescription!.description
remoteOfferParams["sender"] = remoteParticipantPublished.id! + "_webcam"
self.sendJson(method: "receiveVideoFrom", params: remoteOfferParams)
})
self.peersManager.remotePeer!.delegate = self.peersManager
}
func participantLeft(params: Dictionary<String, Any>) {
print("participants", participants)
print("params", params)
let participantId = params["connectionId"] as! String
participants[participantId]!.peerConnection!.close()
if UIDevice().userInterfaceIdiom == .phone && UIScreen.main.nativeBounds.height == 2436 {
let renderer = RTCEAGLVideoView(frame: self.views[0].frame)
//REMOVE VIEW
if(self.peersManager.remoteStreams.count > 0){
let videoTrack = self.peersManager.remoteStreams[0].videoTracks[0]
videoTrack.remove(renderer)
if let index = self.participants.keys.index(of: participantId) {
let i = participants.distance(from: participants.startIndex, to: index)
self.views[i].willRemoveSubview(renderer)
self.names[i].text = ""
self.names[i].backgroundColor = UIColor.clear
}
}
participants.removeValue(forKey: participantId)
}else
{
#if arch(arm64)
let renderer = RTCMTLVideoView(frame: self.views[0].frame)
#else
let renderer = RTCEAGLVideoView(frame: self.views[self.participants.count-1].frame)
#endif
//REMOVE VIEW
if(self.peersManager.remoteStreams.count > 0){
let videoTrack = self.peersManager.remoteStreams[0].videoTracks[0]
videoTrack.remove(renderer)
if let index = self.participants.keys.index(of: participantId) {
let i = participants.distance(from: participants.startIndex, to: index)
self.views[i].willRemoveSubview(renderer)
self.names[i].text = ""
self.names[i].backgroundColor = UIColor.clear
}
participants.removeValue(forKey: participantId)
}
}
}
func saveIceCandidate(json: Dictionary<String, Any>, endPointName: String?) {
let iceCandidate = RTCIceCandidate(sdp: json["candidate"] as! String, sdpMLineIndex: json["sdpMLineIndex"] as! Int32, sdpMid: json["sdpMid"] as? String)
if (endPointName == nil || participants[endPointName!] == nil) {
self.localPeer = self.peersManager.localPeer
self.localPeer!.add(iceCandidate)
} else {
participants[endPointName!]!.peerConnection!.add(iceCandidate)
}
}
func sendJson(method: String, params: [String: String]) {
let json: NSMutableDictionary = NSMutableDictionary()
json.setValue(method, forKey: JSONConstants.Method)
json.setValue(id, forKey: JSONConstants.Id)
id += 1
json.setValue(params, forKey: JSONConstants.Params)
json.setValue(JSON_RPCVERSION, forKey: JSONConstants.JsonRPC)
let jsonData: NSData
do {
jsonData = try JSONSerialization.data(withJSONObject: json, options: JSONSerialization.WritingOptions()) as NSData
let jsonString = NSString(data: jsonData as Data, encoding: String.Encoding.utf8.rawValue)! as String
print("Sending = \(jsonString)")
socket.write(string: jsonString)
} catch _ {
print ("JSON Failure")
}
}
func addIceCandidate(iceCandidateParams: [String: String]) {
iceCandidatesParams!.append(iceCandidateParams)
}
func embedView(_ view: UIView, into containerView: UIView) {
containerView.addSubview(view)
containerView.backgroundColor = UIColor.white.withAlphaComponent(0.8)
view.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
}
}
Any One who has used this code, needed help here. I have Participant ID what is the way to get Audio Video streams, Do I need to make some connection(Peer connection)? or I will get these streams in the socket connection response only.

Decrypt data using AES.GCM.SealedBox in Swift

I am trying to decrypt data using AES.GCM.The encrypted data works fine but when I try to decrypt the data using the same key it gives authentication error.
Below is the code to decrypt
func decryptData(decryptToData: Data, key: SymmetricKey) -> String {
var decryptedString: String!
let combinedData = decryptToData // Previous sealed bo
let sealedBoxToOpen = try! AES.GCM.SealedBox(combined: decryptToData)
if let decryptedData = try? AES.GCM.open(sealedBoxToOpen, using: key) {
decryptedString = String(data: decryptedData, encoding: .utf8)!
print(decryptedString)
} else {
print("error", CryptoKitError.self)
// Ouch, doSomething() threw an error.
}
return decryptedString
}
The following is my encryption code
let iv = AES.GCM.Nonce()
var encryptedData: Data!
let key = SymmetricKey(size: .bits128)
func encryptData(encryptString: String, key: SymmetricKey) -> Data {
var encryptedData: Data?
do {
let datatoEncrypt1 = encryptString.data(using: .utf8)!
let mySealedBox = try AES.GCM.seal(datatoEncrypt1, using: key, nonce: iv)
encryptedData = mySealedBox.combined
} catch {
print("Error")
}
return encryptedData
}
import XCTest
import CryptoKit
import Foundation
class CryptoKitUnitTest: XCTestCase {
func testEncryptandDecrypt(){
let secret = "my-256-bit-secret-my-secret-my-s"
let key = SymmetricKey(data: secret.data(using: .utf8)!)
let plain = "Say hello to my little friend!"
let nonce = try! AES.GCM.Nonce(data: Data(base64Encoded: "fv1nixTVoYpSvpdA")!)
// Encrypt
let sealedBox = try! AES.GCM.seal(plain.data(using: .utf8)!, using: key, nonce: nonce)
let ciphertext = sealedBox.ciphertext.base64EncodedString()
print("ciphertext: \(ciphertext)") // bWtTZkPAu7oXpQ3QpHvoTvc4NQgDTIycXHFJWvjk
let sealedBoxToDecrypt = try! AES.GCM.SealedBox(nonce: nonce,
ciphertext: Data(base64Encoded: ciphertext)!,
tag: sealedBox.tag)
let decrypted = try! AES.GCM.open(sealedBoxToDecrypt, using: key)
print(String(decoding: decrypted, as: UTF8.self))
}
func testEncryptandDecryptFirstWay() {
let keyStr = "d5a423f64b607ea7c65b311d855dc48f36114b227bd0c7a3d403f6158a9e4412"
let key = SymmetricKey(data: Data(hex:keyStr))
let nonceData = Data(hex: "131348c0987c7eece60fc0bc")
let nonce: AES.GCM.Nonce = try! AES.GCM.Nonce(data: nonceData)
let plain = "This is first cypto graphy method"
var decyptedStr = ""
if let encyptedData = plain.asData.encrypt(nonce: nonce, key: key) {
decyptedStr = encyptedData.decrypt(nonce: nonce, key: key)
}
XCTAssertEqual(plain, decyptedStr)
}
}
extension Data {
func encrypt(nonce: AES.GCM.Nonce, key: SymmetricKey) ->Data?{
// Encrypt
do {
let sealedBox = try AES.GCM.seal(self, using: key, nonce: nonce)
let cipherText = sealedBox.ciphertext.base64EncodedString()
let tag = sealedBox.tag
let tagPlusCipherText = tag + cipherText.asData
return tagPlusCipherText
}
catch let exceptionInfo {
debugPrint("Encrypt exception Info: \(exceptionInfo)")
}
return nil
}
func decrypt(nonce: AES.GCM.Nonce, key: SymmetricKey) -> String{
let tag = self.subtract(0, 16)
let cipherTextData = self.subtract(tag.count, self.count - tag.count)
let cipherText = cipherTextData.asString
// Decrypt
var decodeStr: String = ""
do {
let sealedBoxToDecrypt = try AES.GCM.SealedBox(nonce: nonce,
ciphertext: Data(base64Encoded: cipherText)!,
tag: tag)
let decrypted = try AES.GCM.open(sealedBoxToDecrypt, using: key)
decodeStr = String(decoding: decrypted, as: UTF8.self)
} catch let exceptionInfo {
debugPrint("Decrypt exception info: \(exceptionInfo)")
}
return decodeStr
}
public func subtract(_ start: Int, _ length: Int) ->Data {
precondition(self.count >= start + length,
"Invalid data range range. trying to find out of bound data")
let allBytes = Array(Data(bytes: self.bytes, count: self.count))
let partBytes = Array(allBytes[start..<start + length])
let dataPart = Data(bytes: partBytes, count: partBytes.count)
return dataPart
}
var asString: String {
let str = String(decoding: self, as: UTF8.self)
return str
}
}
extension String {
var asData: Data {
return self.data(using: .utf8) ?? Data()
}
}

Swift / Apple Sign In - Type HASH256 has no member hash

Issue: "Type HASH256 has no member hash"
Background: Trying to implement Apple sign in with Firebase on Swift
Tried to resolve the issue with the following:
-all pods update
-import CommonCrypto + import CryptoKit
-clean build folder / build
The error is still present
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
let charset: Array<Character> =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
}
return random
}
randoms.forEach { random in
if length == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
//Start Apple's sign-in flow
// Unhashed nonce.
fileprivate var currentNonce: String?
#available(iOS 13, *)
func startSignInWithAppleFlow() {
let nonce = randomNonceString()
currentNonce = nonce
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self as! ASAuthorizationControllerDelegate
authorizationController.presentationContextProvider = self as! ASAuthorizationControllerPresentationContextProviding
authorizationController.performRequests()
}
#available(iOS 13, *)
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
return String(format: "%02x", $0)
}.joined()
return hashString
}
// func SHA256() -> String {
//
// let data = self.data(using: String.Encoding.utf8)
// let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
// CC_SHA256(((data! as NSData)).bytes, CC_LONG(data!.count), res?.mutableBytes.assumingMemoryBound(to: UInt8.self))
// let hashedString = "\(res!)".replacingOccurrences(of: "", with: "").replacingOccurrences(of: " ", with: "")
// let badchar: CharacterSet = CharacterSet(charactersIn: "\"<\",\">\"")
// let cleanedstring: String = (hashedString.components(separatedBy: badchar) as NSArray).componentsJoined(by: "")
// return cleanedstring
//
// }
}
//Apple extension
#available(iOS 13.0, *)
extension AuthViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
// Initialize a Firebase credential.
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
accessToken: nonce)
// Sign in with Firebase.
Auth.auth().signIn(with: credential) { (authResult, error) in
if (error != nil) {
// Error. If error.code == .MissingOrInvalidNonce, make sure
// you're sending the SHA256-hashed nonce as a hex string with
// your request to Apple.
print(error?.localizedDescription)
return
}
// User is signed in to Firebase with Apple.
// ...
}
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// Handle error.
print("Sign in with Apple errored: \(error)")
}
}
Image of error
I encountered the same problem, I spend two days figured it out!
The reason is we mistaken installed 'CryptoKit' in our Podfile. which apple also has a build-in 'CryptoKit' for iOS version 13+.
Solution :
1.deleted pod ''CryptoKit' in our pod file.
2. pod install
after that, we will use apple build in 'CryptoKit' which has the build-in method hash.
This should work: add this outside of your class and then instead of request.nonce = sha256(nonce), type request.nonce = nonce.sha256()
extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return hexStringFromData(input: digest(input: stringData as NSData))
}
return ""
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
credit

how to get the array count if string is empty from nsdictionary?

this is my respone
{
activeFromDate = "01/01/2017";
terminateDate = “02/05/2019”;
}
{
activeFromDate = "01/01/2013";
terminateDate = "<null>";
}
{
activeFromDate = "01/01/2017";
terminateDate = "02/05/2018";
}
{
activeFromDate = "07/01/2012";
terminateDate = "<null>";
}
{
activeFromDate = "01/01/2017";
terminateDate = "02/05/2019";
}
this is my code
let answerArray = NSMutableArray()
for i in 0..<arr.count
{
let dict = Dictionary[i] as! NSDictionary
let name1 = dict.value(forKey:"terminateDate") as! String
if(name1 == "")
{
print (answerArray.add(dict))
}
}
this is my nsdictionary response
how to get array count if terminatedDate is null and terminatedDate is greater than today date?
I have created a sample to read json file placed in your project. and have integrated the code as per your requirement.
Json File
{"Status":"success","Data":[
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2019"},
{"activeFromDate":"01/01/2013","terminateDate":null},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2018"},
{"activeFromDate":"07/01/2012","terminateDate":null},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2016"},
{"activeFromDate":"01/01/2017","terminateDate":"02/05/2019"}
]
}
In your View Controller
import UIKit
class HomeViewController: UIViewController {
let answerArray = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
jsonParsingFromFile()
}
func jsonParsingFromFile()
{
let path: NSString = Bundle.main.path(forResource: "tjw", ofType: "json")! as NSString
let data : NSData = try! NSData(contentsOfFile: path as String, options: NSData.ReadingOptions.dataReadingMapped)
self.startParsing(data: data)
}
func startParsing(data :NSData)
{
let dict: NSDictionary!=(try! JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.mutableContainers)) as? NSDictionary
//print(dict)
guard let dataArr:NSArray = dict.value(forKey: "Data") as? NSArray else {
return
}
print(dataArr.count)
for i in 0..<dataArr.count{
guard let currentDict:NSDictionary = dataArr[i] as? NSDictionary else {
return
}
let activeFromDateStr:String = currentDict.value(forKey: "activeFromDate") as? String ?? ""
let terminateDateStr:String = currentDict.value(forKey: "terminateDate") as? String ?? ""
print(activeFromDateStr, terminateDateStr)
if terminateDateStr != ""{
let date2 = convertToDate(dateStr: terminateDateStr)
let today = Date()
if date2>today{
answerArray.add(currentDict)
}
}else{
answerArray.add(currentDict)
}
}
print(answerArray)
}
func convertToDate(dateStr:String)->Date{
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy"
dateFormatter.locale = Locale(identifier: "en_IN")
let date = dateFormatter.date(from: dateStr)!
return date
}
}