Record a large number of JceSecurity instances in memory to cause OOM troubleshooting

problem phenomenon

Recently, there is a project that a colleague is in charge of. OOM occurs after running for a while. Let me help you find out why. First, I checked the project startup command and configured the dump file. I saw that the dump file actually has 3.4G, which must be a problem with the code. Resulting in memory not being reclaimed.

-XX:+HeapDumpBeforeFullGC -XX:+HeapDumpAfterFullGC -XX:HeapDumpPath=\dump

insert image description here

Troubleshooting process

Select Biggest Objects and see that the object javax.crypto.JceSecurity actually has 1.9G

insert image description here

Select javax.crypto.JceSecurity and then select Use Selected Objects, select Outgoing references in References
insert image description here
insert image description here

Then you can see that there are 14349 BouncyCastleProvider objects in the HasMap of verificationResults. It seems that this object has not been released and caused a memory leak.
e insert image description here
searched for BouncyCastleProvider in the project, this class is instantiated in RSAUtill, it seems that there is a problem when doing RSA encryption and decryption

insert image description here
The RSA decryption code is as follows, it should be caused by the line of code Cipher.getInstance(“RSA”, new org.bouncycastle.jce.provider.BouncyCastleProvider())

	public static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
    
       
	    try {
    
       
	    	//这里是RSA解密,每解密一次创建了一个对象
	        Cipher cipher = Cipher.getInstance("RSA",   
	                new org.bouncycastle.jce.provider.BouncyCastleProvider());   
	        cipher.init(cipher.DECRYPT_MODE, pk);   
	        int blockSize = cipher.getBlockSize();   
	        ByteArrayOutputStream bout = new ByteArrayOutputStream(64);   
	        int j = 0;
	        while (raw.length - j * blockSize > 0) {
    
       
	            bout.write(cipher.doFinal(raw, j * blockSize, blockSize));   
	            j++;   
	        }   
	        return bout.toByteArray();   
	    } catch (Exception e) {
    
       
	        throw new Exception(e.getMessage());   
	    }   
	}   

Enter the Cipher.getInstance() method, the key is a line of code Exception ve = JceSecurity.getVerificationResult(provider);

    public static final Cipher getInstance(String transformation,
                                           Provider provider)
            throws NoSuchAlgorithmException, NoSuchPaddingException
    {
    
    
        if (provider == null) {
    
    
            throw new IllegalArgumentException("Missing provider");
        }
        Exception failure = null;
        List<Transform> transforms = getTransforms(transformation);
        boolean providerChecked = false;
        String paddingError = null;
        for (Transform tr : transforms) {
    
    
            Service s = provider.getService("Cipher", tr.transform);
            if (s == null) {
    
    
                continue;
            }
            if (providerChecked == false) {
    
    
            	//此处获取了provider
                Exception ve = JceSecurity.getVerificationResult(provider);
                if (ve != null) {
    
    
                    String msg = "JCE cannot authenticate the provider "
                        + provider.getName();
                    throw new SecurityException(msg, ve);
                }
                providerChecked = true;
            }
            if (tr.supportsMode(s) == S_NO) {
    
    
                continue;
            }
            if (tr.supportsPadding(s) == S_NO) {
    
    
                paddingError = tr.pad;
                continue;
            }
            try {
    
    
                CipherSpi spi = (CipherSpi)s.newInstance(null);
                tr.setModePadding(spi);
                Cipher cipher = new Cipher(spi, transformation);
                cipher.provider = s.getProvider();
                cipher.initCryptoPermission();
                return cipher;
            } catch (Exception e) {
    
    
                failure = e;
            }
        }

        // throw NoSuchPaddingException if the problem is with padding
        if (failure instanceof NoSuchPaddingException) {
    
    
            throw (NoSuchPaddingException)failure;
        }
        if (paddingError != null) {
    
    
            throw new NoSuchPaddingException
                ("Padding not supported: " + paddingError);
        }
        throw new NoSuchAlgorithmException
                ("No such algorithm: " + transformation, failure);
    }

I found verificationResults.put(p, PROVIDER_VERIFIED) in JceSecurity.getVerificationResult(provider). Every time it is decrypted, it will put a provider object in the verificationResults object. No wonder there are 14349 BouncyCastleProvider objects in the HasMap of verificationResults. It turned out that they were all found.
insert image description here

Solution

Just rewrite Cipher into a singleton

    private static Cipher cipher;
    static {
    
    
        try {
    
    
            cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

hint

The JDK that comes with javax.crypto.Cipher has no original code. After consulting the data, it is found that the class javax.crypto.Cipher related to jdk encryption does not provide source code documents. The US government controls and restricts the export of software technology products involving encryption algorithms and strong encryption. Based on the requirements of this law, JDK cannot provide source codes related to strong encryption. But it can be downloaded from https://jdk.java.net/java-se-ri/8-MR3
insert image description here

Guess you like

Origin blog.csdn.net/whzhaochao/article/details/122739645