AES代表高级加密系统,它是一种对称加密算法,很多时候我们需要在客户端加密一些纯文本(例如密码)并将其发送到服务器,然后由服务器解密以进行进一步处理.AES加密和解密更加容易在相同的平台(例如Android客户端和Java服务器)中实现,但有时在跨平台环境(例如Java客户端和Java Server)(例如在Spring MVC框架中)中解密AES加密密码变得颇具挑战性,因为如果任何系统默认值都不匹配,那么解密将失败。

在本文中,我们将使用spring mvc和angular js客户端创建一个应用程序。 我们将有一个登录页面,其中包含用户名和密码的表单输入。 在将密码发送到服务器之前,将使用CryptoJS在javascript中对密码进行加密,并且将在java中解密相同的加密密码,并进行比较以匹配密码。我们将在javascript中生成salt和IV,然后生成使用PBKDF2函数从密码,盐和密钥大小中获取密钥,之后我们将使用key和IV对明文进行加密,并使用Java对其进行解密,因此基本上我们将开发一种与Java和Java互操作的AES加密机制。 Javascript。

在继续进行之前,让我们澄清一件事,即该机制仅在数据的有线传输过程中(最有可能)增加了一种额外的安全性,但没有提供完全的证明安全性。 如果您不使用SSL,则攻击者可以执行中间人攻击,并通过为用户提供其他密钥来窃取数据。

项目结构

我们有一个弹簧靴和角度Js Web应用程序设置。以下是结构。

DES Java ecb模式加解密 java实现aes加解密_加密解密

JavaScript中的Aes加密

对于javascript中的AES加密,我们导入了两个js文件crypto.jspbkdf2.js 。我们拥有AesUtil.js ,它们具有执行加密和解密的通用代码。 这里的keySize是密钥的大小,以4字节为单位。因此,要使用128位密钥,我们将位数除以32得到了用于CryptoJS的密钥大小。

AesUtil.js

var AesUtil = function(keySize, iterationCount) {
  this.keySize = keySize / 32;
  this.iterationCount = iterationCount;
};

AesUtil.prototype.generateKey = function(salt, passPhrase) {
  var key = CryptoJS.PBKDF2(
      passPhrase, 
      CryptoJS.enc.Hex.parse(salt),
      { keySize: this.keySize, iterations: this.iterationCount });
  return key;
}

AesUtil.prototype.encrypt = function(salt, iv, passPhrase, plainText) {
  var key = this.generateKey(salt, passPhrase);
  var encrypted = CryptoJS.AES.encrypt(
      plainText,
      key,
      { iv: CryptoJS.enc.Hex.parse(iv) });
  return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}

AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) {
  var key = this.generateKey(salt, passPhrase);
  var cipherParams = CryptoJS.lib.CipherParams.create({
    ciphertext: CryptoJS.enc.Base64.parse(cipherText)
  });
  var decrypted = CryptoJS.AES.decrypt(
      cipherParams,
      key,
      { iv: CryptoJS.enc.Hex.parse(iv) });
  return decrypted.toString(CryptoJS.enc.Utf8);
}

JavaScript中的密码加密

单击提交按钮后,将调用logMeIn()方法。 该方法将使用AesUtil.js定义的通用代码来加密密码并发出POST请求以验证密码。发送的密码将以iv::salt::ciphertext的形式在服务器端,java将解密密码并在响应中发送解密的密码,该密码将显示在警报框中。

var app = angular.module('demoApp', []);
app.controller('loginController', ['$scope', '$rootScope', '$http', function ($scope, $rootScope, $http) {

    $scope.logMeIn = function(){
        if(!$scope.userName || !$scope.password){
            $scope.showMessage("Missing required fields.", false);
            return;
        }
        var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);
        var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);

        var aesUtil = new AesUtil(128, 1000);
        var ciphertext = aesUtil.encrypt(salt, iv, $('#key').text(), $scope.password);

        var aesPassword = (iv + "::" + salt + "::" + ciphertext);
        var password = btoa(aesPassword);
        var data = {
            userName: $scope.userName,
            password: password
        }

        $http.post('/login',data).then(function (response){
			if(response.status === 200){
                alert("Password is " + response.data.password);
			}else {
			    alert("Error occurred");
            }
		})
	};

	}]);

