记一次truelicense安装证书报stream close()异常

背景

近期,由于工作需要研究了下truelicense,给xxx添加一个证书,限制产品的使用期限。前期将truelicense整合到spring-boot项目中很容易,过程中没有出现问题,但是同样的代码在整合到osgi容器中的时候,出现了异常,异常如下:

de.schlichtherle.xml.PersistenceServiceException: java.lang.reflect.UndeclaredThrowableException
    at de.schlichtherle.xml.PersistenceService.load(PersistenceService.java:397)[311:truelicense-xml:1.33.0]
    at de.schlichtherle.license.PrivacyGuard.key2cert(PrivacyGuard.java:174)[312:truelicense-core:1.33.0]
    at com.monk.core.license.CustomLicenseManager.install(CustomLicenseManager.java:48)[306:com.monk.core:1.0.0]
    at de.schlichtherle.license.LicenseManager.install(LicenseManager.java:406)[312:truelicense-core:1.33.0]
    at de.schlichtherle.license.LicenseManager.install(LicenseManager.java:382)[312:truelicense-core:1.33.0]
    at com.monk.core.license.LicenseVerify.installLicense(LicenseVerify.java:80)[306:com.monk.core:1.0.0]
    at com.monk.core.CoreBundleActivator.start(CoreBundleActivator.java:95)[306:com.monk.core:1.0.0]
    at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:645)[org.apache.felix.framework-4.2.1.jar:]
    at org.apache.felix.framework.Felix.activateBundle(Felix.java:2146)[org.apache.felix.framework-4.2.1.jar:]
    at org.apache.felix.framework.Felix.startBundle(Felix.java:2064)[org.apache.felix.framework-4.2.1.jar:]
    at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:955)[org.apache.felix.framework-4.2.1.jar:]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundle(DirectoryWatcher.java:1245)[7:org.apache.felix.fileinstall:3.5.0]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundles(DirectoryWatcher.java:1217)[7:org.apache.felix.fileinstall:3.5.0]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startAllBundles(DirectoryWatcher.java:1207)[7:org.apache.felix.fileinstall:3.5.0]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.doProcess(DirectoryWatcher.java:504)[7:org.apache.felix.fileinstall:3.5.0]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.process(DirectoryWatcher.java:358)[7:org.apache.felix.fileinstall:3.5.0]
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.run(DirectoryWatcher.java:310)[7:org.apache.felix.fileinstall:3.5.0]
Caused by: java.lang.reflect.UndeclaredThrowableException
    at de.schlichtherle.xml.PersistenceService$1.exceptionThrown(PersistenceService.java:79)
    at com.sun.beans.decoder.DocumentHandler.handleException(DocumentHandler.java:359)[:1.8.0_212]
    at com.sun.beans.decoder.DocumentHandler$1.run(DocumentHandler.java:388)[:1.8.0_212]
    at com.sun.beans.decoder.DocumentHandler$1.run(DocumentHandler.java:372)[:1.8.0_212]
    at java.security.AccessController.doPrivileged(Native Method)[:1.8.0_212]
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:74)[:1.8.0_212]
    at com.sun.beans.decoder.DocumentHandler.parse(DocumentHandler.java:372)[:1.8.0_212]
    at java.beans.XMLDecoder$1.run(XMLDecoder.java:201)[:1.8.0_212]
    at java.beans.XMLDecoder$1.run(XMLDecoder.java:199)[:1.8.0_212]
    at java.security.AccessController.doPrivileged(Native Method)[:1.8.0_212]
    at java.beans.XMLDecoder.parsingComplete(XMLDecoder.java:199)[:1.8.0_212]
    at java.beans.XMLDecoder.close(XMLDecoder.java:174)[:1.8.0_212]
    at de.schlichtherle.xml.PersistenceService.load(PersistenceService.java:395)[311:truelicense-xml:1.33.0]
    ... 16 more
Caused by: java.io.IOException: Stream closed
    at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:170)[:1.8.0_212]
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:214)[:1.8.0_212]
    at java.io.BufferedInputStream.read(BufferedInputStream.java:265)[:1.8.0_212]
    at org.apache.xerces.impl.XMLEntityManager$RewindableInputStream.readAndBuffer(Unknown Source)[:]
    at org.apache.xerces.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)[:]
    at org.apache.xerces.impl.XMLVersionDetector.determineDocVersion(Unknown Source)[:]
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)[:]
    at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)[:]
    at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)[:]
    at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)[:]
    at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)[:]
    at org.apache.xerces.jaxp.SAXParserImpl.parse(Unknown Source)[:]
    at com.sun.beans.decoder.DocumentHandler$1.run(DocumentHandler.java:375)[:1.8.0_212]
    ... 26 more

异常分析:

首先从错误堆栈信息来看,能看出来问题是出在解析XML的时候,java.beans.XMLDecoder.close()方法在关闭文件流的时候发现文件流已经被关闭了。于是就可以带着下面的几个问题去跟一下代码

  • 为什么会解析XML? 在哪里解析的XML —> 我生成的证书是个加密文件也不是XML呀,莫非这个加密文件在经过truelicense解密后就是个xml文件,毕竟用xml来存储配置信息也很常见。
  • 如果是解析XML,解析XML的方式有多种,有没有可能出现了jar包冲突

既然有这两个问题,那么truelicense又是开源的,先把源码down下来,刨一下truelicense的源码,肯定可以验证第一个疑问的猜想,以及第一个疑问答案在哪里解析的XML

源码分析

在安装证书的时候,需要传给LicenseManager这个类其中一个参数就是证书的位置,LicenseManager将证书读成byte[]数组,经过一系列的解析(加解密就略过了,不是重点)最终得到一个InputStream,将这个流打印出来发现就是一个XML,解析XML的方式是通过SAXParser去解析的。(解开了第一个疑问

// 入参key就是证书文件的byte[]   该方法在de.schlichtherle.license.LicenseManager中
protected synchronized LicenseContent install(final byte[] key, final LicenseNotary notary) throws Exception {
   
   
    // 这一行就是将证书文件解析成GenericCertificate对象
    final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
    notary.verify(certificate);
    final LicenseContent content = (LicenseContent) certificate.getContent();
    validate(content);
    setLicenseKey(key);
    setCertificate(certificate);


    return content;
}

// 这个方法的作用就是将证书的字节流解析转换成GenericCertificate对象(凭证信息)  该方法在de.schlichtherle.license.PrivacyGuard中
public GenericCertificate key2cert(final byte[] key) throws Exception {
   
   
    // 这一行姑且理解为将证书文件解密之后读成一个InputSteam(XML形式)
    final InputStream in = new GZIPInputStream(new ByteArrayInputStream(getCipher4Decryption().doFinal(key)));
    final GenericCertificate certificate;
    try {
   
   
        // 再经过这个方法将XML解析成GenericCertificate对象
        // PersistenceService.load(in)方法返回的是一个Object对象,再强转成GenericCertificate对象
        certificate = (GenericCertificate) PersistenceService.load(in);
    }
    finally {
   
   
        try {
   
    in.close(); }
        catch (IOException weDontCare) {
   
    }
    }

    return certificate;
}


// 解析XML的具体方法     在de.schlichtherle.xml.PersistenceService类中
public static Object load(InputStream xmlIn) throws PersistenceServiceException {
   
   
    if (null == xmlIn) throw new NullPointerException();
    XMLDecoder decoder = null;
    try {
   
   
        // Note that the constructor already loads the complete object
        // graph into memory. If anything goes wrong, an unchecked
        // exception is thrown already HERE!
        decoder = new XMLDecoder(
                new BufferedInputStream(xmlIn, BUFSIZE),
                null,
                createExceptionListener());

        // decoder.readObject()方法就是解析XML的具体方法,前面日志中打印出来的close()方法就是在这个方法中调用的
        return decoder.readObject();
    } catch (final UndeclaredThrowableException ex) {
   
   
        throw new PersistenceServiceException(ex.getCause()); // unwrap cause
    } catch (final Throwable ex) {
   
   
        throw new PersistenceServiceException(ex);
    } finally {
   
   
        if (null != decoder) {
   
   
            try {
   
   
                decoder.close(); // Could throw e.g. OutOfMemoryError (again)!
            } catch (final Throwable paranoid) {
   
   
                throw new PersistenceServiceException(paranoid);
            }
        }
    }
}

从XMLDecoder.readObject()这个方法一路跟jdk的源码,源码的调用链如下:
java.beans.XMLDecoder#readObject --> com.sun.beans.decoder.DocumentHandler#parse --> com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl#newSAXParserImpl

// 解析XML的真实实现类    在com.sun.beans.decoder.DocumentHandler中
public void parse(final InputSource var1) {
   
   
    if (this.acc == null && null != System.getSecurityManager()) {
   
   
        throw new SecurityException("AccessControlContext is not set");
    } else {
   
   
        AccessControlContext var2 = AccessController.getContext();
        SharedSecrets.getJavaSecurityAccess().doIntersectionPrivilege(new PrivilegedAction<Void>() {
   
   
            public Void run() {
   
   
                try {
   
   
                    SAXParserFactory.newInstance().newSAXParser().parse(var1, DocumentHandler.this);
                } catch (ParserConfigurationException var3) {
   
   
                    DocumentHandler.this.handleException(var3)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值