最近做的一个项目,服务器为Java,采用SSH框架,客户端为Android和IOS。当用户登录时,从客户端向服务器提交用户名和密码。这就存在一个问题,如果数据包在网络上被其他人截取了,密码就有可能泄露。

可以采用Base64对密码编码,但是Base64要进行解码是很容易的事。

另一种方法是对密码进行MD5加密,MD5是不可逆的,只能加密不能解密。但是其他人截取了密码的MD5字符串以后,可以原封不动的将MD5加密后的字符串提交给服务器,服务器肯定会判断这是正确的密码,这样还是可以登录进去。

解决的方法就只能采用加密算法了。加密算法分为对称加密和非对称加密。对称加密算法,加密和解密使用相同的密钥,密钥在网络传输的过程中有可能被截取,所以不是很安全。非对称加密,使用公钥加密,只能使用私钥解密,公钥是公开的,私钥是不公开的。即使在传递的过程中,公钥被其他人获取了也无所谓,因为公钥是用来加密的,只有私钥才能解密,而私钥是不会传递的,也就不可能被其他人获取。

非对称加密最常用的就是RSA算法,RSA算法是由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的,取了他们姓的第一个字母来命名。RSA算法的原理就不讲了。密钥长度为768的RSA算法有可能被破解,密钥长度为1024的RSA算法还没有被破解,所以可以认为密钥长度为1024的RSA算法是比较安全的。但是RSA算法的计算量大,一般只用于关键信息的加密,如密码、对称加密算法的密钥等。在我们的项目中,就使用RSA算法对用户密码进行加密。具体的步骤如下:

1. 客户端向服务器申请密钥;

2. 服务器接收到客户端的申请以后,生成一对密钥,将公钥发给客户端,私钥自己保存;

3. 客户端接收到公钥以后,使用公钥对密码加密,然后将密文发给服务器;

4. 服务器接收到密文以后,使用私钥解密,判断是否是正确的密码。

下面是关键代码。

生成密钥和加密、解密的代码:

/**
* 生成公钥和私钥
* @throws NoSuchAlgorithmException
*
*/
public static HashMap<String, Object> getKeys() throws NoSuchAlgorithmException{
HashMap<String, Object> map = new HashMap<String, Object>();
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
map.put("public", publicKey);
map.put("private", privateKey);
return map;
} /**
* 使用模和指数生成RSA公钥
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
* /None/NoPadding】
*
* @param modulus
* 模
* @param exponent
* 指数
* @return
*/
public static RSAPublicKey getPublicKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
return (RSAPublicKey) keyFactory.generatePublic(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 使用模和指数生成RSA私钥
* 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
* /None/NoPadding】
*
* @param modulus
* 模
* @param exponent
* 指数
* @return
*/
public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {
try {
BigInteger b1 = new BigInteger(modulus);
BigInteger b2 = new BigInteger(exponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
} catch (Exception e) {
e.printStackTrace();
return null;
}
} /**
* 公钥加密
*
* @param data
* @param publicKey
* @return
* @throws Exception
*/
public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// 模长
int key_len = publicKey.getModulus().bitLength() / 8;
// 加密数据长度 <= 模长-11
String[] datas = splitString(data, key_len - 11);
String mi = "";
//如果明文长度大于模长-11则要分组加密
for (String s : datas) {
mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;
} /**
* 私钥解密
*
* @param data
* @param privateKey
* @return
* @throws Exception
*/
public static String decryptByPrivateKey(String data, RSAPrivateKey privateKey)
throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//模长
int key_len = privateKey.getModulus().bitLength() / 8;
byte[] bytes = data.getBytes();
byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
System.err.println(bcd.length);
//如果密文长度大于模长则要分组解密
String ming = "";
byte[][] arrays = splitArray(bcd, key_len);
for(byte[] arr : arrays){
ming += new String(cipher.doFinal(arr));
}
return ming;
}

服务器收到客户端的请求时,生成一对密钥:

                HashMap<String, Object> mymap = RSAUtils.getKeys();
// 生成公钥和私钥
RSAPublicKey publicKey = (RSAPublicKey) mymap.get("public");
RSAPrivateKey privateKey = (RSAPrivateKey) mymap.get("private");
// 模
String modulus = publicKey.getModulus().toString();
// 公钥指数
String public_exponent = publicKey.getPublicExponent().toString();
// 私钥指数
String private_exponent = privateKey.getPrivateExponent().toString();
// 使用模和指数生成公钥和私钥
RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent);
RSAPrivateKey priKey = RSAUtils.getPrivateKey(modulus, private_exponent);

将其中的模和私钥指数发给客户端,客户端收到以后,使用getPublicKey(String modulus, String exponent)生成公钥,使用公钥对密码加密,然后发给服务器。服务器收到密文以后,使用decryptByPrivateKey(String data, RSAPrivateKey privateKey)解密,获得密码明文,然后就可以判断密码是否正确。

05-11 11:30
查看更多