I'm trying to send string data (specifically PEM formatted keys) out from a Native Module (written in Swift) through a Typescript exported function and into the main JS functions of a React Native app.
If I do an end-to-end test in Swift (converting the key data to strings, then to data and back again to simulate the transfer of the strings to and from JavaScript then all the logic works. I'm able to turn the data back into a key and use it to sign/encrypt. I've tried adjusting the encoding, using UTF8 and UTF16 strings but nothing seems to make it fail like it does when coming back into the native module.
The Typescript module definition
declare module 'encrypto' { interface KeyPair { publicKey: string; privateKey: string; } export function generateKeys(keySize: number): Promise<KeyPair>; export function getPublicKeyAsPEM(publicKey: string): Promise<string>; export function encrypt(message: string, publicKeyAsPEM: string): Promise<string>;}export { Encrypto };export default Encrypto;
The Encrypto.m file
#import <React/RCTBridgeModule.h>@interface RCT_EXTERN_MODULE(Encrypto, NSObject)+ (BOOL)requiresMainQueueSetup { return false;}RCT_EXTERN_METHOD(generateKeys:(int) keySize withResolver:(RCTPromiseResolveBlock) resolve withRejecter:(RCTPromiseRejectBlock) reject)RCT_EXTERN_METHOD(getPublicKeyPEM:(NSString) key withResolver:(RCTPromiseResolveBlock) resolve withRejecter:(RCTPromiseRejectBlock) reject)RCT_EXTERN_METHOD(encrypt:(NSString) message withPublicKeyAsPEM:(NSString) publicKeyAsPEM withResolver:(RCTPromiseResolveBlock) resolve withRejecter:(RCTPromiseRejectBlock) reject)@end
The Encryto.swift file
@objc(Encrypto)class Encrypto: NSObject { @objc(generateKeys:withResolver:withRejecter:) func generateKeys(keySize: Int, resolve: @escaping (RCTPromiseResolveBlock), reject:@escaping (RCTPromiseRejectBlock)) { do { let (publicKey, privateKey) = try .generateRSAKeys(keySize) resolve([ "privateKey" : privateKey.base64EncodedString(),"publicKey" : publicKey.base64EncodedString() ]) } catch { let error = error reject("", "Key generation failed", error) } } @objc(getPublicKeyPEM:withResolver:withRejecter:) func getPublicKeyPEM(_ base64PublicKey: String, resolve: @escaping (RCTPromiseResolveBlock), reject: @escaping (RCTPromiseRejectBlock)) { do { guard let publicKeyData = Data(base64Encoded: base64PublicKey) else { reject("", "Could not decode public key (is it Base64 encoded?)." ,NSError()) return } let pemFormattedPublicKey = try pemForPublicKey(publicKeyData) resolve(pemFormattedPublicKey) } catch { let error = error reject("", "Public Key PEM production failed", error) } } @objc(encrypt:withPublicKeyAsPEM:withResolver:withRejecter:) func encrypt(_ message:String, publicKeyAsPEM: String, resolve: @escaping (RCTPromiseResolveBlock), reject: @escaping (RCTPromiseRejectBlock)) { do { let publicKeyData = try publicKeyFromPEM(publicKeyAsPEM) let encrypted = try encrypt(key, publicKeyData: publicKeyData) resolve(encrypted) } catch { let error = error reject("", "Encryption failed", error) } }}
If I log the PEM string in the Javascript, before it calls encrypt, I get this:
-----BEGIN RSA PRIVATE KEY-----MIIBCgKCAQEAqGloHcWKLYUGZZ4C2VsFNtFlMu1LgjbV7tHxwVGhNapz7YJASFqRhjAAOI68w4nKv/cq+rHsy7V4B7rCcttNAu0/fttvRrGpB4Yhh6EZQbSdoUasPSwg7d81kiipcs20CNr+b0SL4Ajb7ld5Sb9JRWnjfYHmh9DuKkcl+hzXKtejQuwvz/ZOzrdT+FJn1/tIKynv12li8JqoZwLaq3Glzu/ZwXYptg9E27v1Fj6+YbaFekrsM98dnt/RB6EkVqxhthIxjhYgCu9u56LZmUpa/ozSh+swpAR/xsewvcMX+geFMSklVgF04P2tEmJgz1bZjJ4OCa3zrezfLbmaKob65wIDAQAB-----END RSA PRIVATE KEY-----
and when I log the same string coming into the Swift function implementation I see this:
-----BEGIN RSA PRIVATE KEY-----MIIBCgKCAQEAqGloHcWKLYUGZZ4C2VsFNtFlMu1LgjbV7tHxwVGhNapz7YJASFqRhjAAOI68w4nKv/cq+rHsy7V4B7rCcttNAu0/fttvRrGpB4Yhh6EZQbSdoUasPSwg7d81kiipcs20CNr+b0SL4Ajb7ld5Sb9JRWnjfYHmh9DuKkcl+hzXKtejQuwvz/ZOzrdT+FJn1/tIKynv12li8JqoZwLaq3Glzu/ZwXYptg9E27v1Fj6+YbaFekrsM98dnt/RB6EkVqxhthIxjhYgCu9u56LZmUpa/ozSh+swpAR/xsewvcMX+geFMSklVgF04P2tEmJgz1bZjJ4OCa3zrezfLbmaKob65wIDAQAB-----END RSA PRIVATE KEY-----
and to make the calls into the Native module I'm simply doing...
const keypair:KeyPair = await Encrypto.generateKeys(2048); const publicKeyPem = await Encrypto.getPublicKeyPEM(keypair.publicKey); const encryptedMessage = await Encrypto.encrypt("a message", publicKeyPem);
The getPublicKeyPEM
function is able to successfully convert the key string passed in from Javascript and send back the resulting PEM string. But when that PEM string comes back for the subsequent encrypt
call is is unable to successfully extract the key from it in order to perform the encrypt and despite both PEM files looking identical.
Does anyone have any ideas whats going on or what I could try please?