JRE8下DGCClient抛出AccessControlException读取trustStore权限问题
问题
使用JRE 8环境,程序通过JavaService.exe作为Windows服务运行,已启用SecurityManager并指定包含相关权限的策略文件。在服务端与客户端完成握手连接且无I/O流存在时,自定义RMIClientSocketFactory中设置系统属性后出现权限异常。
自定义SocketFactory代码
public class CustomClientSocketFactory implements RMIClientSocketFactory, Serializable { @Override public Socket createSocket(String host, int port) throws IOException { SSLSocket socket = null; try { File tsFile = new File("truststore.pfx"); FileInputStream is = AccessController.doPrivileged(new PrivilegedAction<FileInputStream>() { @Override public FileInputStream run() { FileInputStream fs = null; try { fs = new FileInputStream(tsFile); } catch (FileNotFoundException e) { System.out.println("Expected file unfound: " + truststore + "; " + e.getMessage()); } return fs; } }); KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(is, phrase.toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), new SecureRandom()); System.setProperty("javax.net.ssl.trustStore", "truststore.pfx"); System.setProperty("javax.net.ssl.trustStorePassword", phrase); System.setProperty("javax.net.ssl.trustStoreType", "PKCS12"); SSLSocketFactory socketFactory = context.getSocketFactory(); socket = (SSLSocket) socketFactory.createSocket(host, port); socket.setEnabledProtocols(new String[] {"TLSv1.2", "TLSv1.3"}); socket.setEnabledCipherSuites(socket.getSupportedCipherSuites()); } catch (Throwable t) { throw new IOException(t.getMessage(), t); } return socket; } }
异常堆栈信息
java.security.AccessControlException: access denied ("java.util.PropertyPermission" "javax.net.ssl.trustStore" "read") at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) at java.security.AccessController.checkPermission(AccessController.java:886) at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294) at java.lang.System.getProperty(System.java:733) ... at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:617) at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216) at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202) at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:343) at sun.rmi.transport.DGCImpl_Stub.clean(DGCImpl_Stub.java:74) at sun.rmi.transport.DGCClient$EndpointEntry.makeCleanCalls(DGCClient.java:700) at sun.rmi.transport.DGCClient$EndpointEntry.access$1700(DGCClient.java:192) at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread$1.run(DGCClient.java:610) at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread$1.run(DGCClient.java:603) at java.security.AccessController.doPrivileged(Native Method) at sun.rmi.transport.DGCClient$EndpointEntry$RenewCleanThread.run(DGCClient.java:603) at java.lang.Thread.run(Thread.java:750)
策略文件配置
grant { permission java.security.AllPermission; permission java.net.SocketPermission "localhost:1024-","connect,accept"; permission java.util.PropertyPermission "javax.net.ssl.trustStore","read"; };
解决方案
1. 移除不必要的系统属性设置
你的代码已经手动初始化了SSLContext并加载了信任库,完全不需要设置javax.net.ssl.trustStore相关全局系统属性——这些属性是给默认SSL上下文用的,你自定义的SSLContext不会读取它们,反而全局属性会被后续RMI的DGC(分布式垃圾回收)线程读取,触发SecurityManager的权限校验。直接删掉这三行代码:
System.setProperty("javax.net.ssl.trustStore", "truststore.pfx"); System.setProperty("javax.net.ssl.trustStorePassword", phrase); System.setProperty("javax.net.ssl.trustStoreType", "PKCS12");
2. 修复策略文件的加载问题
你配置的AllPermission已经包含所有权限(包括PropertyPermission的读写),后面单独加的权限配置属于冗余。需要确保JavaService启动时明确指定策略文件的绝对路径——Windows服务运行时的工作目录可能不是程序所在目录,相对路径会导致策略文件无法加载。启动参数示例:
-Djava.security.manager -Djava.security.policy=file:C:/your/app/path/policyfile.policy
3. 确认DGC线程的权限上下文
异常是RMI的DGC线程读取全局SSL属性时触发的,只要你不设置这些全局属性,DGC线程就不会去读取它们,自然不会触发权限拒绝。这是解决问题的核心。
内容的提问来源于stack exchange,提问作者SophieW
相关产品推荐
相关产品推荐

