i'm developping a web application in java. During develop of email sender i have to specify credentials (email and password) of my host.
i was doing something like this:
public void sendEmail(String toAddress,
String subject, String message) throws AddressException,
MessagingException {
// sets SMTP server properties
Properties properties = new Properties();
properties.put("mail.smtp.host", smtp.gmail.com);
properties.put("mail.smtp.port", 587);
properties.put("mail.smtp.auth", "true");
properties.put("mail.smtp.starttls.enable", "true");
// Sender's email ID needs to be mentioned
final String username = "user_mail";
final String password = "password_mail";
// creates a new session with an authenticator
Authenticator auth = new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};
Session session = Session.getInstance(properties, auth);
// creates a new e-mail message
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(username));
InternetAddress[] toAddresses = { new InternetAddress(toAddress) };
msg.setRecipients(Message.RecipientType.TO, toAddresses);
msg.setSubject(subject);
msg.setSentDate(new Date());
// set plain text message
msg.setText(message);
// sends the e-mail
Transport.send(msg);
}
i think that the better practice to use credentials is to save them in an xml file, and calling them back using some method, maybe Context's method.
Can someone explain me (or give me some link where i can see) how can i do that or some better?
any help will be appreciated.
One interesting way to do it is to use Vault. It's a system for managing "secrets" like credentials, API keys, etc.
There are other things you can do like write some code to automatically encrypt and decrypt the password.
As a rule of thumb, it's also important to make sure that you don't install the JDK on a production machine. If you do, a hacker would get access to javap which is the Java disassembler. The output wouldn't be that good, but probably enough to get a ballpark idea of the method you are using to secure the password.
It depends how much effort you want to place in the hiding the secrets. At the end - you have to store the credentials somewhere. You can encrypt them, but you have to store the encryption key somewhere anyway.
Many (even mature) systems simply store the username / password as plains in a configuration file and rely on the hosting to make the system secure. Some systems encrypt the passwords, but the encryption key is available anyway somewhere in a confguration and keystore. It makes retrieving your email password a little bit more complex, but not much.
The most secure way I've seen was creating a key based on a configured secret (password) and some environment property (IP, MAC, ...).
So - lets be practical. Storing your SYSTEM credentials as plains in most of the cases may be ok. You have to secure access to your system so there should be no way to get the configuration file.
Related
So i am trying to send a pdf through mail using Gmail smtp port 465 but it keeps on throwing this error,
googled it but couldn't solve this.
i don't understand whats wrong? help me on this?
thanks in advance
tried:
1.changing ports
2.tried to correct something on certificates didn't work
error:
Exception in thread "main" java.lang.RuntimeException: javax.mail.MessagingException: Exception reading response;
nested exception is:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sample.pdf.PDFMailing.pdfMail(PDFMailing.java:72)
at com.sample.pdf.GeneratePdf.addDataToPdf(GeneratePdf.java:42)
at com.sample.pdf.Report.main(Report.java:47)
PDFMailing.java
public static void pdfMail(String file){
//Sender email-ID and Password.
final String senderEmail="xxxxxx";//Sender Mail ID
final String password="xxxxx";//Sender Mail ID Password.
//setting the Properties.
Properties props=new Properties();
props.put("mail.smtp.host", "smtp.gmail.com"); //SMTP Host
props.put("mail.smtp.socketFactory.port", "465"); //SSL Port
props.put("mail.smtp.socketFactory.class",
"javax.net.ssl.SSLSocketFactory"); //SSL Factory Class
props.put("mail.smtp.auth", "true"); //Enabling SMTP Authentication
props.put("mail.smtp.port", "465"); //SMTP Port
//Authenticating the mailID of the sender.
Authenticator auth = new Authenticator() {
//override the getPasswordAuthentication method
protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
return new javax.mail.PasswordAuthentication(senderEmail, password);
}
};
//Creating and getting the Session Object.
Session session=Session.getInstance(props, auth);
//Setting the From, To, Subject, MessageBody.
try{
Message message=new MimeMessage(session);
message.setFrom(new InternetAddress(senderEmail));//Sender Mail ID
message.setRecipients(Message.RecipientType.TO,InternetAddress.parse("harshapirate#gmail.com"));//Receiver Mail ID
message.setSubject("Sample ECO PDF file");
message.setText("This a Sample ECO PDF file.");
MimeBodyPart bodyPart=new MimeBodyPart();
Multipart multipart=new MimeMultipart();
bodyPart.setText("This is multipart Text.");
//Attachments for any file.
MimeBodyPart pdfAttachment=new MimeBodyPart();
pdfAttachment.attachFile(file);
//Attach the Body part to the Multipart.
multipart.addBodyPart(bodyPart);
multipart.addBodyPart(pdfAttachment);
//Associate multipart to the message.
message.setContent(multipart);
System.out.println("Sending mail is in process.......");
//sending the message to-address mail.
Transport.send(message);
System.out.println("Mail has been sent sucessfully.");
}
catch(Exception e){
throw new RuntimeException(e);
}
}
I presume that is Java 8.0.251. Since you have the latest Java 8 release then this should not be happening:
The root certificates in a 8.0.251 keystore should all be current.
The real smtp.google.com should be using well-known root certificates.
The real smtp.google.com would not be presenting the cert chain in the wrong order.
So I think that something is else "getting in the way". Likely explanations include:
Something that is sending outgoing email connections through an SMTP proxy; e.g. a firewall or anti-virus product.
You are running on an application server that has overridden the set of trusted certificates provided by the JDK.
It is also possible that something has subverted your DNS or IP routing and you are talking to a spoofed "smtp.gmail.com" server.
Check the following:
Check if latest JDK is installed (Command: java -version) so that it has latest
certificates and updated CA's.
Check if there are multiple JDKs or JREs installed: If so remove them.
Check if JAVA_HOME is pointing to the latest JDK you installed.
(Command: echo $JAVA_HOME in linux Or echo %JAVA_HOME% in Windows command prompt)
Check if your system date and time are correct.
If above is verified then your Java installation is fine.
Code:
I see a missing property in your code: props.put("mail.smtp.starttls.enable", "true").
Add it and try.
Hope you are able to access Gmail from your browser at least. This confirms that your machine is able to access Gmail servers - Though not an exact test for SMTP, but it is still good to test.
If you are running inside Tomcat, then ensure you have the latest version and hope it has its certificate store not customized or altered in some way.
If everything above is OK, then either you are behind some proxy or you are hacked.
Maybe check for a system property or other way that someone has defined a different trust store e.g. with: -Djavax.net.ssl.trustStore=somefilename.jks
See: https://www.baeldung.com/java-keystore-truststore-difference
But the default truststore should be $JAVA_HOME/jre/lib/security/cacerts (but its contents vary between the Oracle JDK and OpenJDK - this was later fixed, see: https://openjdk.java.net/jeps/319 )
Inspecting/modifying the contents of the store can be done with the command line tool: keytool or a GUI like: https://keystore-explorer.org/
Before you go crazy try starting your application with the following extra System Property for java (-Dsystempropertyname=value):
-Djavax.net.debug=all
Then you will exactly see all details of the used trust store and offered certificates from the server, details of the SSL/TLS handshake and even all data encrypted/decrypted.
For all the information there is on this subject see the JSSE Reference Guide for your Java version, e.g.: https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html
The same kind of code, I have written earlier and is in working state. Could you please give a try on using this code below:
Also, check the proper version of Java installed 8+ in your system, with all the Environment variables properly set and required for your project. If any proxy settings exists in your system, try to remove it and then again running the code.
//this will work only for gmail.
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import com.[MyProjectRelatedImports].pojo.EmployeeDetails;
public class SendEmail {
public static void sendMail(List<EmployeeDetails> emplDetails) {
Address[] to = new Address[emplDetails.size()];
try {
for (int i = 0; i < to.length; i++) {
to[i] = new InternetAddress(emplDetails.get(i).getEmail());
}
} catch (MessagingException e) {
e.printStackTrace();
}
// Recipient's email ID needs to be mentioned.
String tom = "xyz#gmail.com";// address of recipient
// Sender's email ID needs to be mentioned
String from = "abc#gmail.com"; //address of sender
// Assuming you are sending email from localhost
String host = "smtp.gmail.com";
// Get system properties
Properties properties = System.getProperties();
// Setup mail server
properties.setProperty("mail.smtp.host", host);
properties.put("mail.smtp.starttls.enable", "true");
properties.put("mail.smtp.host", host);
properties.put("mail.smtp.user", "emailID"); // User name
properties.put("mail.smtp.password", "password"); // password
properties.put("mail.smtp.port", "587");// default mail submission port
properties.put("mail.smtp.auth", "true");
// Get the default Session object.
Session session = Session.getDefaultInstance(properties, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("emailID", "password"); // username will be your email address and password same as your Gmail account.
// And if your account is protected with 2 step verification then you need to generate the app password from the link provided
//https://security.google.com/settings/security/apppasswords
}
});
try {
// Create a default MimeMessage object.
MimeMessage message = new MimeMessage(session);
// Set From: header field of the header.
message.setFrom(new InternetAddress(from));
// Set To: header field of the header. //message.addRecipient(Message.RecipientType.TO, new InternetAddress(tom));
//Send Email to multiple recipients
message.addRecipients(Message.RecipientType.TO, to);
// Set Subject: header field
message.setSubject("Subject text via Java Class");
// Now set the actual message
message.setText("Message Content should be written here!!! Regards: Pratishtha Sharma ");
// Send message
Transport.send(message);
System.out.println("Sent message successfully....");
} catch (MessagingException mEx){
mEx.printStackTrace();}
}
}
I'm trying to send an email via java.mail SMTP. I have 2 mail accounts using the same provider and settings:
using a short password with no special characters
using a long password with special characters (* > / % !)
The first one works. The second one says 535 Authentication credentials invalid.
I'm using java.mail 1.5.
Here's my code:
Properties props = new Properties();
props.put("mail.smtps.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", "smtp.1und1.de");
props.put("mail.smtp.port", "465");
Session session = Session.getInstance(props, new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("foo#example.org", "$§$&/&%§$!><>");
}
});
Transport transport = session.getTransport("smtps");
transport.connect("smtp.1und1.de", username, password);
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress("foo#example.org"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("bar#example.org"));
message.setSubject("subject");
message.setText("text");
transport.sendMessage(message, message.getAllRecipients());
Do I need to encode the password somehow?
Thanks in advance.
EDIT:
I now tried to login via telnet. Same problem. So this isn't related to Java, but it may be some common SMTP issue.
Using the E-Mail account using Apple Mail on Mac and iPhone works without any issues.
If you have non-ASCII special characters, you'll need to use JavaMail 1.6 and set the mail.mime.allowutf8 Session property. And the server needs to support the SMTPUTF8 extension.
I am using the scopes: mail.send, mail.readwrite, mail.read, offline_access, openid, email, and profile (although I am fairly confident I do not need all of these -> goal is to read inbox and send emails, while also getting email and name if they exist).
I am then connecting to SMTP server with the following code:
OAuth2Authenticator.connectToSmtp("smtp-mail.outlook.com",
587,
user.getOutlookUid(),
accessToken,
true);
The code that actually connects to the server is here:
public static SMTPTransport connectToSmtp(String host, int port, String userEmail, String oauthToken, boolean debug)
throws Exception {
Properties props = new Properties();
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.starttls.required", "true");
props.put("mail.smtp.sasl.enable", "true");
props.put("mail.smtp.sasl.mechanisms", "XOAUTH2");
props.put("mail.smtp.sasl.mechanisms.oauth2.oauthToken", oauthToken);
Session session = Session.getInstance(props);
session.setDebug(debug);
URLName unusedUrlName = null;
SMTPTransport transport = new SMTPTransport(session, unusedUrlName);
// If the password is non-null, SMTP tries to do AUTH LOGIN.
String password = "";
transport.connect(host, port, userEmail, password);
return transport;
}
Okay, now I can get to the most frustrating part... I have used the "connectToSMTP" method to connect to Gmail and it worked perfectly.
OAuth2Authenticator.connectToSmtp("smtp.gmail.com",
587,
user.getGoogleUid(),
accessToken,
true);
So ultimately my question is "what am I doing wrong?" or "what can I update to be able to send emails through Outlook"? I have seen that Outlook has a REST API, but that is plan B. Is there something different about Outlook vs Gmail?
Some things I have considered:
Scope did not request enough access (so I probably am asking for too much now)
access_token was stored incorrectly or encoded in some way (tried decoding it from base_64 which provided nothing). I am able to use my refresh_token to update the access_token so that tells me I am probably storing them correctly.
I tried passing null for the password. Also passed in the actual password and that WORKED, but I have the access_token and refresh_token so I shouldn't need to ask for their explicit password. Also this would be dangerous and sketchy to ask of users.
I tried manually connecting to the smtp server using "openssl s_client -crlf -starttls smtp -connect smtp-mail.outlook.com:587", but it seemed to think my access_token was wrong "535 5.0.0 OAuth failed: OAuth authentication failed due to Invalid token. Code -2147184118" That number when taken two's complement and converted to hex is 0x8004920a. Helped in searches but was to no avail.
I have done a lot of searching for this and will continue now to post this everywhere. A lot of resources for it working with Gmail, but as previously stated I already have it working for Gmail. Something seems different for Outlook. Also I have encountered lots of posts regarding email forwarding on an email client... I am semi-creating an email client so going through outlook.com settings doesn't help me.
Another concern that a buddy of mine had was that my access token was really long, contributing to what the manual smtp server claimed. It is 1188 characters long. It's something like 'EwB4Aul3BAAUo4xeBIbHjhBxWOFekj4Xy2...x9stHxi2K/VFggE=' (obviously I hid most of the characters).
Preemptive THANK YOU for anyone who offers advice or finds my issue. Especially why I can pass in the email password and that fails, but using the oauth access_token fails.
Try using "pop3://user:password#host:port/INBOX". to retrieve email from the inbox ,
more information can be found out https://javamail.java.net/docs/api/com/sun/mail/pop3/package-summary.html
hope this helps : https://technet.microsoft.com/en-ca/dn44016. Properties props = new Properties();
props.put("mail.imap.ssl.enable", "true"); // required for Gmail
props.put("mail.imap.auth.mechanisms", "XOAUTH2");
Session session = Session.getInstance(props);
Store store = session.getStore("imap");
store.connect("imap.gmail.com", username, oauth2_access_token);
We are developing a simple web application in JSF in which there is a need to include a "forget password" module. For the sake of demonstration and simplicity, I tried the following code in Java Servlet. It can send a mail to Gmail and works just fine there is no problem at all. The following is the complete Servlet code.
public class MailClient extends HttpServlet {
private class SMTPAuthenticator extends Authenticator {
private PasswordAuthentication authentication;
public SMTPAuthenticator(String login, String password) {
authentication = new PasswordAuthentication(login, password);
}
#Override
protected PasswordAuthentication getPasswordAuthentication() {
return authentication;
}
}
protected void processRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
String from = "bhaveshp1980#gmail.com";
String to = "bhaveshp1980#gmail.com";
String subject = "A mail from Java.";
String message = "My first mail from Java.";
String login = "bhaveshp1980#gmail.com";
String password = "password";
Properties props = new Properties();
props.setProperty("mail.host", "smtp.gmail.com");
props.setProperty("mail.smtp.port", "587");
props.setProperty("mail.smtp.auth", "true");
props.setProperty("mail.smtp.starttls.enable", "true");
Authenticator auth = new SMTPAuthenticator(login, password);
Session session = Session.getInstance(props, auth);
MimeMessage msg = new MimeMessage(session);
try {
msg.setText(message);
msg.setSubject(subject);
msg.setFrom(new InternetAddress(from));
msg.addRecipient(Message.RecipientType.TO,
new InternetAddress(to));
Transport.send(msg);
} catch (MessagingException ex) {
Logger.getLogger(MailClient.class.getName()).
log(Level.SEVERE, null, ex);
}
} finally {
out.close();
}
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
#Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
#Override
public String getServletInfo() {
return "Short description";
}
}
Now, the first question is that as soon as a user enters his valid email address, a verification code should be submitted to a specific message server (Gmail, Yahoo and so on) without asking the user for his password (Which is not the case of the above code) which is essential in implementing the "forget password" module in a web application.
The second question is that the above code is bound to sending a mail only to Gmail. If I want to send a mail to some other message server say Yahoo, the statement
props.setProperty("mail.host", "smtp.gmail.com");
needs to be changed to
props.setProperty("mail.host", "smtp.mail.yahoo.com");
[and port no too, regarding others] means that a specific message server to which a message has to be sent need to be recognized properly. Which is the best way to do so?. What is the best method(s) to overcome these issues, please.
Do you change your SMTP settings in your email client each time you send a mail to a different mail provider? No. You don't set the SMTP server to gmail when sending a mail to a gmail.com address. And then set it to yahoo when sending a mail to a yahoo.com address. You set it to your email provider SMTP server, and this SMTP server sends the mail to the appropriate location.
Just choose an SMTP server which agrees to send mails from your application. Sending a dozen per day will be OK with any SMTP provider. But if you send thousands a day, then you could have problems with your provider. Just ask your hosting provider how it goes with outgoing mails (how much it costs, how many per day are accepted, is there a bandwidth limit, etc.)
So you can't do that. That's the whole point of keeping people from spamming, masquerading as users they aren't, and lots of other nasty things.
You need to setup a SMTP server or get your domain hosted on a mail service that you can send mail through. Maybe your ISP or hosting provider provides this already and you just need to sign up for that. Otherwise, there are plenty of places out there that will allow you to send mail from your domain. I use:
http://www.authsmtp.com/
Or you can have google host your domain for free, but they put a limit on the number of messages you can send per day which last time I checked was like 100. So if your site plans on sending more mail than that you need to bump up to paid service like authsmtp.
http://www.google.com/apps/intl/en/business/smb/email.html#utm_campaign=en&utm_source=en-ha-na-us-sk&utm_medium=ha&utm_term=%2Bemail%20%2Bhosting
Basically you need to find a mail server that is in charge of your MX record so emails sent to your domain will get routed to those servers, and so you application can send email from your domain and not get black listed as a spammer because you are doing naughty things.
While you can setup your own smtp server it's just so much easier to use a service, and that gives you a professional look for doing things like customer service, and responding to people when you mail comes from a domain associated with your website and not some johnnyappleseed#gmail.com
You need to do some basic research about how email works from a technical perspective as well. That will help you understand why what I'm talking about is important.
The authentication part you cannot get around. This is configured in the remote server, so unless you use your own mailserver, you have to do what gmail asks you to do. But instead of hardcoding the login/password, you could store them in an (encrypted?) properties file and modify that whenever you want to use another mailserver.
You need to generate a random token and send it as part of the
message. You store the token in the database with an association to
the user's profile. When the user returns to your site with that
token (by entering it into a form, or by clicking a link with that
token as GET parameter) you can be sure the user is authentic and
offer the reset password dialog.
Don't send the message to the smtp server of the recipient, but to
the smtp server of your from email address. So sending it to gmail
would be fine. I would make stmp host url, username and password
configurable, e.g. in a property file.
Currently I am using Commons Email to send email messages, but I have not been able to find a way to share smtp connections between emails sent. I have code like the following:
Email email = new SimpleEmail();
email.setFrom("example#example.com");
email.addTo("example#example.com");
email.setSubject("Hello Example");
email.setMsg("Hello Example");
email.setSmtpPort(25);
email.setHostName("localhost");
email.send();
Which is very readable, but is slow when I do a large amount of messages, which I believe is the overhead of reconnecting for each message. So I profiled it with the following code and have found that using the reusing the Transport makes things about three times faster.
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
Session mailSession = Session.getDefaultInstance(props, null);
Transport transport = mailSession.getTransport("smtp");
transport.connect("localhost", 25, null, null);
MimeMessage message = new MimeMessage(mailSession);
message.setFrom(new InternetAddress("example#example.com"));
message.addRecipient(Message.RecipientType.TO, new InternetAddress("example#example.com"));
message.setSubject("Hello Example");
message.setContent("Hello Example", "text/html; charset=ISO-8859-1");
transport.sendMessage(message, message.getAllRecipients());
So I was wondering if there was a way to make Commons Email reuse an SMTP connection for multiple email sendings? I like the Commons Email API better, but the performance is kind of painful.
Thanks,
Ransom
I came up with the following solution after digging into the commons source itself. This should work, but there may be better solutions I do not know of
Properties props = new Properties();
props.setProperty("mail.transport.protocol", "smtp");
Session mailSession = Session.getDefaultInstance(props, null);
Transport transport = mailSession.getTransport("smtp");
transport.connect("localhost", 25, null, null);
Email email = new SimpleEmail();
email.setFrom("example#example.com");
email.addTo("example#example.com");
email.setSubject("Hello Example");
email.setMsg("Hello Example");
email.setHostName("localhost"); // buildMimeMessage call below freaks out without this
// dug into the internals of commons email
// basically send() is buildMimeMessage() + Transport.send(message)
// so rather than using Transport, reuse the one that I already have
email.buildMimeMessage();
Message m = email.getMimeMessage();
transport.sendMessage(m, m.getAllRecipients());
Could we not achieve this easier by getting the mail session from the first email using getMailSession() and putting it to all subsequent messages using setMailSession() ?
Not 100% sure what the
Please note that passing a username and password (in the case of mail authentication) will create a new mail session with a DefaultAuthenticator. This is a convience but might come unexpected. If mail authentication is used but NO username and password is supplied the implementation assumes that you have set a authenticator and will use the existing mail session (as expected).
from the javadoc means, though :-/
http://commons.apache.org/email/api-release/org/apache/commons/mail/Email.html#setMailSession%28javax.mail.Session%29
see also:
https://issues.apache.org/jira/browse/EMAIL-96
not sure how to continue here...