How to Read Encrypted Attachments using Gmail API?
- Digital Engineering
How to Read Encrypted Attachments using Gmail API?
What is Encrypted Attachment?
The attachments which are unreadable without a digital key. In generalize form, those files which contents can’t be read without having a digital key (password) are known as encrypted attachments.
Why do we need encrypted attachment?
- To protect the user’s personal information in the case if the data/device is being hacked.
- To keep sensitive personal information safe from bad actors.
- To be used in Government or Banking sectors, so that some account statements of users can be sent in encrypted pdfs.
What is Gmail API?
- The Gmail Api provides us with a RESTful access to the features you usually have with Gmail.
- Send and receive emails with attachments.
- Send and receive HTML emails.
- CRUD operations with messages, drafts, threads, and labels.
- Access control of your Gmail Inbox.
- Perform specific queries.
- To get specific attachment data of specific messages.
How to start reading Emails using Gmail API?
-
Create a Project at Google Api Console.
If you want to have access to your Gmail from your mobile or web app, you should start with Google Developer Console.
-
Enable Gmail API
After making project and necessary configuration Enter “Gmail API” in the search bar and click on it once found. Now, you need to enable the API for your project. For more information you can visit google support.
-
Credentials and authentication with OAuth 2.0
Once the Gmail API is enabled we need credentials . We can generate our credentials by going to the following site Google Developer Console. Then credentials -> create credentials -> oAuth client id->select appropriate configuration as per your project requirement -> download a JSON file with your credentials.
-
API Client Library.
Now create a Maven Spring boot project and add the following dependencies in pom.xml file.
123456789101112131415161718192021222324252627282930313233343536373839404142434445<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-oauth2-client</artifactId></dependency><dependency><groupId>com.google.apis</groupId><artifactId>google-api-services-oauth2</artifactId><version>v2-rev157-1.25.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.google.api-client</groupId><artifactId>google-api-client</artifactId><version>1.33.2</version></dependency><dependency><groupId>com.google.apis</groupId><artifactId>google-api-services-gmail</artifactId><version>v1-rev110-1.25.0</version></dependency><dependency><groupId>com.google.oauth-client</groupId><artifactId>google-oauth-client-jetty</artifactId><version>1.33.1</version></dependency><dependency><groupId>com.itextpdf</groupId><artifactId>kernel</artifactId><version>7.2.1</version></dependency><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.22</version></dependency></dependencies>Other dependencies are Spring boot starter web and spring boot starter test. These are necessary dependencies required for running a spring project.
-
Ready to Access Gmail Account.
In this we need to authorize access to your gmail account. For this we need to follow.Official Documentation.
- Now first you have to make an authorization flow . That contains Access token and Refresh Token.
- This is how we can authorize and set the flow of authorization.
- We also have to set the scope. Scopes are related to like what are the things we can do with gmail api. Some of the scopes are SEND, READ ONLY, INSERT, MODIFY. To know more about scopes Click Here.
- After authorization we get access token in response object that is used to create a gmail service builder.Now We build a gmail Builder that can give us the mails that we have to fetch.
- Now we can fetch mails by setting a query and the date between when we want the mail.
123456789101112131415161718192021222324252627public String getAuthorizedUrl() throws Exception{AuthorizationCodeRequestUrl authorizationUrl;// Define Required Scopes like, allow user to read only GMAIL messages, get its user info, user profile.Collection<String> SCOPES = new ArrayList<String>();SCOPES.add(GmailScopes.GMAIL_READONLY);SCOPES.add(Oauth2Scopes.USERINFO_EMAIL);SCOPES.add(Oauth2Scopes.USERINFO_PROFILE);if(flow == null) {// CREDENTIALS file path is the path of your JSON file which is downloaded from developer console.InputStream in = new FileInputStream(CREDENTIALS_FILE_PATH);clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(in));NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();// Create google authorization builder flow.flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES).setAccessType("offline").setApprovalPrompt("force").build();}authorizationUrl = flow.newAuthorizationUrl().setRedirectUri(REDIRECT_URL);return authorizationUrl.build();}1234567891011public void generateBuilder(String accesstoken) throws IOException, GeneralSecurityException{@SuppressWarnings("deprecation")GoogleCredential credentials = new GoogleCredential().setAccessToken(accesstoken);NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();// Create Gmail Builder service, APPLICATION_NAME could be any string value.service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credentials).setApplicationName(APPLICATION_NAME).build();} -
Fetch Messages From Inbox of User Email.
Endpoint: /mails?date1=?&date2=?&username=?
Controller: getMailsBetweenDate- Request end point controller, from here,next is to generate a gmail builder.
- Generate a gmail Builder.
- Now, get mails by defining a query to take only those emails which show transaction information.
- Now, create files/attachments at location /files/file_name.pdf
- If attachments are encrypted, than decrypt attachments as follows.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546@GetMapping("/mails")public ResponseEntity<?> getMailsBetweenDate(@RequestParam("date1") String date1, @RequestParam("date2") String date2,@RequestParam("useremail") String useremail){try {Map<String,String> response = new HashMap<>();User user1 = this.userRepository.findUserByEmail(useremail);// Check IF user is not found, then ask user to login at /login endpointif(user1 == null) {response.put("statusCode", "403");response.put("message","User not found, please login.");return ResponseEntity.status(HttpStatus.OK).body(response);}Long tokenExpiratonTime = user1.getExpirationTime();Long currentTime = new Date().getTime();boolean isTokenExpired = (tokenExpiratonTime-currentTime)<=0;// Check if token is expired, then generate a new access token with refresh tokenif(isTokenExpired) {String newAccessToken = this.gmailApiService.refreshAccessToken(user1.getRefreshtoken());user1.setAccesstoken(newAccessToken);user1.setExpirationTime(currentTime + (3600L*1000L));this.userRepository.save(user1);}// Generate a Gmail Builder servicethis.gmailApiService.generateBuilder(user1.getAccesstoken());// String name = user1.getName().replace(" ", "").substring(0, 4).toUpperCase();String name = ""; // first four letter of user name if any present in DB.String year = ""; // yyyy from your user DOB if any present in DB.// this.gmailApiService.getBirthDate(user1.getAccesstoken());List<Map<String,String>> messages = this.gmailApiService.getMails(date1,date2,name,year);if(messages.isEmpty()) {response.put("statusCode", "200");response.put("message","No Emails");return ResponseEntity.status(HttpStatus.OK).body(response);}// return all messages which are fetched.Map<String, List<Map<String,String>>> messageResponse = new HashMap<>();messageResponse.put("messages", messages);return ResponseEntity.status(HttpStatus.OK).body(messageResponse);}catch(Exception e) {System.out.println(e);return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());}}12345678910111213141516171819202122232425262728293031323334353637383940414243public String refreshAccessToken(String refreshToken) throws Exception{try {Map<String, Object> params = new LinkedHashMap<>();params.put("grant_type", "refresh_token");params.put("client_id", CLIENT_ID);params.put("client_secret", CLIENT_SECRET);params.put("refresh_token",refreshToken);StringBuilder postData = new StringBuilder();for (Map.Entry<String, Object> param : params.entrySet()) {if (postData.length() != 0) {postData.append('&');}postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));postData.append('=');postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));}byte[] postDataBytes = postData.toString().getBytes("UTF-8");URL url = new URL("https://accounts.google.com/o/oauth2/token");HttpURLConnection con = (HttpURLConnection) url.openConnection();con.setDoOutput(true);con.setUseCaches(false);con.setRequestMethod("POST");con.getOutputStream().write(postDataBytes);BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));StringBuffer buffer = new StringBuffer();for (String line = reader.readLine(); line != null; line = reader.readLine()) {buffer.append(line);}JSONObject json = new JSONObject(buffer.toString());String accessToken = json.getString("access_token");return accessToken;}catch (Exception ex) {ex.printStackTrace();}return null;}1234567891011public void generateBuilder(String accesstoken) throws IOException, GeneralSecurityException{@SuppressWarnings("deprecation")GoogleCredential credentials = new GoogleCredential().setAccessToken(accesstoken);NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();// Create Gmail Builder service, APPLICATION_NAME could be any string value.service = new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, credentials).setApplicationName(APPLICATION_NAME).build();}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485public List<Map<String,String>> getMails(String date1, String date2, String name, String year) throws IOException, Exception{// ₹ -> Query through which transaction mails are fetched.String query = "in:inbox " + " subject:(summary OR added OR invested OR deposit OR order OR statement OR credit OR repayment)" + " ((paid OR rs OR money OR credit OR debit OR inr OR wallet OR ₹ OR statement OR transaction) OR has:attachment OR has:drive AND -codechef AND -edureka)"+ " after:" + date1 + " before:"+date2;Gmail.Users.Messages.List request = service.users().messages().list(user).setQ(query).setMaxResults(5L);ListMessagesResponse messagesResponse = request.execute();request.setPageToken(messagesResponse.getNextPageToken());List<Message> messages = messagesResponse.getMessages();if(messages == null) {return new ArrayList<>();}int size = messages.size();List<Map<String,String>> messageList = new ArrayList<>();for(int i = 0;i<size;i++) {String messageId = messages.get(i).getId();Message message = service.users().messages().get(user, messageId).execute();String attachmentID = "";String fileName = "";String mimeType = message.getPayload().getMimeType().toString();List<MessagePart> messageParts = message.getPayload().getParts();// Here check the if messages contains any attachments or not.if(messageParts != null && (mimeType.contains("multipart/mixed") || mimeType.contains("multipart/related"))) {boolean isFound = false;for(int j = 0;j<messageParts.size(); j++) {String content = messageParts.get(j).getMimeType();// Get all pdf files and octet-stream files.if(content.contains("pdf") || content.contains("octet-stream")) {attachmentID = messageParts.get(j).getBody().getAttachmentId();fileName = messageParts.get(j).getFilename();}if(attachmentID != "" && fileName != "") {isFound = true;break;}}if(isFound) {String fileData = service.users().messages().attachments().get(user, messageId, attachmentID).execute().getData();// Save file at location /files/file_name.pdffileCreateService.createFile(fileData,fileName, name, year);}}Map<String,String> CustomMap = new HashMap<>();List<MessagePartHeader> headers = message.getPayload().getHeaders();String subject = "" ;String date = "";boolean isSubjectFound = false;boolean isDateFound = false;for(MessagePartHeader header: headers) {if(header.getName().toLowerCase().equalsIgnoreCase("subject")) {subject = header.getValue();isSubjectFound = true;}else if(header.getName().toLowerCase().equalsIgnoreCase("date")) {date = header.getValue();isDateFound = true;}if(isSubjectFound && isDateFound) {break;}}CustomMap.put("id", messageId);CustomMap.put("Subject",subject);CustomMap.put("Date",date);CustomMap.put("summary", message.getSnippet());messageList.add(CustomMap);}return messageList;}123456789101112131415161718192021222324public void createFile(String filedata, String fileName, String name, String year) throws IOException {String SRC = System.getProperty("user.dir") + FOLDER + "/" + fileName;String DESTINATION = System.getProperty("user.dir") + FOLDER + "/" + "decrypt_" + fileName;USER_PASSWORD = name+year;File file = new File(SRC);file.getParentFile().mkdirs();file.createNewFile();try ( FileOutputStream fos = new FileOutputStream(file, false);) {@SuppressWarnings("deprecation")byte[] decoder = Base64.decodeBase64(filedata);fos.write(decoder);decryptPDF_FILE(DESTINATION, SRC); // Fucntion to decrypt a encrypted attachment.System.out.println("Encrypted PDF File Saved");} catch (Exception e) {e.printStackTrace();}}12345678910111213141516171819202122232425262728293031323334353637383940public void decryptPDF_FILE(String DESTINATION, String SRC) throws Exception{boolean isProtectedPdf = true;// Check if the pdf which is saved is password protected or not.// if not, then do not create decrypt file.try (PDDocument pdfDocument = PDDocument.load(new File(SRC))){isProtectedPdf = false;}catch (Exception e) {System.out.println(e);}if(isProtectedPdf) {File file = new File(DESTINATION);file.getParentFile().mkdirs();file.createNewFile();manipulatePdf(DESTINATION,SRC); // Save decrypt pdf files.}}private void manipulatePdf(String DESTINATION, String SRC){// Create a passwords List, which contains all combinations of password for a pdf files.// for suppose,// 1. First Four letter of user name in small letter + last four digit of the mobile number.// 2. First Four letter of user name in small letter + Year of DOB in YYYY format.List<String> passwords = new ArrayList<>();int size = passwords.size();boolean isFound = false;int index = 0;while(index<size && !isFound) {try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(SRC, new ReaderProperties().setPassword(passwords.get(index).getBytes())).setUnethicalReading(true), new PdfWriter(DESTINATION))) {pdfDoc.close();isFound = true;}catch(Exception e) {}index++;}if(!isFound) {System.out.println("password not Detected");}}
Pros of using Gmail API
-
Time Saving
-
Analytics
-
Security
We need less back and forth communication between servers . So it can save lots of time and is a faster method.
An gmail api usually includes statistical insights into recipient-email interactions like email deliverability, open rate, unsubscribes, and more.
When using email API to send emails, you need to use an API key. This protects your website or application from phishers or spammers.
Cons of using Gmail API
-
Requirement of Technical Knowledge
-
Needs Repeated Updates
Setting up and running email APIs is difficult since it requires extensive knowledge of a programming language and client libraries. Even if you have extensive documentation, understanding and putting it into practise for email sending can be difficult.
API versions, unlike SMTP, change with each passing year. As a result, you’ll need to keep an eye out for upgrades and make adjustments to accommodate the new protocols.
Related content
Auriga: Leveling Up for Enterprise Growth!
Auriga’s journey began in 2010 crafting products for India’s