security exception when loading web image in jar
Question
I am trying to create a java jar Applet that will run in a browser, download an image from a URL, and display it to the user. My implementation is:
try {
String imageURL = "http://www.google.com/intl/en_ALL/images/logo.gif";
URL url = new URL(imageURL);
img = ImageIO.read(url);
} catch (IOException e) {
System.out.println(e);
}
But it gives me a security exception:
java.security.AccessControlException: access denied (java.net.SocketPermission www.google.com:80 connect,resolve)
Solution:
I have implemented Knife-Action-Jesus's suggestion, and it works in a web browser (but not using the applet viewer).
Only with the applet viewer I still encounter:
java.security.AccessControlException: access denied (java.net.SocketPermission www.google.com:80 connect,resolve)
When loading the web page in a browser, there is a Trust/Deny dialogue box, if I click Trust, then the image shows up.
These are the steps that I'm taking:
ant makejar
jarsigner -keystore keystore-name -storepass password -keypass password web/LoadImageApp.jar alias-name
jarsigner -verify -verbose web/LoadImageApp.jar
appletviewer web/index.html ## as mentioned above, this gives a security exception. instead, load the webpage in a browser.
The output from jarsigner -verify is:
Warning: The signer certificate will expire within six months.
332 Thu Jan 07 20:03:38 EST 2010 META-INF/MANIFEST.MF
391 Thu Jan 07 20:03:38 EST 2010 META-INF/ALIAS-NA.SF
1108 Thu Jan 07 20:03:38 EST 2010 META-INF/ALIAS-NA.DSA
sm 837 Thu Jan 07 20:03:38 EST 2010 LoadImageApp$1.class
sm 925 Thu Jan 07 20:03:38 EST 2010 LoadImageApp.class
sm 54 Wed Jan 06 01:28:02 EST 2010 client.policy
s = signature was verified
m = entry is listed in manifest
k = at least one certificate was found in keystore
i = at least one certificate was found in identity scope
jar verified.
The following is the complete java source code (to emphasize the concept, I removed all that extra exception handling/null checking) :
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
import javax.swing.*;
import java.net.*;
import java.security.*;
public class LoadImageApp extends JApplet
{
private BufferedImage img;
private final String imageURL = "http://www.google.com/intl/en_ALL/images/logo.gif";
public void init()
{
loadImage();
}
public void paint(Graphics g)
{
if (null != img) { g.drawImage(img, 0, 0, null); }
}
public void loadImage()
{
AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
try
{
URL url = new URL(imageURL);
if (null == url)
{
throw new MalformedURLException();
}
img = ImageIO.read(url);
}
catch (Exception e) { e.printStackTrace(); }
return null;
}
});
}
}
Solution
Your running into the exception because by default applets load into sandbox security, Sandbox only allows you to make url connections to the domain serving the applet. Meaning you cannot create a url connection to google unless you applet is hosted by google.
You need to do the following in order to properly connect to a remote url.
Create a self signed certificate at least, Ideally you have a verified certificate linking back through verisign or some other certificate authority (CA) of your choosing. Certificate Instructions
Sign your jar using jarsigner. Signing Instructions
Now you can wrap your code in a privileged block as follows
try
{
final String imageURL = "http://www.google.com/intl/en_ALL/images/logo.gif";
URL url = (URL) AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
try
{
return new URL(imageURL);
}
catch (MalformedURLException e)
{
e.printStackTrace();
return null;
}
}
});
if(url == null)
{
// Something is wrong notify the user
}
else
{
// We know the url is good so continue on
img = ImageIO.read(url);
}
}
catch (IOException e)
{
System.out.println(e);
}
I imported your applet code, I switched some of it around, Pulling the img instantiation out of the privileged block & having the block return a url. This works when I load it into a webbrowser.
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.IOException;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class LoadImageApp extends Applet
{
private BufferedImage img;
private final String imageURL = "http://www.google.com/intl/en_ALL/images/logo.gif";
public void init()
{
loadImage();
}
public void paint(Graphics g)
{
if (null != img) { g.drawImage(img, 0, 0, null); }
}
public void loadImage()
{
URL url = (URL) AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
try
{
return new URL(imageURL);
}
catch (Exception e) { e.printStackTrace(); }
return null;
}
});
try {
img = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
}
}
OTHER TIPS
It seems you're running an applet instead of a normal application. An applet is not allowed to retrieve any resources outside the domain it has been loaded from. The idea is to prevent "bad" applications from calling something like
String targetURL = "http://www.foo.bar/spoof?source" + System.getProperty("...);
or transfering other kinds of data to an unknown destination.
If you really need to retrieve external data you have to sign your applet.
- Create a file call "policy.all" in the same folder of your jar
copy/paste the following text in it :
grant { permission java.security.AllPermission;};
Start application like this :
java -jar yourjar.jar -Djava.security.policy=policy.all
Sounds like you’re not trying to create a jar
file which will be run from the command line but an applet which will be executed in the browser. If that is true, you’re pretty much out of luck because applets are only allowed to access the server they have been loaded from. If you really want to access other servers from an applet, you have to sign your applet; Google can help you find more information.