java.lang.IllegalAccessError: cannot access class because module does not export to unnamed module

whme picture whme · Dec 5, 2018 · Viewed 22.4k times · Source

I am currently trying to successfully extract the java.base.java.util.jar package from the jdk-9.0.1, building a .jar out of it and importing the jar as external library into another project, so that I can modify the behavior of some of the methods from the classes contained in it.

I seem to be successful at extracting the package, as I am able to eradicate all possible pre-compilation-errors in the project and build the .jar artifact. I can also import this .jar as external library in my other project.

Edit: every private class from outside java.util.jar (i.e.: SharedSecrets) that was needed was also extracted and put in the .jar

However, when I try to run it (by replacing the import java.util.jar.*; in order to use my own version of it) I get this error: java.lang.IllegalAccessError: class SharedSecrets (in unnamed module @0x2b546384) cannot access class jdk.internal.misc.Unsafe (in module java.base) because module java.base does not export jdk.internal.misc to unnamed module @0x2b546384

I tried both adding this: --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED and adding this: --add-exports=java.base/jdk.internal.misc.Unsafe=ALL-UNNAMED to the Compilation options of both, the project consisting of the extracted java.util.jar package and the project I want to import it as external library, none worked -> the error persists.

All other --add-exports which are in the Compilation Options do work fine on both projects.

What am I doing wrong? What do I have to change in order for it to work?

N.B.: if things are unclear, feel free to ask!

Edit: The code where I try to use my own 'java.util.jar' instead of the official one (note at the moment both are identical, the only difference is that one remains inside the jdk while the other is just the 'minimal viable product')

This is not a duplicate of this as I (and I already pointed that out) tried the --add-exports which are suggested as answer in the other question.

The error occurs in the 4. line where the JarFile constructor is called which will not call the one from the jdk but the one from the selfmade library I imported.

public boolean verifyJar(String jarName)
        throws Exception {
    boolean anySigned = false;  // if there exists entry inside jar signed

    Map<String, String> digestMap = new HashMap<>();
    Map<String, PKCS7> sigMap = new HashMap<>();
    try (JarFile jf = new JarFile(jarName, true)) {  // error
        Vector<JarEntry> entriesVec = new Vector<>();
        byte[] buffer = new byte[8192];

        Enumeration<JarEntry> entries = jf.entries();
        while (entries.hasMoreElements()) {
            JarEntry je = entries.nextElement();
            entriesVec.addElement(je);
            try (InputStream is = jf.getInputStream(je)) {
                String name = je.getName();
                if (MySignatureFileVerifier.isSigningRelated(name)
                        && MySignatureFileVerifier.isBlockOrSF(name)) {
                    String alias = name.substring(name.lastIndexOf('/') + 1,
                            name.lastIndexOf('.'));
                    try {
                        if (name.endsWith(".SF")) {
                            Manifest sf = new Manifest(is);
                            for (Object obj : sf.getMainAttributes().keySet()) {
                                String key = obj.toString();
                                if (key.endsWith("-Digest-Manifest")) {
                                    digestMap.put(alias,
                                            key.substring(0, key.length() - 16));
                                    break;
                                }
                            }

                        } else {
                            sigMap.put(alias, new PKCS7(is));
                        }
                    } catch (IOException ioe) {
                        throw ioe;
                    }
                } else {
                    while (is.read(buffer, 0, buffer.length) != -1) {
                        // we just read. this will throw a SecurityException
                        // if  a signature/digest check fails.
                    }
                }
            }
        }

        Manifest man = jf.getManifest();
        boolean hasSignature = false;

        if (man != null) {
            Enumeration<JarEntry> e = entriesVec.elements();
            while (e.hasMoreElements()) {
                JarEntry je = e.nextElement();
                String name = je.getName();

                hasSignature = hasSignature
                        || MySignatureFileVerifier.isBlockOrSF(name);

                CodeSigner[] signers = getCodeSigners(je, sigMap.get("SIGNER"));
                boolean isSigned = (signers != null);
                anySigned |= isSigned;
            }
        }
        if (man == null) {
            System.out.println();
        }

        // Even if the verbose option is not specified, all out strings
        // must be generated so seeWeak can be updated.
        if (!digestMap.isEmpty()
                || !sigMap.isEmpty()) {
            for (String s : digestMap.keySet()) {
                PKCS7 p7 = sigMap.get(s);
                if (p7 != null) {
                    String history;
                    try {
                        SignerInfo si = p7.getSignerInfos()[0];
                        X509Certificate signer = si.getCertificate(p7);
                        String digestAlg = digestMap.get(s);
                        String sigAlg = AlgorithmId.makeSigAlg(
                                si.getDigestAlgorithmId().getName(),
                                si.getDigestEncryptionAlgorithmId().getName());
                        PublicKey key = signer.getPublicKey();
                        PKCS7 tsToken = si.getTsToken();
                        if (tsToken != null) {
                            SignerInfo tsSi = tsToken.getSignerInfos()[0];
                            X509Certificate tsSigner = tsSi.getCertificate(tsToken);
                            byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
                            TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
                            PublicKey tsKey = tsSigner.getPublicKey();
                            String tsDigestAlg = tsTokenInfo.getHashAlgorithm().getName();
                            String tsSigAlg = AlgorithmId.makeSigAlg(
                                    tsSi.getDigestAlgorithmId().getName(),
                                    tsSi.getDigestEncryptionAlgorithmId().getName());
                        }
                    } catch (Exception e) {
                        throw e;
                    }
                }
            }
        }
        System.out.println();
        if (!anySigned) {
            if (hasSignature) {
                System.out.println("jar.treated.unsigned");
            } else {
                System.out.println("jar.is.unsigned");
                return false;
            }
        } else {
            System.out.println("jar.verified.");
            return true;

        }
        return false;
    } catch (Exception e) {
        throw e;
    }
}

Answer

whme picture whme · Dec 6, 2018

As pointed out by Nicolai's answer to this question --add-exports java.base/jdk.internal.misc=ALL-UNNAMED has to be done when compiling (javac) and when running (java) the code.