How to parse Hex with String, Float - swift

I have binary files which containing names of place and coordinates ( latitude, longitude ), whenever I parse it to String using encoding .ascii it won't parse it well. I assume that parsing from Float values (coordinates) failing.
Reading InputStream
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)
self.append(buffer, count: read)
}
buffer.deallocate()
input.close()
}
}
File to parse
let filepath = Bundle.main.path(forResource: "MN", ofType: "dat")
let data = Data.init(reading: InputStream(fileAtPath: filepath)!)
let parsedData = String.init(data: data, encoding: .ascii)
Any ideas how could I parse it in correct way ?
For example Java ObjectInputStream have methods called:
inputStreamObj.readUTF()
inputStreamObj.readFloat()
Java

As I wrote in the comment, you need to read the spec Object Serialization Stream Protocol.
So, first 4 bytes represents STREAM_MAGIC, STREAM_VERSION, expected to be always the same value. And 5 byte sequence 0x7A 0xhh 0xhh 0xhh 0xhh represents TC_BLOCKDATALONG(0xhhhhhhhh).
And all blocks needs to be concatenated before parsing strings and floats.
So, preparing the DataReader:
(Nearly the same as Sulthan's, but this treats Modified UTF-8 correctly.)
struct DataReader {
enum DataReaderError: Error {
case invalidFirstByte(byte: UInt16, offset: Int)
case invalidFollowingByte
case missingFollowingByte
case insufficientData
}
var data: Data
var currentPosition: Int
init(data: Data) {
self.data = data
self.currentPosition = 0
}
mutating func skipBytes(_ n: Int) {
currentPosition += n
}
private mutating func readBigEndian<T: FixedWidthInteger>() throws -> T {
guard currentPosition + MemoryLayout<T>.size <= data.count else {
throw DataReaderError.insufficientData
}
var fixedWithInteger: T = 0
let range: Range<Int> = currentPosition ..< currentPosition + MemoryLayout<T>.size
withUnsafeMutableBytes(of: &fixedWithInteger) {ptrT in
let uint8Ptr = ptrT.baseAddress!.assumingMemoryBound(to: UInt8.self)
data.copyBytes(to: uint8Ptr, from: range)
}
currentPosition += MemoryLayout<T>.size
return fixedWithInteger.bigEndian
}
mutating func readFloat() throws -> Float {
let floatBits: UInt32 = try readBigEndian()
return Float(bitPattern: floatBits)
}
mutating func readUnsignedShort() throws -> Int {
let ushortValue: UInt16 = try readBigEndian()
return Int(ushortValue)
}
mutating func readInt() throws -> Int {
let intValue: Int32 = try readBigEndian()
return Int(intValue)
}
mutating func readUnsignedByte() throws -> Int {
guard currentPosition < data.count else {
throw DataReaderError.insufficientData
}
let byte = data[currentPosition]
currentPosition += 1
return Int(byte)
}
mutating func readBytes(_ n: Int) throws -> Data {
guard currentPosition + n <= data.count else {
throw DataReaderError.insufficientData
}
let subdata = data[currentPosition ..< currentPosition+n]
currentPosition += n
return subdata
}
mutating func readUTF() throws -> String {
//Get byte size of the string
let count = try readUnsignedShort()
//Decoding Modified UTF-8
var utf16: [UInt16] = []
var offset = 0
while offset < count {
let firstByte = UInt16(data[currentPosition + offset])
if firstByte & 0b1_0000000 == 0b0_0000000 {
utf16.append(firstByte)
offset += 1
} else if firstByte & 0b111_00000 == 0b110_00000 {
guard offset + 1 < count else {throw DataReaderError.missingFollowingByte}
let secondByte = UInt16(data[currentPosition + offset + 1])
guard secondByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
let codeUnit = ((firstByte & 0b000_11111) << 6) | (secondByte & 0b00_111111)
utf16.append(codeUnit)
offset += 2
} else if firstByte & 0b1111_0000 == 0b1110_0000 {
guard offset + 2 < count else {throw DataReaderError.missingFollowingByte}
let secondByte = UInt16(data[currentPosition + offset + 1])
guard secondByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
let thirdByte = UInt16(data[currentPosition + offset + 2])
guard thirdByte & 0b11_000000 == 0b10_000000 else {throw DataReaderError.invalidFollowingByte}
let codeUnit = ((firstByte & 0b0000_1111) << 12) | ((secondByte & 0b00_111111) << 6) | (thirdByte & 0b00_111111)
utf16.append(codeUnit)
offset += 3
} else {
throw DataReaderError.invalidFirstByte(byte: firstByte, offset: currentPosition+offset)
}
}
currentPosition += offset
return String(utf16CodeUnits: &utf16, count: utf16.count)
}
var isAtEnd: Bool {
return currentPosition == data.count
}
}
We can parse your MN.dat as follows:
let mnUrl = Bundle.main.url(forResource: "MN", withExtension: "dat")!
do {
let data = try Data(contentsOf: mnUrl)
var reader = DataReader(data: data)
reader.skipBytes(4)
//First collect all blocks
var blockData = Data()
while !reader.isAtEnd {
let contentType = try reader.readUnsignedByte()
if contentType == 0x7A {//TC_BLOCKDATALONG
let size = try reader.readInt()
let block = try reader.readBytes(size)
blockData.append(block)
} else if contentType == 0x77 {//TC_BLOCKDATA
let size = try reader.readUnsignedByte()
let block = try reader.readBytes(size)
blockData.append(block)
} else {
print("Unsupported content type")
break
}
}
//Then read the contents of blockData
var blockReader = DataReader(data: blockData)
while !blockReader.isAtEnd {
let string = try blockReader.readUTF()
print(string)
let float1 = try blockReader.readFloat()
print(float1)
let float2 = try blockReader.readFloat()
print(float2)
//Use string, float1, float2 as you like
}
} catch {
print(error)
}
Output:
Albert Lea
43.648
-93.3683
Albertville
45.2377
-93.6544
Alexandria
45.8852
-95.3775
(... no errors...)
Woodbury
44.9239
-92.9594
Worthington
43.62
-95.5964
Wyoming
45.3364
-92.9972
Zimmerman
45.4433
-93.59
You may need to modify the code above if your binary data may contain other content types.

I will show you how to parse Java-encoded data. However, since I cannot understand the format of the file, the response will not be complete:
First, load the file:
// load the file
let fileUrl = URL(fileURLWithPath: "/Users/sulthan/Downloads/MN.dat")
let data = try! Data(contentsOf: fileUrl)
Second, create a simple Java data reader:
// create a simple data reader
class Reader {
let data: Data
private var offset = 0
init(data: Data) {
self.data = data
}
var hasMoreData: Bool {
return offset < data.count
}
func skip(length: Int) {
offset += length
}
func readByte() -> UInt8 {
defer { offset += 1}
return data[offset]
}
// java bytes are unsigned
func readJavaByte() -> Int8 {
return Int8(bitPattern: readByte())
}
func readBytes(length: Int) -> Data {
defer { offset += length }
return data.subdata(in: offset ..< offset + length)
}
private func readJavaUShort() -> UInt16 {
let byte1 = UInt16(exactly: readByte())!
let byte2 = UInt16(exactly: readByte())!
return (byte1 << 8) | byte2
}
func readJavaShort() -> Int16 {
return Int16(bitPattern: readJavaUShort())
}
// Java UTF-8 encodes the length as first two bytes (unsigned java short)
func readJavaUtf() -> String? {
let length = readJavaUShort()
let data = readBytes(length: Int(length))
return String(data: data, encoding: .utf8)
}
private func readUInt32() -> UInt32 {
let short1 = UInt32(exactly: readJavaUShort())!
let short2 = UInt32(exactly: readJavaUShort())!
return (short1 << 16) | short2
}
func readJavaInt() -> Int32 {
let short1 = Int32(exactly: readJavaShort())!
let short2 = Int32(exactly: readJavaShort())!
return (short1 << 16) | short2
}
// interpret the 4 bytes as a floating point number
func readJavaFloat() -> Float {
let bits = readUInt32()
return Float(bitPattern: bits)
}
}
Third, parse the data. I cannot do this completely since the data format is unknown:
// create a reader from our data
let reader = Reader(data: data)
// some data I don't understand
reader.skip(length: 4)
var offset = 0
while reader.hasMoreData {
// some data I don't understand in the beginning and after every 52 items
if offset % 53 == 0 {
reader.skip(length: 5)
}
print(reader.readJavaUtf())
print(reader.readJavaFloat())
print(reader.readJavaFloat())
offset += 1
}
The data parsing will crash with the provided data after some items are parsed. I am assuming you know how to handle that since you know the format.

Related

Appending and extracting objects from NSMutableData

I need to create create a data stream that contains multiple parameters, send it over the network and then extract those parameter when I receive the data.
This is how and create my data (I'm certain all of my variables contain a value)
let dataToSend = NSMutableData()
var mType = Int32(messageType.rawValue)
var reqId = Int32(requestId)
dataToSend.appendDataWithUnsafeBytes(from: &mType, of: Int32.self)
dataToSend.appendDataWithUnsafeBytes(from: &reqId, of: Int32.self)
/* extra protocol data length. In version 0, this is0 as thereis no extra data.
In the future, if you need to add extra protocol data use this*/
var unUsedProtocol = Int32(0)
dataToSend.appendDataWithUnsafeBytes(from: &unUsedProtocol, of: Int32.self)
var encodedDataJson = !jsonString.isEmptyOrNil ? jsonString?.asciiValues : [UInt8]()
dataToSend.appendDataWithUnsafeBytes(from: &encodedDataJson, of: [UInt8]?.self)
var bData = bindaryData
dataToSend.appendDataWithUnsafeBytes(from: &bData, of: Data.self)
here is my appendDataWithUnsafeBytes NSMutableData extension.
extension NSMutableData {
func appendDataWithUnsafeBytes<T>(from element: inout T, of type: T.Type) {
let size = MemoryLayout.size(ofValue: element)
withUnsafeBytes(of: &element) { ptr in
let buffer = ptr.bindMemory(to: type)
if let address = buffer.baseAddress {
self.append(address, length: size)
} else {
VsLogger.logDebug("appendDataWithUnsafeBytes", "unable to get base address of pointer of type: \(type)")
}
}
}
}
and this is how try to extract it (I get the index value along with the data)
var messageTypeValue: Int32? = nil
var requestId: Int32? = nil
var encodedJsonData: Data? = nil
var binaryData: Data? = nil
let intSize = MemoryLayout<Int32>.size
let dataSize = MemoryLayout<Data>.size
var offset = index
bufferData.getBytes(&messageTypeValue, range: NSRange(location: offset, length: intSize))
offset += intSize //8
bufferData.getBytes(&requestId, range: NSRange(location: offset, length: intSize))
offset += intSize //12
/*skipping extra bytes (unsuedProtocol in sendMessageFunction). They come from a future version
that this code doesn't understand*/
offset += intSize //16
bufferData.getBytes(&encodedJsonData, range: NSRange(location: offset, length: dataSize))
offset += dataSize //32
bufferData.getBytes(&binaryData, range: NSRange(location: offset, length: dataSize))
I'm only able to get the first value (messageTypeValue) but for the rest I either get nil or not the right data.
Thank you!
***** UPDATE *****
I got it working by modifying my sending and receiving functions as follows. Where I send it.
let dataToSend = NSMutableData()
var mType = Int32(messageType.rawValue)
var reqId = Int32(requestId)
dataToSend.appendDataWithUnsafeBytes(from: &mType, of: Int32.self)
dataToSend.appendDataWithUnsafeBytes(from: &reqId, of: Int32.self)
/* estra protocol data length. In version 0, this is0 as thereis no extra data.
In the future, if you need to add extra protocol data use this*/
var unUsedProtocol = Int32(0)
dataToSend.appendDataWithUnsafeBytes(from: &unUsedProtocol, of: Int32.self)
var jsonData = Data(!jsonString.isEmptyOrNil ? jsonString!.asciiValues : [UInt8]())
dataToSend.appendDataWithUnsafeBytes(from: &jsonData, of: Data.self)
var bData = bindaryData
dataToSend.appendDataWithUnsafeBytes(from: &bData, of: Data.self)
where I receive it
var offset = Int(index)
let int32Size = MemoryLayout<Int32>.size
let dataSize = MemoryLayout<Data>.size
let messageTypeValue = (bufferData.bytes + offset).load(as: Int32.self)
offset += int32Size
let requestId = (bufferData.bytes + offset).load(as: Int32.self)
offset += int32Size
//skip this one since it not used
//let unusedProtocol = (bufferData.bytes + offset).load(as: Int32.self)
offset += int32Size
let encodedJsonData = (bufferData.bytes + offset).load(as: Data.self)
offset += dataSize
let binaryData = bufferData.bytes.load(fromByteOffset: offset, as: Data.self)
the data is supposed to always be align properly, but is there a way to do some error checking on bufferData.bytes.load(fromByteOffset:as:)
As already mentioned in the comments I highly recommend to use native Data.
First of all you need MartinR's Data extension to convert numeric types to Data and vice versa. I added an custom append method
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
mutating func append<T>(_ other: T) {
append(.init(from: other))
}
}
This is a very simple version of your data set, three Int32 values and a JSON array
let mType : Int32 = 1
let reqId : Int32 = 2
let unUsedProtocol : Int32 = 0
let json = try! JSONEncoder().encode(["hello", "world"])
For convenience I omitted the error handling. In production code try! is discouraged.
The huge benefit of Data is that it can be treated as a collection type, an array of (UInt8) bytes. To build the Data package convert the numeric values and append the bytes respectively
var dataToSend = Data()
dataToSend.append(mType)
dataToSend.append(reqId)
dataToSend.append(unUsedProtocol)
dataToSend.append(json)
On the receiver side to extract the numeric values back this is a helper function which increments also the current index (as inout type), the byte length arises from the type. The function throws an error if the index is out of range and if the type cannot be converted.
enum ConvertDataError : Error { case outOfRange, invalidType}
func extractNumber<T : ExpressibleByIntegerLiteral>(from data : Data, type: T.Type, startIndex: inout Int) throws -> T {
let endIndex = startIndex + MemoryLayout<T>.size
guard endIndex <= data.endIndex else { throw ConvertDataError.outOfRange }
let subdata = data[startIndex..<endIndex]
guard let resultType = subdata.to(type: type) else { throw ConvertDataError.invalidType }
startIndex = endIndex
return resultType
}
Get the start index of the data package and extract the Int32 values
var index = dataToSend.startIndex
do {
let mType1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
let reqId1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
let unUsedProtocol1 = try extractNumber(from: dataToSend, type: Int32.self, startIndex: &index)
print(mType1, reqId1, unUsedProtocol1)
For the json part you need the length, in this example 17 bytes
let jsonLength = json.count
let jsonOffset = index
index += jsonLength
let json1 = try JSONDecoder().decode([String].self, from: dataToSend[jsonOffset..<index])
print(json1)
} catch {
print(error)
}

HMAC SHA 256 returns different value from javascript

I need to generate the same hash value as the site below.
https://cryptii.com/pipes/hmac
If the key is aaaa and the message is a
The expected hash value is e29f14beeb21a8ee1d1c3b8c2be4cf440584da4d46aff5bacb2ae9aa7deb3271.
But the result is 8c21ecf95763195811ac0513bfa42a29be13b9d895b896af45e115dde9bc7382
Below is my code I don't know what's wrong.
I am wasting 6 hours now... so PLEASE HELP ME... : (
import CommonCrypto
import CryptoKit
enum CryptoAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
var HMACAlgorithm: CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5: result = kCCHmacAlgMD5
case .SHA1: result = kCCHmacAlgSHA1
case .SHA224: result = kCCHmacAlgSHA224
case .SHA256: result = kCCHmacAlgSHA256
case .SHA384: result = kCCHmacAlgSHA384
case .SHA512: result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
var digestLength: Int {
var result: Int32 = 0
switch self {
case .MD5: result = CC_MD5_DIGEST_LENGTH
case .SHA1: result = CC_SHA1_DIGEST_LENGTH
case .SHA224: result = CC_SHA224_DIGEST_LENGTH
case .SHA256: result = CC_SHA256_DIGEST_LENGTH
case .SHA384: result = CC_SHA384_DIGEST_LENGTH
case .SHA512: result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
extension String {
func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
let str = self.cString(using: String.Encoding.utf8)
let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr = key.cString(using: String.Encoding.utf8)
let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
let digest = stringFromResult(result: result, length: digestLen)
result.deallocate()
result.deinitialize(count: digestLen)
return digest
}
private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash).lowercased()
}
}
public extension Data {
/**
Method creates bytes array from given Data
- Returns: Array of bytes
*/
func bytesArray<T: ExpressibleByIntegerLiteral>() -> [T] {
var bytes = Array<T>(repeating: 0, count: self.count)
(self as NSData).getBytes(&bytes, length:self.count * MemoryLayout<T>.size)
return bytes
}
}
public extension String {
/**
Method creates bytes array from given String
- Returns: Array of bytes
*/
func bytesArray<T: ExpressibleByIntegerLiteral>() -> [T] {
let data = self.data(using: String.Encoding.utf8)!
return data.bytesArray()
}
}
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return map { String(format: format, $0) }.joined()
}
var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
}
print("a".hmac(algorithm: .SHA256, key: Data("aaaa".utf8).hexEncodedString()))
print("a".hmac(algorithm: .SHA256, key: "aaaa").data(using: .utf8)?.base64EncodedString().description)

