Skip to content

Commit

Permalink
added MessageService.java, MessageController.java, MessageServiceImpl…
Browse files Browse the repository at this point in the history
….java with methods to send and read message
  • Loading branch information
St3plox committed Jul 21, 2023
1 parent 9b9e63d commit 2edfdd7
Show file tree
Hide file tree
Showing 3 changed files with 282 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package ru.tvey.cloudserverapp.controller;

import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import ru.tvey.cloudserverapp.entity.messaging.Message;
import ru.tvey.cloudserverapp.service.message.MessageService;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.time.LocalDate;

@RestController
@RequestMapping("/cloud/message")
@AllArgsConstructor
public class MessageController {

private final MessageService messageService;


@PostMapping
public ResponseEntity<HttpStatus> sendMessage(Authentication auth,
@RequestParam @NotBlank String text,
@RequestParam LocalDate date,
@RequestParam String groupId,
@RequestParam(required = false) String fileId) throws Exception {

Long idOfGroup = Long.valueOf(groupId.toString());
Long idOfFile = null;
if (fileId != null) {
idOfFile = Long.valueOf(fileId.toString());
}
messageService.sendMessage(auth, text, date, idOfGroup, idOfFile);
return new ResponseEntity<>(HttpStatus.ACCEPTED);
}

@GetMapping("/{id}")
public ResponseEntity<Message> getMessage(@PathVariable Long id, Authentication auth)
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException {
return new ResponseEntity<>(messageService.getMessage(auth, id), HttpStatus.ACCEPTED);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
package ru.tvey.cloudserverapp.service.message;

import org.springframework.security.core.Authentication;
import ru.tvey.cloudserverapp.entity.FileData;
import ru.tvey.cloudserverapp.entity.messaging.Message;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.time.LocalDate;
import java.util.List;

public interface MessageService {
void sendMessage(Authentication auth, Long groupId,
String text, FileData file);
void sendMessage(Authentication auth,
String text,
LocalDate date,
Long groupId,
Long fileId) throws Exception;

Message getMessage(Authentication auth, long id);
Message getMessage(Authentication auth, long id) throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidKeySpecException, InvalidAlgorithmParameterException;

void deleteMessage(Authentication auth, long id);

List<LocalDate> getLastNMessages(Authentication auth, long groupId);
}
Original file line number Diff line number Diff line change
@@ -1,25 +1,234 @@
package ru.tvey.cloudserverapp.service.message;

import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import ru.tvey.cloudserverapp.entity.FileData;
import ru.tvey.cloudserverapp.datacache.CacheStore;
import ru.tvey.cloudserverapp.encryption.*;
import ru.tvey.cloudserverapp.entity.KeyPairEntity;
import ru.tvey.cloudserverapp.entity.UserKeyIvPair;
import ru.tvey.cloudserverapp.entity.messaging.Group;
import ru.tvey.cloudserverapp.entity.messaging.Message;
import ru.tvey.cloudserverapp.entity.user.User;
import ru.tvey.cloudserverapp.repository.FileDataRepository;
import ru.tvey.cloudserverapp.repository.KeyPairEntityRepository;
import ru.tvey.cloudserverapp.repository.MessageRepository;
import ru.tvey.cloudserverapp.security.SecurityConstants;
import ru.tvey.cloudserverapp.service.EntityService;
import ru.tvey.cloudserverapp.service.file.FileService;
import ru.tvey.cloudserverapp.service.group.GroupService;
import ru.tvey.cloudserverapp.service.user.UserService;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.time.LocalDate;
import java.util.Base64;
import java.util.List;

@Service
@AllArgsConstructor
public class MessageServiceImpl implements MessageService {

private final MessageRepository messageRepository;

private final GroupService groupService;

private final FileService fileService;

private final UserService userService;

private final AsymmetricKeyGenerator asymmetricKeyGenerator;

private final SymmetricKeyGenerator symmetricKeyGenerator;

private final CacheStore<UserKeyIvPair> keyCacheStore;

private final EntityService entityService;

private final AsymmetricCipher asymmetricCipher;

private final FileDataRepository fileDataRepository;

private final SymmetricCipher symmetricCipher;

private final KeyPairEntityRepository keyPairEntityRepository;


@Override
public void sendMessage(Authentication auth, Long groupId, String text, FileData file) {
public void sendMessage(Authentication auth, String text,
LocalDate date, Long groupId, Long fileId) throws Exception {

long senderId = userService.getUser(auth.getName()).getId();

if (groupService.getUserIdOfGroup(senderId, groupId).isEmpty()) {
throw new RuntimeException("message sender is not in group");
}

Message message = new Message();

message.setDate(date);


if (fileId != null) {
message.setFileId(fileService.serveFile(fileId, auth));
}
Group group = groupService.getGroup(groupId);
message.setGroupId(group);
message.setSenderName(auth.getName());

String keyForSenderCache = auth.getName() + "." + SecurityConstants.CACHE_SECRET;


KeyPairEntity senderKeyPairEntity = (KeyPairEntity) entityService.
unwrapEntity(keyPairEntityRepository.
findKeyPairEntityByUserId(senderId));

byte[] publicKeyBytes = senderKeyPairEntity.getPublicKey();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey senderPublicKey = keyFactory.generatePublic(keySpec);


//check for existing keys
if (keyCacheStore.get(keyForSenderCache) != null) {

UserKeyIvPair secretKeyIv = keyCacheStore.get(keyForSenderCache);

byte[] encryptedSecretKeyB = secretKeyIv.getKey();
byte[] ivB = secretKeyIv.getIv();

byte[] privateKeyBytes = senderKeyPairEntity.getPrivateKey();
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey senderPrivateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));

byte[] decKeyB = asymmetricCipher.doCrypto(Cipher.DECRYPT_MODE, senderPrivateKey, encryptedSecretKeyB);

byte[] encTextBytes = symmetricCipher
.doCrypto(text.getBytes(),
EncryptionConstants.AES_ALGO,
new SecretKeySpec(decKeyB, "AES"),
new IvParameterSpec(ivB),
Cipher.ENCRYPT_MODE);

message.setText( Base64.getEncoder().encodeToString(encTextBytes));
messageRepository.save(message);
return;
}

//encrypting message with generated K
SecretKey secretKey = symmetricKeyGenerator.getKey();
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

byte[] encryptedTextBytes = symmetricCipher.doCrypto(text.getBytes(), EncryptionConstants.AES_ALGO,
secretKey, ivParameterSpec, Cipher.ENCRYPT_MODE);
text = Base64.getEncoder().encodeToString(encryptedTextBytes);

message.setText(text);

//Encrypting every group user with his public key


byte[] encryptedKeyBytes = asymmetricCipher.doCrypto(Cipher.ENCRYPT_MODE, senderPublicKey, secretKey.getEncoded());

UserKeyIvPair secretKeyIvPair = new UserKeyIvPair(encryptedKeyBytes, iv);// secretKeyIvString = key.iv

keyCacheStore.add(keyForSenderCache, secretKeyIvPair);

List<Long> groupUserIds = groupService.getIdsOfGroup(groupId);
// User[] users = new User[groupUserIds.size()];

for (long id : groupUserIds) {
if (id != senderId) {
User user = userService.getUser(id);
StringBuilder keyCacheSBuilder = new StringBuilder();
keyCacheSBuilder.append(user.getUsername());
keyCacheSBuilder.append('.');
keyCacheSBuilder.append(SecurityConstants.CACHE_SECRET);

KeyPairEntity userKPE = (KeyPairEntity) entityService.
unwrapEntity(keyPairEntityRepository.
findKeyPairEntityByUserId(user.getId()));

byte[] userPublicKeyBytes = userKPE.getPublicKey();
X509EncodedKeySpec userKeySpec = new X509EncodedKeySpec(userPublicKeyBytes);
KeyFactory userKeyFactory = KeyFactory.getInstance("RSA");
PublicKey userPublicKey = userKeyFactory.generatePublic(userKeySpec);

byte[] userEncryptedKeyBytes = asymmetricCipher
.doCrypto(Cipher.ENCRYPT_MODE, userPublicKey, secretKey.getEncoded());


UserKeyIvPair userKeyIvPair = new UserKeyIvPair(userEncryptedKeyBytes, iv);

keyCacheStore.add(keyCacheSBuilder.toString(), userKeyIvPair);
}
}
messageRepository.save(message);
}
//cacheKey = "username.cache_secret"

@Override
public Message getMessage(Authentication auth, long id) {
return null;
public Message getMessage(Authentication auth, long messageId) throws
NoSuchPaddingException, IllegalBlockSizeException,
NoSuchAlgorithmException, BadPaddingException,
InvalidKeyException, InvalidKeySpecException,
InvalidAlgorithmParameterException {

Message message = (Message) entityService.unwrapEntity(messageRepository.findById(messageId));

User user = userService.getUser(auth.getName());
List<Long> userIds = groupService.getIdsOfGroup(message.getGroupId().getId());

if(!userIds.contains(user.getId())){
throw new RuntimeException("user does not belong to the group");
}

String text = message.getText();

UserKeyIvPair userKeyIvPair = keyCacheStore.get(auth.getName() + "." + SecurityConstants.CACHE_SECRET);

if (userKeyIvPair == null) {
throw new RuntimeException("there is no such message key in the cache");
}

KeyPairEntity keyPair = (KeyPairEntity) entityService.
unwrapEntity(keyPairEntityRepository.findKeyPairEntityByUserId(user.getId()));

KeyFactory kf = KeyFactory.getInstance("RSA");

PrivateKey privateKey = kf.generatePrivate
(new PKCS8EncodedKeySpec(keyPair.getPrivateKey()));

byte[] decSymKeyBytes = asymmetricCipher.doCrypto(Cipher.DECRYPT_MODE, privateKey, userKeyIvPair.getKey());

SecretKey secretKey = new SecretKeySpec(decSymKeyBytes, "AES");

byte[] textBytes = Base64.getDecoder().decode(text);

byte[] decTextBytes = symmetricCipher.doCrypto(textBytes,
EncryptionConstants.AES_ALGO,
secretKey,
new IvParameterSpec(userKeyIvPair.getIv()),
Cipher.DECRYPT_MODE);

message.setText(new String(decTextBytes));

return message;
}

@Override
public void deleteMessage(Authentication auth, long id) {

}

@Override
public List<LocalDate> getLastNMessages(Authentication auth, long id) {
return null;
}
}

0 comments on commit 2edfdd7

Please sign in to comment.