Статьи

Шифрование AES в JavaScript и дешифрование в Java

AES расшифровывается как Advanced Encryption System и представляет собой симметричный алгоритм шифрования. Много раз нам требуется зашифровать некоторый простой текст, такой как пароль, на стороне клиента и отправить его на сервер, а затем сервер расшифровывает его для дальнейшей обработки. Шифрование и дешифрование AES проще реализовать на одной и той же платформе, такой как клиент Android и сервер Java, но иногда становится затруднительно расшифровать зашифрованный пароль AES в кроссплатформенной среде, такой как клиент Javascript, и сервер Java, такой как среда mvc весной, потому что, если какие-либо системные значения по умолчанию не совпадают, то расшифровка не удастся.

В этой статье мы будем создавать приложение с пружинным mvc и угловым js клиентом. У нас будет страница входа с формами ввода имени пользователя и пароля. Перед отправкой пароля на сервер, пароль будет зашифрован в javascript с использованием CryptoJS, и тот же зашифрованный пароль будет дешифрован в java, и будет выполнено сравнение для соответствия паролю. Мы будем генерировать соль и IV в javascript, а затем генерировать ключ с использованием функции PBKDF2 из ключевой фразы, соли и размера ключа. После этого мы будем шифровать открытый текст с использованием ключа и IV, и то же самое будет расшифровано в Java. Поэтому в основном мы будем разрабатывать механизм для совместимого шифрования AES с Java и Javascript.

Прежде чем продолжить, давайте поясним одну вещь: этот механизм только добавляет своего рода дополнительную безопасность во время проводной передачи данных (скорее всего), но не обеспечивает полную доказательную безопасность. Если вы не используете SSL, злоумышленник может просто выполнить атаку «человек посередине» и украсть данные, предоставив пользователю другой ключ.

Структура проекта

У нас есть весенняя загрузка и угловая настройка веб-приложения Js. Ниже приводится структура.

Aes Encryption в JavaScript

Для шифрования AES в javascript мы импортировали два файла js — crypto.js и pbkdf2.js нас есть AesUtil.js который имеет общие коды для выполнения шифрования и дешифрования. Здесь this.keySize — это размер ключа в 4-байтовых блоках. Поэтому, чтобы использовать 128-битный ключ, мы разделили число битов на 32, чтобы получить размер ключа, используемого для CryptoJS.

AesUtil.js

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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 расшифровывает пароль и отправьте расшифрованный пароль в ответе, который будет показан в окне предупреждения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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");
            }
        })
    };
 
    }]);

Расшифровка AES на Java

Сначала давайте реализуем класс контроллера, который будет перехватывать запрос на вход. Здесь мы жестко закодировали ключ. Этот ключ будет уникально генерироваться сервером и отправляться клиенту для каждого запроса входа. Клиент будет использовать один и тот же ключ, в то время как шифрование и сервер будут использовать один и тот же ключ для расшифровки. Убедитесь, что длина ключа составляет 16, потому что мы используем 128-битное шифрование. Запомните формат зашифрованного текста, который мы отправляем со стороны клиента — iv::salt::ciphertext . Текст расшифровывается в том же формате. У нас уже есть IV, соль и зашифрованный текст.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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);
    }
 
}

Ниже приведен класс утилит java для шифрования и дешифрования AES. Вы можете следить за шифрованием и дешифрованием AES в java для более подробного объяснения относительно последующей реализации.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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-шифрования и дешифрования

Запустите DemoApplication.java как приложение java и нажмите http: // localhost: 8080 . После появления страницы входа в систему вы можете ввести имя пользователя и пароль, нажать кнопку «Отправить», и вы сможете увидеть расшифрованный пароль в предупреждении.


/>

Вывод

В этом посте мы обсудили совместимое шифрование AES с Java и Javascript. Мы использовали библиотеку Crypto.js для выполнения этого шифрования в javascript. Полный исходный код можно найти здесь. Если у вас есть что-то, что вы хотите добавить или поделиться, пожалуйста, поделитесь этим ниже в разделе комментариев

См. Оригинальную статью здесь: шифрование AES в Javascript и дешифрование в Java

Мнения, высказанные участниками Java Code Geeks, являются их собственными.