Java中的AES解密

首先,让我们实现将拦截登录请求的控制器类。 这里我们已经对该密钥进行了硬编码,该密钥将由服务器唯一生成,并针对每个登录请求发送给客户端。 客户端将使用相同的密钥,而加密和服务器将使用相同的密钥进行解密。请确保密钥长度为16,因为我们使用的是128位加密。 记住我们从客户端发送的加密文本的格式– iv::salt::ciphertext 。 文本以相同格式解密。 我们已经有IV,salt和密文。

package com.example.demo.controller;

import com.example.demo.model.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Controller
public class WelcomeController {

	private static final Logger LOGGER = LoggerFactory.getLogger(WelcomeController.class);

	@RequestMapping(value={"/login"},method = RequestMethod.GET)
    public String loginPage(HttpServletRequest request){
		LOGGER.info("Received request for login page with id - " + request.getSession().getId());
		String randomKey = UUID.randomUUID().toString();
		//String uniqueKey = randomKey.substring(randomKey.length()-17, randomKey.length() -1);
        String uniqueKey = "1234567891234567";
	    request.getSession().setAttribute("key", uniqueKey);
	    return "index";
    }

    @RequestMapping(value={"/login"},method = RequestMethod.POST)
    public @ResponseBody ResponseEntity login(@RequestBody Credentials credentials, HttpServletRequest request) {
        String decryptedPassword =  new String(java.util.Base64.getDecoder().decode(credentials.getPassword()));
        AesUtil aesUtil = new AesUtil(128, 1000);
        Map map = new HashMap<>();
        if (decryptedPassword != null && decryptedPassword.split("::").length == 3) {
            LOGGER.info("Password decrypted successfully for username - " + credentials.getUserName());
            String password = aesUtil.decrypt(decryptedPassword.split("::")[1], decryptedPassword.split("::")[0], "1234567891234567", decryptedPassword.split("::")[2]);
            map.put("password", password);
        }
        return new ResponseEntity<>(map, HttpStatus.OK);
    }

}

以下是用于AES加密和解密的java util类。您可以在java中遵循AES加密和解密,以获取有关以下实现的更多详细说明。

AesUtil.java
package com.example.demo.controller;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class AesUtil {
    private final int keySize;
    private final int iterationCount;
    private final Cipher cipher;
    
    public AesUtil(int keySize, int iterationCount) {
        this.keySize = keySize;
        this.iterationCount = iterationCount;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        }
        catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw fail(e);
        }
    }
    
    public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
        try {
            SecretKey key = generateKey(salt, passphrase);
            byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
            return new String(decrypted, "UTF-8");
        }
        catch (UnsupportedEncodingException e) {
            return null;
        }catch (Exception e){
            return null;
        }
    }
    
    private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
        try {
            cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
            return cipher.doFinal(bytes);
        }
        catch (InvalidKeyException
                | InvalidAlgorithmParameterException
                | IllegalBlockSizeException
                | BadPaddingException e) {
            return null;
        }
    }
    
    private SecretKey generateKey(String salt, String passphrase) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize);
            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
            return key;
        }
        catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            return null;
        }
    }

    public static byte[] base64(String str) {
        return Base64.decodeBase64(str);
    }
    
    public static byte[] hex(String str) {
        try {
            return Hex.decodeHex(str.toCharArray());
        }
        catch (DecoderException e) {
            throw new IllegalStateException(e);
        }
    }
    
    private IllegalStateException fail(Exception e) {
        return null;
    }

}

测试AES加密和解密

作为Java应用程序运行DemoApplication.java并点击http:// localhost:8080 。 登录页面出现后,您可以输入用户名和密码,然后单击“提交”按钮,您可以在警报中看到解密的密码。

DES Java ecb模式加解密 java实现aes加解密_加密解密_02
/>

结论

在本文中,我们讨论了可与Java和Javascript互操作的AES加密。 我们使用Crypto.js库在javascript中执行此加密。 完整的源代码可以在这里找到。如果您有任何要添加或共享的内容,请在下面的评论部分中共享。