-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added MessageService.java, MessageController.java, MessageServiceImpl…
….java with methods to send and read message
- Loading branch information
Showing
3 changed files
with
282 additions
and
9 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
src/main/java/ru/tvey/cloudserverapp/controller/MessageController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
22 changes: 18 additions & 4 deletions
22
src/main/java/ru/tvey/cloudserverapp/service/message/MessageService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
219 changes: 214 additions & 5 deletions
219
src/main/java/ru/tvey/cloudserverapp/service/message/MessageServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |