客户端与服务端进行接口通讯时,往往采用ras加解密来保障通讯过程数据安全。
我们公司做的安卓app,接口全是明文,运行了2年左右,测试部突袭我们app,对我们app进行了漏洞扫描。
抓取了登录过程中发送参数与返回参数,并通过代理,改掉了返回参数,欺骗客户端。导致在账号和密码错误的情况下,顺利登入了app。这是一个巨大的漏洞。向全公司做了通报。并要求立马修复。
该app接口原本并非本人设计,我最近刚接手这个app。于是迅速启动修复方案。即采用ras非对称加解密的方式,在通讯过程对参数进行加密。
1、首先由本人在网上通过在线工具,生成了一套key。配对的公钥pub与私钥pri。
2、公钥给了后端.net开发人员,期望对接口返回结果采用公钥进行加密;同时,私钥给了安卓开发人员,期望对接口返回结果采用私钥进行解密。
3、在改动接口前,首先需要将服务端的加密后密文,客户端能够解析,这一步优先级最高,务必先调通。
在调试过程中,遇到了没有想过的问题,就是.net语言和java语言对ras加解密的方式不同。
java直接对在线生成后的私钥,拿来就能用,但.net拿到的公钥无法直接使用,.net需使用xml格式的公钥才能使用。
于是按照.net xml的公钥格式,拼接出了xml的字符串。加密后,无法用java采用私钥解密。
后来推测,xml虽然格式正确了,但内容可能并不是简单的拼接,需要通过工具进行转换。
果不其然,网上提供了java将ras公钥转换为.net公钥的方法,代码如下:
package com.jusitan.utils;
import java.io.File;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class Ras {
static String pubkey="MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAunE5gTO1KOTtuoJXMjCX"+
"UvoSVeRG6UhkCRsHenEj4vWuP+tuVrQe76AaigW9K5BZqIM98DZMJvohk+kakDdP"+
"UH4bEQ/0MYAiWRUbGFhPp3d46qwhZMglhAbGDbliVO4MLZIhruTwPoz6uMcxsgh2"+
"DXUwDViWdZ8xuKATdYgnXBQBaz8Fg8GUqoFa2cfQj1lTi002fku+J84ucxwvDWpq"+
"4d37GdZzfDFKJ4n5cqHqo+8HSNqXatASZVLsXdVmgtETdwLiHc5jmIzau3HVr78a"+
"xcoJ6SasBhMKnkMQF7oT6tymkWLi14OGP9zPslvjG0F1XGNtzNhOpEl3SrbjNX7D"+
"juF/cR1i9R2lV7VoeixgqxOFnLdHeGvZVeRmLytzLXE/hSrT4enDqerQKgq/I+PW"+
"WBbLvq9JzPjRdN8VTvbgofN5EoK+z4ZcD33AVNr5M8x50C1yfbLVe0eGbt5FWaNE"+
"1x0OUOCteUUojXh2BNNL3bRujGFPNuIy4nPcPpEK67GXyOPNjA3E/WY7ybwKHHR/"+
"sgL8gCPGMbtCmdYoSicxSoWqP8oLZMn7l7dK9YLfiNS8qiL4EEtb49lSXJHYXMDw"+
"bAyU6GOSB8e8ZpJutSBnnAMhLmjg/OLcOA7Hlj9ifntbKc32GPungxOO6AKrLekD"+
"FwxxTBhHTHyYXO0UHliyRQ8CAwEAAQ==" ;
/**
* @param args
*/
public static void main(String[] args) {
try {
// PrivateKey privateKey=RSAUtils.getRSAPrivateKeyBybase64("");
PublicKey publicKey=RSAUtils.getRSAPublidKeyBybase64(pubkey);
String publicKeyXml = getRSAPublicKeyAsNetFormat(publicKey.getEncoded());
// String privateKeyXml = getRSAPrivateKeyAsNetFormat(privateKey.getEncoded());
System.out.println(publicKeyXml);
//getRSAPublicKeyAsNetFormat(publicKey.getEncoded());
} catch (Exception e) {
e.printStackTrace();
}
}
private static String getRSAPrivateKeyAsNetFormat(byte[] encodedPrivkey) {
try {
StringBuffer buff = new StringBuffer(1024);
PKCS8EncodedKeySpec pvkKeySpec = new PKCS8EncodedKeySpec(
encodedPrivkey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateCrtKey pvkKey = (RSAPrivateCrtKey) keyFactory
.generatePrivate(pvkKeySpec);
buff.append("<RSAKeyValue>");
buff.append("<Modulus>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getModulus().toByteArray()))
+ "</Modulus>");
buff.append("<Exponent>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getPublicExponent()
.toByteArray())) + "</Exponent>");
buff.append("<P>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getPrimeP().toByteArray()))
+ "</P>");
buff.append("<Q>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getPrimeQ().toByteArray()))
+ "</Q>");
buff.append("<DP>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getPrimeExponentP()
.toByteArray())) + "</DP>");
buff.append("<DQ>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getPrimeExponentQ()
.toByteArray())) + "</DQ>");
buff.append("<InverseQ>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getCrtCoefficient()
.toByteArray())) + "</InverseQ>");
buff.append("<D>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pvkKey.getPrivateExponent()
.toByteArray())) + "</D>");
buff.append("</RSAKeyValue>");
return buff.toString().replaceAll("[ \t\n\r]", "");
} catch (Exception e) {
System.err.println(e);
return null;
}
}
private static String getRSAPublicKeyAsNetFormat(byte[] encodedPublicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey pukKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(encodedPublicKey));
StringBuffer buff = new StringBuffer(1024);
buff.append("<RSAKeyValue>");
buff.append("<Modulus>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pukKey.getModulus().toByteArray()))
+ "</Modulus>");
buff.append("<Exponent>"
+ com.sun.org.apache.xml.internal.security.utils.Base64.encode(removeMSZero(pukKey.getPublicExponent().toByteArray())) + "</Exponent>");
buff.append("</RSAKeyValue>");
return buff.toString().replaceAll("[ \t\n\r]", "");
} catch (Exception e) {
System.err.println(e);
return null;
}
}
private static byte[] removeMSZero(byte[] data) {
byte[] data1;
int len = data.length;
if (data[0] == 0) {
data1 = new byte[data.length - 1];
System.arraycopy(data, 1, data1, 0, len - 1);
} else
data1 = data;
return data1;
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.jusitan.utils;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public abstract class RSAUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);
private static final String ALGORITHOM = "RSA";
private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
private static final String SIGNATURE_MD5WITHRSA = "MD5withRSA";
private static final String SIGNATURE_SHA1WITHRSA = "SHA1withRSA";
private static final String SIGNATURE_ALGORITHM_DEFAULT = "NONEwithRSA";
private static final String CIPHER_TRANSFORMATION_DEFAULT = "RSA/ECB/PKCS1Padding";
private static final String HASHID_SHA1 = "01";
private static final String HASHID_MD5 = "02";
private static KeyPairGenerator keyPairGen = null;
private static KeyFactory keyFactory = null;
static {
try {
keyPairGen = KeyPairGenerator.getInstance("RSA", DEFAULT_PROVIDER);
keyFactory = KeyFactory.getInstance("RSA", DEFAULT_PROVIDER);
} catch (NoSuchAlgorithmException var1) {
LOGGER.error(var1.getMessage());
}
}
private RSAUtils() {
}
public static synchronized KeyPair generateRSAKeyPair(int keysize, BigInteger publicExponent) {
try {
keyPairGen.initialize(new RSAKeyGenParameterSpec(keysize, publicExponent), new SecureRandom());
return keyPairGen.generateKeyPair();
} catch (Exception var3) {
LOGGER.error("生成模长 =" + keysize + ",指数=" + publicExponent + "的RSA密钥对失败", var3);
return null;
}
}
public static PrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent) {
if(!StringUtils.isEmpty(hexModulus) && !StringUtils.isEmpty(hexPrivateExponent)) {
BigInteger mbig = new BigInteger(hexModulus, 16);
BigInteger ebig = new BigInteger(hexPrivateExponent, 16);
RSAPrivateKeySpec prispec = new RSAPrivateKeySpec(mbig, ebig);
try {
return keyFactory.generatePrivate(prispec);
} catch (InvalidKeySpecException var6) {
LOGGER.error("hexModulus or hexPrivateExponent value is invalid. return null(RSAPrivateKey).");
return null;
}
} else {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("hexModulus and hexPrivateExponent cannot be empty. RSAPrivateKey value is null to return.");
}
return null;
}
}
public static PublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent) {
if(!StringUtils.isEmpty(hexModulus) && !StringUtils.isEmpty(hexPublicExponent)) {
BigInteger mbig = new BigInteger(hexModulus, 16);
BigInteger ebig = new BigInteger(hexPublicExponent, 16);
RSAPublicKeySpec pubspec = new RSAPublicKeySpec(mbig, ebig);
try {
return keyFactory.generatePublic(pubspec);
} catch (InvalidKeySpecException var6) {
LOGGER.error("hexModulus or hexPublicExponent value is invalid. return null(RSAPublicKey).");
return null;
}
} else {
if(LOGGER.isDebugEnabled()) {
LOGGER.debug("hexModulus and hexPublicExponent cannot be empty. return null(RSAPublicKey).");
}
return null;
}
}
public static RSAPublicKey getRSAPublidKeyBybase64(String base64s) throws Base64DecodingException {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(com.sun.org.apache.xml.internal.security.utils.Base64.decode(base64s));
RSAPublicKey publicKey = null;
try {
publicKey = (RSAPublicKey)keyFactory.generatePublic(keySpec);
} catch (InvalidKeySpecException var4) {
LOGGER.error("base64编码=" + base64s + "转RSA公钥失败", var4);
}
return publicKey;
}
public static RSAPrivateKey getRSAPrivateKeyBybase64(String base64s) throws Base64DecodingException {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(com.sun.org.apache.xml.internal.security.utils.Base64.decode(base64s));
RSAPrivateKey privateKey = null;
try {
privateKey = (RSAPrivateKey)keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException var4) {
LOGGER.error("base64编码=" + base64s + "转RSA私钥失败", var4);
}
return privateKey;
}
}
得到转换后的xml后,.net 就可以使用了。
转换后的xml如下;
<RSAKeyValue><Modulus>unE5gTO1KOTtuoJXMjCXUvoSVeRG6UhkCRsHenEj4vWuP+tuVrQe76AaigW9K5BZqIM98DZMJvohk+kakDdPUH4bEQ/0MYAiWRUbGFhPp3d46qwhZMglhAbGDbliVO4MLZIhruTwPoz6uMcxsgh2DXUwDViWdZ8xuKATdYgnXBQBaz8Fg8GUqoFa2cfQj1lTi002fku+J84ucxwvDWpq4d37GdZzfDFKJ4n5cqHqo+8HSNqXatASZVLsXdVmgtETdwLiHc5jmIzau3HVr78axcoJ6SasBhMKnkMQF7oT6tymkWLi14OGP9zPslvjG0F1XGNtzNhOpEl3SrbjNX7DjuF/cR1i9R2lV7VoeixgqxOFnLdHeGvZVeRmLytzLXE/hSrT4enDqerQKgq/I+PWWBbLvq9JzPjRdN8VTvbgofN5EoK+z4ZcD33AVNr5M8x50C1yfbLVe0eGbt5FWaNE1x0OUOCteUUojXh2BNNL3bRujGFPNuIy4nPcPpEK67GXyOPNjA3E/WY7ybwKHHR/sgL8gCPGMbtCmdYoSicxSoWqP8oLZMn7l7dK9YLfiNS8qiL4EEtb49lSXJHYXMDwbAyU6GOSB8e8ZpJutSBnnAMhLmjg/OLcOA7Hlj9ifntbKc32GPungxOO6AKrLekDFwxxTBhHTHyYXO0UHliyRQ8=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>