2021-10-27 End user tried to act as a CA 与 证书验证

8 篇文章 0 订阅
7 篇文章 0 订阅

那天同事换ELB证书后访问域名出现错误:End user tried to act as a CA 最终用户想冒充CA,什么意思嘞? 我在github上找到open-jdk代码,搜索它,看到是类SimpleValidator,接着回到IDE,找出这个异常抛出的地儿:
就是说,没有找到BasicConstraints这个拓展属性,它的Id是2.5.29.19,可以从类sun.security.x509.X509CertImpl再追到sun.security.util.ObjectIdentifier.PKIXExtensions中列举了所有证书属性OID与名称

代码分析

我直接复制反编译的代码,这种无名变量看起来问题也不是很大。

private int checkBasicConstraints(X509Certificate var1, Set<String> var2, int var3) throws CertificateException {
        var2.remove("2.5.29.19");
        int var4 = var1.getBasicConstraints();
        if (var4 < 0) {
            throw new ValidatorException("End user tried to act as a CA", ValidatorException.T_CA_EXTENSIONS, var1);
        } else {
            if (!X509CertImpl.isSelfIssued(var1)) {
                if (var3 <= 0) {
                    throw new ValidatorException("Violated path length constraints", ValidatorException.T_CA_EXTENSIONS, var1);
                }
                --var3;
            }
            if (var3 > var4) {
                var3 = var4;
            }
            return var3;
        }
    }

再追,看到拓展属性检测里先获取证书OID,检测BasicConstraints,再检测key的作用等

    private int checkExtensions(X509Certificate var1, int var2) throws CertificateException {
        Set var3 = var1.getCriticalExtensionOIDs();
        if (var3 == null) {
            var3 = Collections.emptySet();
        }

        int var4 = this.checkBasicConstraints(var1, var3, var2);
        this.checkKeyUsage(var1, var3);
        this.checkNetscapeCertType(var1, var3);
        if (!var3.isEmpty()) {
            throw new ValidatorException("Certificate contains unknown critical extensions: " + var3, ValidatorException.T_CA_EXTENSIONS, var1);
        } else {
            return var4;
        }
    }

哪些证书会被检测扩展属性呢?我们再跟一下,来到关键的SimpleValiatorengineValidate方法。var1就是服务器发来的证书数组,这个数组的最后一个就是服务器证书,往前依次是中间CA证书;这里var2方法里根本没用,var3是指定的受限算法
总得验证过程是先验证服务器证书,再验证证书链中前后验证
服务器证书验证中:只验证证书是否在不受新信证书黑名单里$JAVA_HOME/lib/security/blacklisted.certs,
然后再构造算法验证器var10,
接下来的关键:一个for循环,把所有中间CA证书都验了个遍。注意var1.length - 2,从长度-2上看即跳过了最后一个证书,也就是服务器证书,倒着一个个验证:取出这个序列的证书,和下个证书(第一个循环里var13就是服务器证书),

  1. 先进行证书黑名单var6验证
  2. 再进行受限算法验证,可以看到有指定受限算法和系统指定的受限算法,sun.security.provider.certpath.AlgorithmChecker#check方法可是非常复杂,这里只指出系统指定的受限算法获取方法:Security.getProperty("jdk.certpath.disabledAlgorithms") windows输出MD2, MD5, RSA keySize < 1024 同时这里有个IBM参考文章:customization-disabled-restricted-cryptographic-algorithms 这里的算法是指什么呢?证书签名算法啊!其中(AlgorithmChecker)使用TrustAnchor主要是取出其publicKey作为prevPubKey
  3. 接下来进行有效性验证
  4. 前后证书签发者principal与主题principal验证(我也不知道怎么翻译):var14.getIssuerX500Principal().equals(var13.getSubjectX500Principal()
    比对的就是这个东西:CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US
    X500Principal This class represents an X.500 Principal. X500Principals are represented by distinguished names such as "CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US".
  5. 再用较上级CA证书验证下级证书的公钥签名,可以参考:X509CertImpl#verify(PublicKey var1),步骤也很多
  6. 最后,非最后一个证书,验证扩展属性,到了我们出问题那里!
X509Certificate[] engineValidate(X509Certificate[] var1, Collection<X509Certificate> var2, AlgorithmConstraints var3, Object var4) throws CertificateException {
        if (var1 != null && var1.length != 0) {
            var1 = this.buildTrustedChain(var1);
            Date var5 = this.validationDate;
            if (var5 == null) {
                var5 = new Date();
            }

            UntrustedChecker var6 = new UntrustedChecker();
            X509Certificate var7 = var1[var1.length - 1];

            try {
                var6.check(var7);
            } catch (CertPathValidatorException var18) {
                throw new ValidatorException("Untrusted certificate: " + var7.getSubjectX500Principal(), ValidatorException.T_UNTRUSTED_CERT, var7, var18);
            }

            TrustAnchor var8 = new TrustAnchor(var7, (byte[])null);
            AlgorithmChecker var9 = new AlgorithmChecker(var8);
            AlgorithmChecker var10 = null;
            if (var3 != null) {
                var10 = new AlgorithmChecker(var8, var3);
            }

            int var11 = var1.length - 1;

            for(int var12 = var1.length - 2; var12 >= 0; --var12) {
                X509Certificate var13 = var1[var12 + 1];
                X509Certificate var14 = var1[var12];

                try {
                    var6.check(var14, Collections.emptySet());
                } catch (CertPathValidatorException var17) {
                    throw new ValidatorException("Untrusted certificate: " + var14.getSubjectX500Principal(), ValidatorException.T_UNTRUSTED_CERT, var14, var17);
                }

                try {
                    var9.check(var14, Collections.emptySet());
                    if (var10 != null) {
                        var10.check(var14, Collections.emptySet());
                    }
                } catch (CertPathValidatorException var19) {
                    throw new ValidatorException(ValidatorException.T_ALGORITHM_DISABLED, var14, var19);
                }

                if (!this.variant.equals("code signing") && !this.variant.equals("jce signing")) {
                    var14.checkValidity(var5);
                }

                if (!var14.getIssuerX500Principal().equals(var13.getSubjectX500Principal())) {
                    throw new ValidatorException(ValidatorException.T_NAME_CHAINING, var14);
                }

                try {
                    var14.verify(var13.getPublicKey());
                } catch (GeneralSecurityException var16) {
                    throw new ValidatorException(ValidatorException.T_SIGNATURE_ERROR, var14, var16);
                }

                if (var12 != 0) {
                    var11 = this.checkExtensions(var14, var11);
                }
            }
            return var1;
        } else {
            throw new CertificateException("null or zero-length certificate chain");
        }
    }
错误的原因

所以我们证书验证不通过是扩展属性验证不通过,为什么呢?

  1. 自己颁发的证书错误,没有填充拓展字段 OID 2.5.29.19 BasicConstrains
  2. AWS上传证书错误,AWS上传证书那里有三个框,服务器证书,证书链,CA;我们在证书链那里也上传的pem也含了服务器证书
后记
  1. java.security.为什么继承自Properties难以理解。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值