I am trying to save a file from URL using outputstream. The URL is secure by https. So I got some error when I try to get the file as the following
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 sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
at java.net.URL.openStream(Unknown Source)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown Source)
at java.security.cert.CertPathBuilder.build(Unknown Source)
... 60 more
Suppose that I want to open the file from this URL
https://www.filepicker.io/api/file/KW9EJhYtS6y48Whm2S6D?signature=4098f262b9dba23e4766ce127353aaf4f37fde0fd726d164d944e031fd862c18&policy=eyJoYW5kbGUiOiJLVzlFSmhZdFM2eTQ4V2htMlM2RCIsImV4cGlyeSI6MTUwODE0MTUwNH0=
So I do something like:
try{
URL URL = new URL('https://www.filepicker.io/api/file/KW9EJhYtS6y48Whm2S6D?signature=4098f262b9dba23e4766ce127353aaf4f37fde0fd726d164d944e031fd862c18&policy=eyJoYW5kbGUiOiJLVzlFSmhZdFM2eTQ4V2htMlM2RCIsImV4cGlyeSI6MTUwODE0MTUwNH0=');
String = path = "D://download/";
InputStream ins = url.openStream();
OutputStream ous = new FileOutputStream(path);
final byte[] b = new byte[2048];
int length;
while ((length = inputStream.read(b)) != -1) {
ous.write(b, 0, length);
}
ins.close();
ous.close();
}
The result is nothing happen in the dedicated floder because the error is show up. How can I get the file from the HTTPS url?
A HTTPS connection requires handshaking. I.e. explicitly acknowledge each other. The server has identified itself by a HTTPS certificate, but you apparently don't have this certificate in your trust store and you are nowhere in your Java code explicitly acknowledging the identification, so the HttpsURLConnection
(which is being used under the covers here) refuses to continue the HTTPS request.
As a kickoff example, you can use the following piece of code in your class to let HttpsURLConnection
accept all SSL certificates, regardless of the HTTPS URL you use.
static {
final TrustManager[] trustAllCertificates = new TrustManager[] {
new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null; // Not relevant.
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Do nothing. Just allow them all.
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCertificates, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (GeneralSecurityException e) {
throw new ExceptionInInitializerError(e);
}
}
If you however want more fine grained control on a per-certificate basis, then implement the methods accordingly as per their Javadoc.
Unrelated to the concrete problem, you've a second problem in your code. You're attempting to save it the downloaded file as a folder instead of as a file.
String = path = "D://download/";
OutputStream ous = new FileOutputStream(path);
Apart from the syntax error which is more likely result of carelessness during formulating the question (i.e. editing the code straight in question instead of actually copypasting working code), this isn't making any sense. You should not specify a folder as save location. You should specify a file name. You can if necessary extract it from the Content-Disposition
header, or autogenerate one with File#createTempFile()
. E.g.
File file = File.createTempFile("test-", ".jpg", new File("D:/download/"));
Files.copy(url.openStream(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
(and if you're already on Java 7, just make use of Files#copy()
instead of that boilerplate)
The cause of the error is that Java cannot confirm the validity of the certificate. This can be fixed by importing the certificate into the JAVA certificate storage, or "fixed" by disabling SSL certificate validation.