tkyaji / cordova-plugin-crypt-file

This plugin to encrypt the source files.
Apache License 2.0
179 stars 116 forks source link

(android) not working well with other plugin #12

Closed zean00 closed 7 years ago

zean00 commented 8 years ago

Just realise that this plugin will replace url scheme file:///android_aset/www into http://localhost/ for the android. This work just fine when the apps is pure javascript that will run completely in the browser. But when I combine this plugin with another plugin such as camera and geolocation, the problem shows up. The plugin seems unable to call the cordova API as it's using nodejs like require mechanism, so it will failed when the url is converted into http://localhost/

I found a workaround with this issue by partially encrypt the source, only the javascript of my apps that need to be encrypted. Just modify the file searching function of file to be encrypted, and change the source link of the url of the encrypted file from relative path to http://localhost/ (bower_components/js -> http://localhost/bower_components/js)

The downside is that I have to create separate project for the android and ios with this method, and if I want to encrypt the .html I have to refactor all the page link to include http://localhost schema , and I can't encrypt the index.html as it will convert everything into http://localhost schema (as the original plugin method)

I wish there will be a better solution and patch to this issue for the future. Anyway this plugin is very useful and thank you.

snt4all commented 8 years ago

How to stop replacement of url scheme file:///android_aset/www into http://localhost/ for the android...I don't want to have http://localhost/ file system

SoftWebProduction commented 8 years ago

How do you tell to encrypt only your files? I tried replacing return fs.statSync(path.join(dir, file)).isFile() && /..(htm|html|js|css)$/.test(file); with: return fs.statSync(path.join(dir, file)).isFile() && /..(updates.js|logs.js)$/.test(file); but din't worked...

zean00 commented 8 years ago

Below is my complete code after_prepare.js


function findCryptoFiles(context, dir, platform) {
    var path              = context.requireCordovaModule('path'),
        fs                = context.requireCordovaModule('fs');

    var fileList = [];
    var list = fs.readdirSync(dir);
    list.filter(function(file) {
        //console.log(file);
        if(platform == 'android' && isEncrypt(file))
            return fs.statSync(path.join(dir, file)).isFile() && /.*\.(js)$/.test(file);
        else if(platform == 'ios')
          return fs.statSync(path.join(dir, file)).isFile() && /.*\.(htm|html|css|js)$/.test(file);
    }).forEach(function(file) {
        fileList.push(path.join(dir, file));
    });
    // sub dir
    list.filter(function(file) {
        return fs.statSync(path.join(dir, file)).isDirectory();
    }).forEach(function(file) {
        var subDir = path.join(dir, file)
        var subFileList = findCryptoFiles(context, subDir,platform);
        fileList = fileList.concat(subFileList);
    });

    return fileList;
}

function getCryptList(){
  var files = [
    'framework7.keypad.min.js',
    'socket.io.js',
    'sodium.js',
    'lz-string.min.js',
    'qr.min.js',
    'shake.js',
    'radialIndicator.min.js',
    'localeNumber.min.js',
    'ajax.min.js',
    'bundle.min.js',
    'auray.min.js',
    'language.min.js',
    'index.js'
  ]
  return files;
}

function alterIndex(path){
  var fs = require('fs');
  var content = fs.readFileSync(path, 'utf-8');
  getCryptList().forEach(function(source){
    var res = content.match(new RegExp('src="(.*?)'+ source + '">','g'));
    res.forEach(function(txt){
      var srv = txt.replace('src="','src="http://localhost/');
      content = content.replace(txt,srv);
    })
  })
  //console.log(content);
  fs.writeFileSync(path, content, 'utf-8');
}

function isEncrypt(file){
  var encrypt = false;
  getCryptList().some(function(name){
    if(file.endsWith(name)){
      return encrypt = true;
      //return true;
    }
  })
  return encrypt;
}

function encryptData(input, key, iv) {
    var crypto = require("crypto");

    var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    var encrypted = cipher.update(input, 'utf8', 'base64') + cipher.final('base64');

    return encrypted;
}

