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
}
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>