Convert into generic function

I am writing an iOS app in Swift. My class has below functions for reading bytebuffer. Below all functions do same task but work on different number types. So, how to convert these functions into a single function using generics?
var array = [UInt8]()
private var currentIndex: Int = 0
private let hostEndianness: Endianness = OSHostByteOrder() == OSLittleEndian ? .little : .big
private var currentEndianness: Endianness = .big
public func getUInt16() -> UInt16 {
let check=(currentIndex + MemoryLayout<UInt16>.size) - 1
if array.indices.contains(check){
let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<UInt16>.size]), UInt16.self)
currentIndex += MemoryLayout<UInt16>.size
return result.littleEndian
}else{
return 0
}
}
public func getInt32() -> Int32 {
let check=(currentIndex + MemoryLayout<Int32>.size) - 1
if array.indices.contains(check){
let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<Int32>.size]), Int32.self)
currentIndex += MemoryLayout<Int32>.size
return result.littleEndian
}else{
return 0
}
}
public func getFloat() -> Float {
let check=(currentIndex + MemoryLayout<UInt32>.size) - 1
if array.indices.contains(check){
let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<UInt32>.size]), UInt32.self)
currentIndex += MemoryLayout<UInt32>.size
return Float(bitPattern: result.littleEndian)
}else{
return 0.0
}
}
public func getDouble() -> Double {
let check=(currentIndex + MemoryLayout<UInt64>.size) - 1
if array.indices.contains(check){
let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<UInt64>.size]), UInt64.self)
currentIndex += MemoryLayout<UInt64>.size
return Double(bitPattern: result.bigEndian)
}else{
return 0.0
}
}
The only difference in the above functions is types: Float, double, UInt16, Int32.
From Function:
private func from<T>(_ value: [UInt8], _: T.Type) -> T {
return value.withUnsafeBytes {
$0.load(fromByteOffset: 0, as: T.self)
}
}
My Solution:
public func getNumber<T:Numeric>() -> T {
let check=(currentIndex + MemoryLayout<T>.size) - 1
if array.indices.contains(check){
let result = from(Array(array[currentIndex..<currentIndex + MemoryLayout<T>.size]), T.self)
currentIndex += MemoryLayout<T>.size
return result.bigEndian
}else{
return 0
}
}
Error:
Value of type 'T' has no member 'bigEndian'

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 do i convert HexString To ByteArray in Swift 3

