有这么一个应用场景。当我们把一些重要的文件放在asset文件夹中时,直接解压apk就可以得到文件。一些重要信息我们不希望文件被反编译并带走。这时候我们就需要先对文件进行加密,然后放到Android的资源目录下,使用的时候再解密。在现代密码学中,加密系统的安全性是基于密钥,而不是算法。下面介绍一整套加密、解密、申请的流程。这种加密过程在实用性和安全性上还是非常可靠的。是的,这也是市场上常用的方法。核心逻辑其实比较简单。毕竟,最难的加解密算法的实现是现成的。我们公司也采用这种工艺。当然,它会比我说的更复杂。.1、介绍主要涉及以下算法的应用,RSA、AES、Base64编码。基本思路是使用【AES算法+AES密钥】对文件进行加密。为了保证密钥的安全,[RSA算法]+RSA私钥]对AES密钥进行加密。如果你对这些算法不熟悉,可以看看我们老大的《常用加密方法及应用场景》一文。你只需要知道大概的原理和使用方法,因为算法都是java现成的,直接拿来用就可以了。就用它吧。AndroidEncryptionandDecryptionFlowchart.png组织流程,就是上面的流程图,分为三部分:第一块是将加密过程封装成一个小工具,使用加密工具对文件进行加密;第二块是将解密过程封装成一个解密的小工具,使用解密工具解密我们的文件,以便我们进行相关的修改;第三块是我们的目的,将加密后的文件和加解密的AES算法密钥放到Android资源文件中。具体使用。要添加的一件事是RSA算法的公钥和私钥。从第三块可以发现,RSA的公私钥并没有放在资源文件中。其实你想想就知道了。如果加密后的文件,如果加解密的AES密钥和加密AES密钥的RSA密钥都放在文件夹中,那么就完全没有安全性了(注:加解密的算法可以转化为company-owned是的,这是我们公司做的),所以为了保证安全,我们的RSA公钥和私钥是通过应用签名(.keystore签名文件)中的代码动态获取的。感兴趣的可以阅读这篇文章:【从JavaKeystore文件中提取私钥和证书】。2.块1:加密工具加密工具的java接口开发是通过java的swing包实现的。对swing感兴趣的可以参考这篇JavaSwing图形界面开发介绍,很详细。一开始,没有AES密钥。我们需要生成一个安全的密钥,所以生成一个随机的AES密钥,然后保存。加密工具操作页面界面:Encryption2.1,generatearandomkey生成随机密钥分为几个步骤:使用UUID.randomUUID()生成一个随机数作为seed种子;seedseed提供给KeyGenerator生成AES秘钥,只要seed生成的AES秘钥一致即可;通过应用签名私钥获取RSA算法所需的公钥;RSA使用私钥加密AES秘钥;因为生成的秘钥是byte[],所以通过Base64编码显示在界面上。/***Generaterandomkey*/privatevoidrandomKey(){try{//生成随机数作为种子Stringuuid=UUID.randomUUID().toString();byte[]seed=uuid.getBytes("UTF-8");//生成AES秘钥byte[]rawkey=AES.getRawKey(seed);//获取应用签名的密钥对KeyPairpair=SignKey.getSignKeyPair();//用RSA加密AES秘钥byte[privatekey]key=RSA.encrypt(rawkey,pair.getPrivate());//Base64编码成字符串显示Stringbase64Key=Base64.encode(key);mKeyText.setText(base64Key);}catch(Exceptione){e.printStackTrace();}}其中AES.getRawKey(seed)主要是通过AES密钥生成器生成一个128位的密钥。具体实现:/***生成一个用AES算法加密的密钥流,这个密钥会对用于签名的密钥{@linkSignKey}加密两次*/publicstaticbyte[]getRawKey(byte[]seed)throwsException{KeyGeneratorkgen=KeyGenerator.getInstance("AES");SecureRandomsr=SecureRandom.getInstance("SHA1PRNG");sr.setSeed(seed);//192and256bitsmaynotbeavailablekgen.init(128,sr);SecretKeyskey=kgen.generateKey();returnskey.getEncoded();}SignKey.getSignKeyPair()为获取RSA算法所需的公私钥,来自我们的应用程序签名,大家应该很熟悉了。应用打包上传需要签名打包。keystorejava提供api获取testkey.keystore文件(工作室生成)的私钥和证书,将testkey.keystore文件放在目录:/***Author:xishuang*Date:2018.05.06*Des:根据导入的Application签名,读取密钥对和证书*/publicclassSignKey{//applicationsignatureprivatestaticfinalStringkeystoreName="testkey.keystore";privatestaticfinalStringkeystorePassword="123456";//applicationsignaturealiasprivatestaticfinalStringalias="key0";privatestaticfinalStringaliasPassword="123456";/***获取签名密钥对加密密钥*/publicstaticKeyPairgetSignKeyPair(){try{FilestoreFile=newFile(keystoreName);if(!storeFile.exists()){thrownewIllegalArgumentException("尚未设置签名文件!");}StringkeyStoreType="JKS";char[]keystorepasswd=keystorePassword.toCharArray();char[]keyaliaspasswd=aliasPassword.toCharArray();KeyStorekeystore=KeyStore.getInstance(keyStoreType);keystore.load(newFileInputStream(storeFile),keystorepasswd);//取私钥Keykey=keystore.getKey(alias,keyaliaspasswd);if(keyinstanceofPrivateKey){//取公钥Certificatecert=keystore.getCertificate(alias);PublicKeypublicKey=cert.getPublicKey();///保存公私钥到KeyPairreturnnewKeyPair(publicKey,(PrivateKey)key);}}catch(Exceptione){e.printStackTrace();}returnnull;}}把testkey.keystore需要的参数和我们打包的应用签名需要的参数是一样的。通过java提供的keystore类获取,然后使用刚才获取的testkey.keystore私钥加密AES密钥,再通过Base64转换成字符字符串显示,编码转换只显示钥匙。密钥Base64编码.png2.2。导出密钥将密钥导出到文件中,下次解密文件时直接导入密钥。导出密钥需要使用Base64,将文本框中的Base64密钥字符串转成Byte[]再保存。byte[]key=Base64.decode(base64Key);//输出rawkeyFilekeyFile=newFile(dir,"testkey.dat");FileOutputStreamfos=newFileOutputStream(keyFile);2.3、加密文件密钥已经存在,AES算法是Ready-made的,直接调用api加密即可:privatestaticfinalStringAES="AES";/***AES算法加密文件**@paramrawKeyAESkey*@paramfromFile待加密文件*@paramtoFile加密文件*/publicstaticvoidencryptFile(byte[]rawKey,FilefromFile,FiletoFile)throwsException{if(!fromFile.exists()){thrownewNullPointerException("文件不存在");}if(toFile.exists()){toFile.delete();}SecretKeySpecskeySpec=newSecretKeySpec(rawKey,AES);Ciphercipher=Cipher.getInstance(AES);//加密方式cipher.init(Cipher.ENCRYPT_MODE,skeySpec);FileInputStreamfis=newFileInputStream(fromFile);FileOutputStreamfos=newFileOutputStream(toFile,true);byte[]buffer=newbyte[512*1024-16];intoffset;//使用加密流加密CipherInputStreambis=newCipherInputStream(fis,cipher);while((offset=bis.read(buffer))!=-1){fos.write(buffer,0,offset);fos.flush();}fos.close();fis.close();}选择文件,用AES算法加密和AES密钥,最终效果如下,我输入加密前后都无法解密统计出任何密钥。3.Block2:解密工具对解密过程进行解密。其实解密的过程没必要多说,因为解密的过程就是加密过程的逆过程。这个解密在应用中并没有用到,是为了方便我们更新加密后的文件。在修改文件之前,必须先解密文件。解密3.1。导入AES密钥。这个密钥就是我们之前生成的密钥。导入后使用应用签名的RSA公钥解密AES密钥://获取加密后的密钥rawkeyStringkeyStr=mKeyText.getText();byte[]key=Base64.decode(keyStr);//获取应用签名密钥对,公钥解密rawkeyKeyPairkeypair=SignKey.getSignKeyPair();byte[]rawkey=RSA.decrypt(key,keypair.getPublic());//使用rawkey解密文件AES.decryptFile(rawkey,fromFile,toFile);3.2.得到解密文件的纯AES密钥后,可以直接调用AES算法解密文件:/***AES算法解密文件**@paramrawKeyAES密钥*@paramfromFile加密文件*@paramtoFile解密文件*/publicstaticvoiddecryptFile(byte[]rawKey,FilefromFile,FiletoFile)throwsException{if(!fromFile.exists()){thrownewNullPointerException("文件不存在");}if(toFile.exists()){toFile.delete();}SecretKeySpecskeySpec=newSecretKeySpec(rawKey,AES);Ciphercipher=Cipher.getInstance(AES);//解密方式cipher.init(Cipher.DECRYPT_MODE,skeySpec);FileInputStreamfis=newFileInputStream(fromFile);FileOutputStreamfos=newFileOutputStream(toFile,true);byte[]buffer=newbyte[512*1024+16];intoffset;//使用解密流解密CipherInputStreamcipherInputStream=newCipherInputStream(fis,cipher);while((offset=cipherInputStream.read(buffer))!=-1){fos.write(buffer,0,offset);fos.flush();}fos.close();fis.close();}和对比一下AES加密过程,你会发现只是切换了AES算法模式3,第3块:要解密Android应用程序中的文件,需要在资源文件夹中添加加密后的AES密钥。此密钥在上面导出。是的,有加密文件。正确解密的前提是你的应用签名和加密文件的签名是一样的。资源文件3.1。解密AES密钥Android应用程序中的文件解密和java工具中的文件解密主要是RSA密钥的获取。javatools中的应用签名testkey.keystore为开发者所有,可以获取。在Android中,应用要发布到应用市场,任何人都可以下载我们的包,应用签名只能通过Android提供的API获取其公钥。/***Author:xishuang*Date:2018.05.06*Des:应用签名读取工具类*/publicclassSignKey{/***获取当前应用的签名**@paramcontextcontext*/publicstaticbyte[]getSign(Contextcontext){PackageManagerpm=context.getPackageManager();try{PackageInfoinfo=pm.getPackageInfo(context.getPackageName(),PackageManager.GET_SIGNATURES);Signature[]signatures=info.signatures;if(signatures!=null){returnsignatures[0]。toByteArray();}}catch(NameNotFoundExceptione){e.printStackTrace();}returnnull;}/***根据签名获取公钥*/publicstaticPublicKeygetPublicKey(byte[]signature){try{CertificateFactorycertFactory=CertificateFactory.getInstance("X.509");X509Certificatecert=(X509Certificate)certFactory.generateCertificate(newByteArrayInputStream(signature));returncert.getPublicKey();}catch(CertificateExceptione){e.printStackTrace();}returnnull;}}获取应用签名testkey.keystore公钥之后的过程和op基本一样java工具中的ation,RSA公钥用于解密AES密钥。privatestaticfinalStringSIMPLE_KEY_DATA="testkey.dat";/***获取解密后的文件加密密钥*/privatestaticbyte[]getRawKey(Contextcontext)throwsException{//获取应用的签名密钥byte[]sign=SignKey.getSign(context);PublicKeypubKey=SignKey.getPublicKey(sign);//获取加密文件的密钥InputStreamkeyis=context.getAssets().open(SIMPLE_KEY_DATA);byte[]key=getData(keyis);//解密密钥returnRSA.decrypt(key,pubKey);}最后,使用解密后的AES密钥解密文件。3.2.AES密钥解密文件通过资源管理器获取加密文件的文件流,使用AES密钥通过AES算法对文件流进行解密。/***获取解密后的文件流*/publicstaticInputStreamObtainInputStream(Contextcontext){try{AssetManagerassetmanager=context.getAssets();InputStreamis=assetmanager.open("encrypt_test.txt");byte[]rawkey=getRawKey(context);//使用解密流,在数据写入基本OutputStream之前会先解密SecretKeySpecskeySpec=newSecretKeySpec(rawkey,"AES");Ciphercipher=Cipher.getInstance("AES");cipher.init(Cipher.DECRYPT_MODE,skeySpec);returnnewCipherInputStream(is,cipher);}catch(Exceptione){e.printStackTrace();}returnnull;}得到加密后的文件流,达到目的,可以解析成字符串显示:privatevoidinputData(){InputStreamin=DecryptUtil.onObtainInputStream(this);try{BufferedReaderreader=newBufferedReader(newInputStreamReader(in,"GBK"));StringBuildersb=newStringBuilder();Stringline;while((line=reader.readLine())!=null){sb.append(line+"\n");}contentTv.setText(sb.toString());}catch(IOExceptione){e.printStackTrace();}finally{try{in.close();}catch(IOExceptione){e.printStackTrace();}}}示例效果图如下,请关注红框内的内容,因为懒得新建工程,所以用原工程进行测试:结果是目前工具使用的是市面上比较常见的加解密算法,可以改算法,比如DES或者其他对称和非对称算法,甚至是自己修改的算法。如果要运行示例演示:QQ截图20180508234849.png是运行java文件打开加解密小工具。加解密工具的接口是从我们的工具包中抽取出来的一小部分。毕竟写接口很烦人。感谢我们的师傅,多年前就写了这样一个工具。
