はじめに
JDK1.6で動いている環境から社内のシステムにTLS1.2で接続する必要があり、いろいろと調べた結果BouncyCastleを使うしか方法がなく、なんとか接続できたので記録として残しておきます。
社で使用しているJDKはIBM JDK1.6のため、まだサポートがありTLS1.2接続も可能でした。しかし、接続先が許可しているCipherと一致するものが存在せず、BouncyCastleに頼ることにしました。
こちらの記事は自宅で確認したため、Oracle JDKを使用しています。
ちなみにOracleについてもhttps://www.oracle.com/technetwork/java/javase/overview-156328.html#R160_121に記載があるように商用サポートを受ければTLS1.2が使えるバージョンが入手可能のようです。ただし、Cipher Suitesがどうなっているかは要確認です。
動作確認環境
OS:Window 10
Oracle JDK: Version 1.6.0_45
Java Library:
bcprov-jdk15on-160.jar
bctls-jdk15on-160.jar
jersey-client-1.8.jar
jersey-core-1.8.jar
json-20131018.jar
1. Bouncy Castleの設定
%JAVA_HOME%\jre\lib\security\java.securityに
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
を追加します。
#
# List of providers and their preference orders (see above):
#
security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=com.sun.net.ssl.internal.ssl.Provider
security.provider.4=com.sun.crypto.provider.SunJCE
security.provider.5=sun.security.jgss.SunProvider
security.provider.6=com.sun.security.sasl.Provider
security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.8=sun.security.smartcardio.SunPCSC
security.provider.9=sun.security.mscapi.SunMSCAPI
security.provider.10=org.bouncycastle.jce.provider.BouncyCastleProvider
2. ソースコード準備
package tlsclient.tworks55;
import java.security.Security;
public class BCtest {
public static void main(String[] args) {
// BC is the ID for the Bouncy Castle provider;
if (Security.getProvider("BC") == null) {
System.out.println("Bouncy Castle provider is NOT available");
} else {
System.out.println("Bouncy Castle provider is available");
}
}
}
package tlsclient.tworks55;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.json.JSONObject;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
import java.security.Provider;
import java.security.Security;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.ws.rs.core.MediaType;
public class TLSJerseyClientTest {
public static void main(String[] args) {
try {
TrustManager[] trustAllCerts = { new InsecureTrustManager() };
HostnameVerifier hostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
ClientConfig config = new DefaultClientConfig();
Provider provider = new BouncyCastleJsseProvider();
Security.addProvider(provider);
SSLContext ctx = SSLContext.getInstance("TLSv1.2", provider.getName());
ctx.init(null, trustAllCerts, null);
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
new HTTPSProperties(hostnameVerifier, ctx));
Client c = Client.create(config);
WebResource resource = c.resource("https://github.com/login/oauth/access_token");
JSONObject jsonObject = new JSONObject();
jsonObject.put("client_id", "*** Set client_id here ***");
jsonObject.put("client_secret", "*** Set client_secret here ***");
jsonObject.put("code", "*** Set code here ***");
String response = resource.type(MediaType.APPLICATION_JSON_TYPE).post(String.class, jsonObject.toString());
System.out.println(response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package tlsclient.tworks55;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
public class InsecureTrustManager implements X509TrustManager {
/**
* {@inheritDoc}
*/
public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
// Everyone is trusted!
}
/**
* {@inheritDoc}
*/
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
// Everyone is trusted!
}
/**
* {@inheritDoc}
*/
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
※Githubはこちら
3. Bouncy Castleが読めているか確認
BCtest.javaを実行し"Bouncy Castle provider is available"が表示されることを確認します。
Bouncy Castle provider is available
Not Availableになった場合は、設定がうまくいっていません。
4. TLSv1.2でサイトに接続
上記ソースコードは、GithubithubにAuthorizing OAuth Appsの設定をした上で、OAuth Tokenを取得する部分を試す感じになっています。
GithubのOauth Appsはhttps://github.com/settings/applications/newで作成可能です。
client_id及びclient_secretは、アプリ作成時にサイトに表示されますが、codeについては動的に生成する必要があります。
ブラウザで以下のようにClient_idをセットするとcallback URLに対してcodeがパラメーターとして渡されます。
[https://github.com/login/oauth/authorize?client_id=[Set Client id here]](https://github.com/login/oauth/authorize?client_id=Set Client id here)
Callback URLをLocalhostなどにしておけば、ブラウザ上に
http://localhost:5555/?code=d86cabc30fdabc6abcb8
といった形で表示されますので、これをメモしておきます。
TLSJerseyClientTest.javaの以下にそれぞれパラメーターをセットして、実行します。
jsonObject.put("client_id", "*** Set client_id here ***");
jsonObject.put("client_secret", "*** Set client_secret here ***");
jsonObject.put("code", "*** Set code here ***");
うまく行けば、以下のような結果が得られます。
access_token=abc4cf0fda71e2abc358c22fba7abc8aabc56abc&scope=&token_type=bearer
5. java.security.InvalidKeyException: Illegal key size が発生する場合の対応
接続先の状況によるものだと思いますが、InvalidKeyExceptionが発生するケースがあるようです。
その際はjce_policy-6をダウンロードし、以下にライブラリをコピーすると動作するようです。
%JAVA_HOME%\jre\lib\security
local_policy.jar
US_export_policy.jar
以下、InvalidKeyException発生例:
2019/01/26 23:06:46 org.bouncycastle.jsse.provider.ProvTlsClient notifyAlertRaised
警告: Client raised fatal(2) internal_error(80) alert: Failed to read record
java.lang.RuntimeException: java.security.InvalidKeyException: Illegal key size
at org.bouncycastle.tls.crypto.impl.jcajce.JceChaCha20Poly1305.init(Unknown Source)
at org.bouncycastle.tls.crypto.impl.TlsAEADCipher.<init>(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createChaCha20Poly1305(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createCipher(Unknown Source)
at org.bouncycastle.tls.crypto.impl.AbstractTlsSecret.createCipher(Unknown Source)
at org.bouncycastle.tls.AbstractTlsClient.getCipher(Unknown Source)
at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1031)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler$1$1.getOutputStream(URLConnectionClientHandler.java:225)
at com.sun.jersey.api.client.CommittingOutputStream.commitWrite(CommittingOutputStream.java:117)
at com.sun.jersey.api.client.CommittingOutputStream.write(CommittingOutputStream.java:89)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
at java.io.BufferedWriter.flush(BufferedWriter.java:236)
at com.sun.jersey.core.util.ReaderWriter.writeToAsString(ReaderWriter.java:191)
at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.writeToAsString(AbstractMessageReaderWriterProvider.java:128)
at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:88)
at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:58)
at com.sun.jersey.api.client.RequestWriter.writeRequestEntity(RequestWriter.java:300)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:204)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:147)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:670)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:563)
at tlsclient.tworks55.TLSJerseyClientTest.main(TLSJerseyClientTest.java:42)
Caused by: java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
... 39 more
com.sun.jersey.api.client.ClientHandlerException: org.bouncycastle.tls.TlsFatalAlert: internal_error(80)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:149)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:670)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.post(WebResource.java:563)
at tlsclient.tworks55.TLSJerseyClientTest.main(TLSJerseyClientTest.java:42)
Caused by: org.bouncycastle.tls.TlsFatalAlert: internal_error(80)
at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.blockForHandshake(Unknown Source)
at org.bouncycastle.tls.TlsClientProtocol.connect(Unknown Source)
at org.bouncycastle.jsse.provider.ProvSSLSocketDirect.startHandshake(Unknown Source)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1031)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:230)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler$1$1.getOutputStream(URLConnectionClientHandler.java:225)
at com.sun.jersey.api.client.CommittingOutputStream.commitWrite(CommittingOutputStream.java:117)
at com.sun.jersey.api.client.CommittingOutputStream.write(CommittingOutputStream.java:89)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:276)
at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:122)
at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:212)
at java.io.BufferedWriter.flush(BufferedWriter.java:236)
at com.sun.jersey.core.util.ReaderWriter.writeToAsString(ReaderWriter.java:191)
at com.sun.jersey.core.provider.AbstractMessageReaderWriterProvider.writeToAsString(AbstractMessageReaderWriterProvider.java:128)
at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:88)
at com.sun.jersey.core.impl.provider.entity.StringProvider.writeTo(StringProvider.java:58)
at com.sun.jersey.api.client.RequestWriter.writeRequestEntity(RequestWriter.java:300)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler._invoke(URLConnectionClientHandler.java:204)
at com.sun.jersey.client.urlconnection.URLConnectionClientHandler.handle(URLConnectionClientHandler.java:147)
... 5 more
Caused by: java.lang.RuntimeException: java.security.InvalidKeyException: Illegal key size
at org.bouncycastle.tls.crypto.impl.jcajce.JceChaCha20Poly1305.init(Unknown Source)
at org.bouncycastle.tls.crypto.impl.TlsAEADCipher.<init>(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createChaCha20Poly1305(Unknown Source)
at org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto.createCipher(Unknown Source)
at org.bouncycastle.tls.crypto.impl.AbstractTlsSecret.createCipher(Unknown Source)
at org.bouncycastle.tls.AbstractTlsClient.getCipher(Unknown Source)
at org.bouncycastle.tls.TlsClientProtocol.handleHandshakeMessage(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)
at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)
at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)
... 29 more
Caused by: java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
... 39 more
参考URL
https://www.bouncycastle.org/
http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions#FrequentlyAskedQuestions-1.WhydoIget%22java.lang.SecurityException:Unsupportedkeysizeoralgorithmparameters%22or%22java.security.InvalidKeyException:Illegalkeysize%22whenItryusingtheBouncyCastleProvider?
http://tech-son.hatenablog.jp/entry/2017/11/01/003000
https://stackoverflow.com/questions/33364100/how-to-use-tls-1-2-in-java-6
https://stackoverflow.com/questions/6363801/invalidkeyexception-illegal-key-size-java-code-throwing-exception-for-encryp?rq=1
https://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html
https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/