I'm was trying to convert hexString to Array of Bytes([UInt8]) I searched everywhere but couldn't find a solution. Below is my swift 2 code
func stringToBytes(_ string: String) -> [UInt8]? {
let chars = Array(string)
let length = chars.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
for var i = 0; i < length; i += 2 {
if let a = find(hexChars, chars[i]),
let b = find(hexChars, chars[i+1]) {
bytes.append(UInt8(a << 4) + UInt8(b))
} else {
return nil
}
}
return bytes
}
Example Hex
Hex : "7661706f72"
expectedOutput : "vapor"
This code can generate the same output as your swift 2 code.
func stringToBytes(_ string: String) -> [UInt8]? {
let length = string.characters.count
if length & 1 != 0 {
return nil
}
var bytes = [UInt8]()
bytes.reserveCapacity(length/2)
var index = string.startIndex
for _ in 0..<length/2 {
let nextIndex = string.index(index, offsetBy: 2)
if let b = UInt8(string[index..<nextIndex], radix: 16) {
bytes.append(b)
} else {
return nil
}
index = nextIndex
}
return bytes
}
let bytes = stringToBytes("7661706f72")
print(String(bytes: bytes!, encoding: .utf8)) //->Optional("vapor")
Here is a sketch of how I'd do it in a more idiomatic Swift style (this might be Swift 4 only):
func toPairsOfChars(pairs: [String], string: String) -> [String] {
if string.count == 0 {
return pairs
}
var pairsMod = pairs
pairsMod.append(String(string.prefix(2)))
return toPairsOfChars(pairs: pairsMod, string: String(string.dropFirst(2)))
}
func stringToBytes(_ string: String) -> [UInt8]? {
// omit error checking: remove '0x', make sure even, valid chars
let pairs = toPairsOfChars(pairs: [], string: string)
return pairs.map { UInt8($0, radix: 16)! }
}
Following code may be help for you
extension String {
/// Create `Data` from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a `Data` object. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
///
/// - returns: Data represented by this hexadecimal string.
func hexadecimal() -> Data? {
var data = Data(capacity: characters.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)!
data.append(&num, count: 1)
}
guard data.count > 0 else {
return nil
}
return data
}
}
extension String {
/// Create `String` representation of `Data` created from hexadecimal string representation
///
/// This takes a hexadecimal representation and creates a String object from that. Note, if the string has any spaces, those are removed. Also if the string started with a `<` or ended with a `>`, those are removed, too.
init?(hexadecimal string: String) {
guard let data = string.hexadecimal() else {
return nil
}
self.init(data: data, encoding: .utf8)
}
/// - parameter encoding: The `NSStringCoding` that indicates how the string should be converted to `NSData` before performing the hexadecimal conversion.
/// - returns: `String` representation of this String object.
func hexadecimalString() -> String? {
return data(using: .utf8)?
.hexadecimal()
}
}
extension Data {
/// Create hexadecimal string representation of `Data` object.
/// - returns: `String` representation of this `Data` object.
func hexadecimal() -> String {
return map { String(format: "%02x", $0) }
.joined(separator: "")
}
}
Use like this :
let hexString = "68656c6c 6f2c2077 6f726c64"
print(String(hexadecimalString: hexString))
Or
let originalString = "hello, world"
print(originalString.hexadecimalString())
After lot searching and thinking here is how you do it
func toByteArray( _ hex:String ) -> [UInt8] {
// remove "-" from Hexadecimal
var hexString = hex.removeWord( "-" )
let size = hexString.characters.count / 2
var result:[UInt8] = [UInt8]( repeating: 0, count: size ) // array with length = size
// for ( int i = 0; i < hexString.length; i += 2 )
for i in stride( from: 0, to: hexString.characters.count, by: 2 ) {
let subHexStr = hexString.subString( i, length: 2 )
result[ i / 2 ] = UInt8( subHexStr, radix: 16 )! // ! - because could be null
}
return result
}
extension String {
func subString( _ from: Int, length: Int ) -> String {
let size = self.characters.count
let to = length + from
if from < 0 || to > size {
return ""
}
var result = ""
for ( idx, char ) in self.characters.enumerated() {
if idx >= from && idx < to {
result.append( char )
}
}
return result
}
func removeWord( _ word:String ) -> String {
var result = ""
let textCharArr = Array( self.characters )
let wordCharArr = Array( word.characters )
var possibleMatch = ""
var i = 0, j = 0
while i < textCharArr.count {
if textCharArr[ i ] == wordCharArr[ j ] {
if j == wordCharArr.count - 1 {
possibleMatch = ""
j = 0
}
else {
possibleMatch.append( textCharArr[ i ] )
j += 1
}
}
else {
result.append( possibleMatch )
possibleMatch = ""
if j == 0 {
result.append( textCharArr[ i ] )
}
else {
j = 0
i -= 1
}
}
i += 1
}
return result
}
}
Refer this video to know how I did it.
Credit : AllTech
Conversion of String to Data with nicer syntax.
static func hexStringToData(string: String) -> Data {
let stringArray = Array(string)
var data: Data = Data()
for i in stride(from: 0, to: string.count, by: 2) {
let pair: String = String(stringArray[i]) + String(stringArray[i+1])
if let byteNum = UInt8(pair, radix: 16) {
let byte = Data([byteNum])
data.append(byte)
}
else{
fatalError()
}
}
return data
}