你的位置:首页 > 操作系统

[操作系统]Java Web项目RSA加密


最近做的一个项目,服务器为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)解密,获得密码明文,然后就可以判断密码是否正确。