Skip to content

Commit

Permalink
Improved handling of sensitive data (symmetric encryption of Garmin U…
Browse files Browse the repository at this point in the history
…serAccesstokenSecret and Polar UserAccessToken)
  • Loading branch information
r-follador committed Feb 15, 2024
1 parent b688b26 commit 46bc3ee
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 8 deletions.
1 change: 0 additions & 1 deletion src/main/java/com/cubetrek/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ public String dashboard(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Users user = (Users)authentication.getPrincipal();
ActivitityService.TopActivities bla = activitityService.getTopActivities(user);
TrackData.TrackMetadata bli = bla.alltimeDistance.get(0);
model.addAttribute("user", user);
model.addAttribute("activityHeatmapJSON", activitityService.getActivityHeatmapAsJSON(user, user.getTimezone()));
model.addAttribute("monthlyTotalJSON", activitityService.getMonthlyTotalAsJSON(user, user.getTimezone()));
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/cubetrek/database/AESEncryptionService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.cubetrek.database;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;

@Service
public class AESEncryptionService {

private static SecretKey secretKey;

public AESEncryptionService(@Value("${thirdparty.clientkey.key}") String encryptionSecret) {
secretKey = generateSecretKeyFromString(encryptionSecret);
}


//Generate a SecretKey from a String by hashing
public static SecretKey generateSecretKeyFromString(String password) {
MessageDigest sha = null;
try {
sha = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] key = sha.digest(password.getBytes());
key = Arrays.copyOf(key, 32);

return new SecretKeySpec(key, "AES");
}

public String encrypt(String data) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}

public String decrypt(String encryptedData) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] decodedBytes = Base64.getDecoder().decode(encryptedData);
byte[] decryptedBytes = cipher.doFinal(decodedBytes);
return new String(decryptedBytes);
}
}
38 changes: 36 additions & 2 deletions src/main/java/com/cubetrek/database/UserThirdpartyConnect.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Entity
@Getter
Expand All @@ -19,21 +21,24 @@ public class UserThirdpartyConnect {
@Column
private String garminUseraccesstoken;

@Convert(converter = DataEncryptDecryptConverter.class)
@Column
private String garminUseraccesstokenSecret;
private String garminUseraccesstokenSecret; //to be encrypted/decrypted

@Column
private boolean garminEnabled = false;

@Convert(converter = DataEncryptDecryptConverter.class)
@Column
private String polarUseraccesstoken;
private String polarUseraccesstoken; //to be encrypted/decrypted

@Column
private String polarUserid;

@Column
private boolean polarEnabled = false;


@Column(columnDefinition = "TEXT")
private String suuntoUseraccesstoken;

Expand All @@ -47,5 +52,34 @@ public class UserThirdpartyConnect {
private boolean suuntoEnabled = false;


@Component
@Converter
public static class DataEncryptDecryptConverter implements AttributeConverter<String, String> {

@Autowired
AESEncryptionService aesEncryptionService;

@Override
public String convertToDatabaseColumn(String attribute) {
if (attribute == null || attribute.isEmpty())
return "";
try {
return aesEncryptionService.encrypt(attribute);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

@Override
public String convertToEntityAttribute(String dbData) {
if (dbData == null || dbData.isEmpty())
return "";
try {
return aesEncryptionService.decrypt(dbData);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

}
2 changes: 1 addition & 1 deletion src/main/java/com/cubetrek/upload/StorageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ protected String createTitleFinal(LatLon highestPoint, TrackData trackData, Stri
if (reverseGeoCode!=null) {
return reverseGeoCode;
}
logger.info("@@ Reverse Geocode failed..."+trackData.getId());
logger.info("@@ Reverse Geocode failed... for Track ID "+trackData.getId());
return createTitlePreliminary(trackData, timeZone);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ public void downloadFile(OnEvent event) {
Users user = userThirdpartyConnect.getUser();

OAuthConsumer consumer = new DefaultOAuthConsumer(garminConsumerKey, garminConsumerSecret);
consumer.setTokenWithSecret(userThirdpartyConnect.getGarminUseraccesstoken(), userThirdpartyConnect.getGarminUseraccesstokenSecret());

String garminUserAccesstoken = userThirdpartyConnect.getGarminUseraccesstoken();
String garminUseraccesstokenSecret = userThirdpartyConnect.getGarminUseraccesstokenSecret();

consumer.setTokenWithSecret(garminUserAccesstoken, garminUseraccesstokenSecret);
HttpURLConnection request;
UploadResponse uploadResponse = null;
try {
Expand All @@ -70,7 +74,7 @@ public void downloadFile(OnEvent event) {

} catch (IOException | OAuthMessageSignerException | OAuthExpectationFailedException |
OAuthCommunicationException e) {
logger.error("GarminConnect: pull file failed: User id: "+user.getId()+", UserAccessToken '"+event.getUserAccessToken()+"', CallbackURL '"+event.getCallbackURL()+"'; Payload: "+event.getPayload());
logger.error("GarminConnect: pull file failed: User id: "+user.getId()+", UserAccessToken '"+event.getUserAccessToken().substring(0,10)+"xxx', CallbackURL '"+event.getCallbackURL()+"'; Payload: "+event.getPayload());
logger.error("GarminConnect", e);
return;
} catch (ExceptionHandling.FileNotAccepted e) {
Expand All @@ -81,7 +85,7 @@ public void downloadFile(OnEvent event) {
if (uploadResponse != null)
logger.info("GarminConnect: pull file successful: User id: "+user.getId()+"; Track ID: "+uploadResponse.getTrackID());
else
logger.error("GarminConnect: pull file failed: User id: "+user.getId()+", UserAccessToken '"+event.getUserAccessToken()+"', CallbackURL '"+event.getCallbackURL()+"'; Payload: "+event.getPayload());
logger.error("GarminConnect: pull file failed: User id: "+user.getId()+", UserAccessToken '"+event.getUserAccessToken().substring(0,10)+"xxx', CallbackURL '"+event.getCallbackURL()+"'; Payload: "+event.getPayload());
}

@Getter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public String connectToGarmin2(@RequestParam("oauth_token") String oauth_token,
userThirdpartyConnect.setGarminUseraccesstokenSecret(userAccessTokenSecret);
userThirdpartyConnectRepository.save(userThirdpartyConnect);
}
logger.info("GarminConnect User successfully linked Garmin Account: User id '"+user.getId()+"'; Garmin User Access Token: '"+userAccessToken+"'");
logger.info("GarminConnect User successfully linked Garmin Account: User id '"+user.getId()+"'; Garmin User Access Token: '"+userAccessToken.substring(0,10)+"xxx'");
return "redirect:/profile";
} catch (OAuthMessageSignerException | OAuthNotAuthorizedException | OAuthExpectationFailedException |
OAuthCommunicationException e) {
Expand Down

0 comments on commit 46bc3ee

Please sign in to comment.