function replaceCryptKey_ios(pluginDir, key, iv) {
    var path = require('path');
    var fs = require('fs');

    var sourceFile = path.join(pluginDir, 'CDVCryptURLProtocol.m');
    var content = fs.readFileSync(sourceFile, 'utf-8');

    content = content.replace(/kCryptKey = @".*";/, 'kCryptKey = @"' + key + '";')
                     .replace(/kCryptIv = @".*";/, 'kCryptIv = @"' + iv + '";');

    fs.writeFileSync(sourceFile, content, 'utf-8');
}

function replaceCryptKey_android(pluginDir, key, iv) {
    var path = require('path');
    var fs = require('fs');

    var sourceFile = path.join(pluginDir, 'com/tkyaji/cordova/DecryptResource.java');
    var content = fs.readFileSync(sourceFile, 'utf-8');

    content = content.replace(/CRYPT_KEY = ".*";/, 'CRYPT_KEY = "' + key + '";')
                     .replace(/CRYPT_IV = ".*";/, 'CRYPT_IV = "' + iv + '";');

    fs.writeFileSync(sourceFile, content, 'utf-8');
}

module.exports = function(context) {

    var path              = context.requireCordovaModule('path'),
        fs                = context.requireCordovaModule('fs'),
        crypto            = context.requireCordovaModule('crypto'),
        Q                 = context.requireCordovaModule('q'),
        cordova_util      = context.requireCordovaModule('cordova-lib/src/cordova/util'),
        platforms         = context.requireCordovaModule('cordova-lib/src/platforms/platforms'),
        Parser            = context.requireCordovaModule('cordova-lib/src/cordova/metadata/parser'),
        ParserHelper      = context.requireCordovaModule('cordova-lib/src/cordova/metadata/parserhelper/ParserHelper'),
        xml               = context.requireCordovaModule('cordova-common').xmlHelpers,
        ConfigParser      = context.requireCordovaModule('cordova-common').ConfigParser;

    var deferral = new Q.defer();
    var projectRoot = cordova_util.cdProjectRoot();

    var key = crypto.randomBytes(24).toString('base64');
    var iv = crypto.randomBytes(12).toString('base64');

    console.log("key=" + key + ", iv=" + iv)

    context.opts.platforms.map(function(platform) {
        var platformPath = path.join(projectRoot, 'platforms', platform);
        var platformApi = platforms.getPlatformApi(platform, platformPath);
        var platformInfo = platformApi.getPlatformInfo();
        var wwwDir = platformInfo.locations.www;

        findCryptoFiles(context, wwwDir, platform).forEach(function(file) {
              var content = fs.readFileSync(file, 'utf-8');
              fs.writeFileSync(file, encryptData(content, key, iv), 'utf-8');
              console.log("encrypt: " + file);
        });

        if (platform == 'ios') {
            var ios_parser = context.requireCordovaModule('cordova-lib/src/cordova/metadata/ios_parser');
            var iosParser = new ios_parser(platformPath);
            var pluginDir = path.join(iosParser.cordovaproj, 'Plugins', context.opts.plugin.id);
            replaceCryptKey_ios(pluginDir, key, iv);

        } else if (platform == 'android') {
            var pluginDir = path.join(platformPath, 'src');
            replaceCryptKey_android(pluginDir, key, iv);

            alterIndex(platformInfo.locations.www + '/index.html');

            /*
            var cfg = new ConfigParser(platformInfo.projectConfig.path);
            cfg.doc.getroot().getchildren().filter(function(child, idx, arr) {
                return (child.tag == 'content');
            }).map(function(child) {
                if (!child.attrib.src .match(/^https?:\/\//)) {
                    child.attrib.src = 'http://localhost/' + child.attrib.src;
                }
            });
            cfg.write();
            */
        }
    });

    deferral.resolve();
    return deferral.promise;
}
SoftWebProduction commented 8 years ago

after replacing my after_prepare.js with yours and using only 2 files on getCryptList(), when i run ionic build android --release i have the following error:

add to body class: platform-android

key=xxxxxxxxxxxxxxxxxxxxx, iv=xxxxxxxxxxxxxxxxxx

Error: Cannot read property 'forEach' of null

Do you have any clue?

sareap commented 8 years ago

I am also facing the same issue, @SoftWebProduction did you find solution ? please let me know