skip to Main Content

I want to get RSA Public Key which is used for AWS authentication. I have a Kotlin Code but am unable to find a similar code in Swift. Here is the code for Kotlin.

Please let me know if anyone has an idea

Thanks!

Kotlin code

private fun getPublicKey(n: String, e: String): RSAPublicKey {
       val publicKeySpec = RSAPublicKeySpec(
              BigInteger(1, Base64.getUrlDecoder().decode(n)),
              BigInteger(1, Base64.getUrlDecoder().decode(e)),
       )
    
       return KeyFactory
             .getInstance("RSA")
             .generatePublic(publicKeySpec) as RSAPublicKey
}

2

Answers


  1. Chosen as BEST ANSWER

    Found the right solution for that after many tries and attempts. I have copied the data extension from Heimdall. It is helpful to combine modulus and exponent data.

    Here is the complete code.

    import Security
    
    class RSAPublicKey {
        
        class func generateRSAPublicKey(modulus: String, exponent: String) -> SecKey? {
            guard let modulusData = Data(base64UrlEncoded: modulus) else {
                return nil
            }
            guard let exponentData = Data(base64UrlEncoded: exponent) else {
                return nil
            }
            
            let publicKeyAttributes: [CFString: Any] = [
                kSecAttrKeyType: kSecAttrKeyTypeRSA,
                kSecAttrKeyClass: kSecAttrKeyClassPublic,
            ]
            
            var error: Unmanaged<CFError>?
            
            let dataa = Data(modulus: modulusData, exponent: exponentData)
            
            guard let publicKey = SecKeyCreateWithData(dataa as CFData, publicKeyAttributes as CFDictionary, &error) else {
                if let error = error?.takeRetainedValue() {
                    print("Failed to create RSA public key: (error)")
                }
                return nil
            }
            var error1:Unmanaged<CFError>?
            if let cfdata = SecKeyCopyExternalRepresentation(publicKey, &error1) {
                let data:Data = cfdata as Data
                let b64Key = data.base64EncodedString()
                print("Key =>", b64Key)
            }
            return publicKey
        }
        
        class func secKeyToData(_ publicKey: SecKey) -> Data? {
            var error: Unmanaged<CFError>?
            
            guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
                if let error = error?.takeRetainedValue() {
                    print("Failed to convert SecKey to data: (error)")
                }
                return nil
            }
            return publicKeyData
        }
    }
    
    
    /// Encoding/Decoding lengths as octets
    ///
    private extension NSInteger {
        func encodedOctets() -> [CUnsignedChar] {
            // Short form
            if self < 128 {
                return [CUnsignedChar(self)];
            }
            
            // Long form
            let i = Int(log2(Double(self)) / 8 + 1)
            var len = self
            var result: [CUnsignedChar] = [CUnsignedChar(i + 0x80)]
            
            for _ in 0..<i {
                result.insert(CUnsignedChar(len & 0xFF), at: 1)
                len = len >> 8
            }
            
            return result
        }
        
        init?(octetBytes: [CUnsignedChar], startIdx: inout NSInteger) {
            if octetBytes[startIdx] < 128 {
                // Short form
                self.init(octetBytes[startIdx])
                startIdx += 1
            } else {
                // Long form
                let octets = NSInteger(octetBytes[startIdx] as UInt8 - 128)
                
                if octets > octetBytes.count - startIdx {
                    self.init(0)
                    return nil
                }
                
                var result = UInt64(0)
                
                for j in 1...octets {
                    result = (result << 8)
                    result = result + UInt64(octetBytes[startIdx + j])
                }
                
                startIdx += 1 + octets
                self.init(result)
            }
        }
    }
    
    private extension Data {
        init(modulus: Data, exponent: Data) {
            // Make sure neither the modulus nor the exponent start with a null byte
            var modulusBytes = [CUnsignedChar](UnsafeBufferPointer<CUnsignedChar>(start: (modulus as NSData).bytes.bindMemory(to: CUnsignedChar.self, capacity: modulus.count), count: modulus.count / MemoryLayout<CUnsignedChar>.size))
            let exponentBytes = [CUnsignedChar](UnsafeBufferPointer<CUnsignedChar>(start: (exponent as NSData).bytes.bindMemory(to: CUnsignedChar.self, capacity: exponent.count), count: exponent.count / MemoryLayout<CUnsignedChar>.size))
            
            // Make sure modulus starts with a 0x00
            if let prefix = modulusBytes.first , prefix != 0x00 {
                modulusBytes.insert(0x00, at: 0)
            }
            
            // Lengths
            let modulusLengthOctets = modulusBytes.count.encodedOctets()
            let exponentLengthOctets = exponentBytes.count.encodedOctets()
            
            // Total length is the sum of components + types
            let totalLengthOctets = (modulusLengthOctets.count + modulusBytes.count + exponentLengthOctets.count + exponentBytes.count + 2).encodedOctets()
            
            // Combine the two sets of data into a single container
            var builder: [CUnsignedChar] = []
            let data = NSMutableData()
            
            // Container type and size
            builder.append(0x30)
            builder.append(contentsOf: totalLengthOctets)
            data.append(builder, length: builder.count)
            builder.removeAll(keepingCapacity: false)
            
            // Modulus
            builder.append(0x02)
            builder.append(contentsOf: modulusLengthOctets)
            data.append(builder, length: builder.count)
            builder.removeAll(keepingCapacity: false)
            data.append(modulusBytes, length: modulusBytes.count)
            
            // Exponent
            builder.append(0x02)
            builder.append(contentsOf: exponentLengthOctets)
            data.append(builder, length: builder.count)
            data.append(exponentBytes, length: exponentBytes.count)
            
            self.init(bytes: data.bytes, count: data.length)
        }
    }
    
    extension Data {
        init?(base64UrlEncoded: String) {
            var base64Encoded = base64UrlEncoded
                .replacingOccurrences(of: "-", with: "+")
                .replacingOccurrences(of: "_", with: "/")
    
            let paddingLength = 4 - base64Encoded.count % 4
            if paddingLength < 4 {
                base64Encoded += String(repeating: "=", count: paddingLength)
            }
    
            self.init(base64Encoded: base64Encoded)
        }
    }
    

    Example:

     if let rsakey = RSAPublicKey.generateRSAPublicKey(modulus: "3bmGZLQSl3-wvGZVrZeI-HGz0XjB6F1lcwyxGRNl4GN3c7qHJyK5EiTqBNHKCQ76njmbERvIph8NlrH4G5l8tWUbB5z3vQYBkegc-fK0q7-QAZMJ9GglxjbppeIHpvYlk5G04CNedzyxA4SG2KNPdELgXYZDOVKtmd2jSSVys1P81H7olm1TS_3jTZP9PScY0t0fOibzzOFK7pdpfz6yjMt2FMLwMtLYNcZ2QaBRqFntNZ5biDiSsk60M5f_DwwAAKnDMZ5pT0qDeFMo8JQfhGMhr8a46oNXXbRmLXGUEytbiesQPVaTMpDkWqyxq_pOFhn2Er99i698YS6LNuZpeQ", exponent: "AQAB") {
           print("Key", rsakey)
     }
    

    Output:

    Key <SecKeyRef algorithm id: 1, key type: RSAPublicKey, version: 4, block size: 2048 bits, exponent: {hex: 10001, decimal: 65537}, modulus: DDB98664B412977FB0BC6655AD9788F871B3D178C1E85D65730CB1191365E0637773BA872722B91224EA04D1CA090EFA9E399B111BC8A61F0D96B1F81B997CB5651B079CF7BD060191E81CF9F2B4ABBF90019309F46825C636E9A5E207A6F6259391B4E0235E773CB1038486D8A34F7442E05D86433952AD99DDA3492572B353FCD47EE8966D534BFDE34D93FD3D2718D2DD1F3A26F3CCE14AEE97697F3EB28CCB7614C2F032D2D835C67641A051A859ED359E5B883892B24EB43397FF0F0C0000A9C3319E694F4A83785328F0941F846321AFC6B8EA83575DB4662D7194132B5B89EB103D56933290E45AACB1ABFA4E1619F612BF7D8BAF7C612E8B36E66979, addr: 0x281877d60>
    

  2. I found this in some of my projects, check it please.

    import Foundation
    import Security
    
    func getPublicKey(n: String, e: String) throws -> SecKey {
    guard let nData = Data(base64Encoded: n),
          let eData = Data(base64Encoded: e) else {
        throw NSError(domain: "Error", code: -1, userInfo: nil)
    }
    
    let attributes: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPublic,
        kSecAttrKeySizeInBits: 2048,
    ]
    
    var error: Unmanaged<CFError>?
    guard let key = SecKeyCreateWithData(Data() as CFData,
                                         attributes as CFDictionary,
                                         &error) else {
        throw error!.takeRetainedValue() as Error
    }
    
    let addParams: [CFString: Any] = [
        kSecAttrKeyType: kSecAttrKeyTypeRSA,
        kSecAttrKeyClass: kSecAttrKeyClassPublic,
        kSecAttrKeySizeInBits: 2048,
        kSecAttrIsPermanent: false,
    ]
    
    let publicKeyData = Data([UInt8](nData) + [UInt8](eData))
    
    guard let publicKey = SecKeyCreateWithData(publicKeyData as CFData,
                                               addParams as CFDictionary,
                                               &error) else {
        throw error!.takeRetainedValue() as Error
    }
    
    return publicKey 
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search