KeyUsage does not allow key encipherment ssl证书验证失败问题

博客详细分析了一次SSL证书验证失败的问题,错误信息显示KeyUsage不允许keyencipherment。通过代码调试,定位到问题在于证书的KeyUsage未配置key_encipherment。解决方案是在证书制作时增加keyUsage配置,例如:keyUsage=DigitalSignature,keyEncipherment。经过验证,此方法有效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ssl证书验证失败问题

错误信息

javax.net.ssl.SSLHandshakeException: KeyUsage does not allow key encipherment}, caused by {sun.security.validator.ValidatorException: KeyUsage does not allow key encipherment

调查方式底层代码debug查找

通过小编费力的debug,找到了报错代码的位置。
通常解决问题的方式都是找到根源报错位置才可以。
代码片段,只保留需要部分。

package sun.security.validator;

import java.security.cert.CertificateException;
...

class EndEntityChecker {
 	private boolean checkKeyUsage(X509Certificate var1, int var2) throws CertificateException {
        boolean[] var3 = var1.getKeyUsage();
        if (var3 == null) {
            return true;
        } else {
            return var3.length > var2 && var3[var2];
        }
    }
    ...

    private void checkTLSServer(X509Certificate var1, String var2, Set<String> var3) throws CertificateException {
        if (KU_SERVER_ENCRYPTION.contains(var2)) {
            if (!this.checkKeyUsage(var1, 2)) {
                throw new ValidatorException("KeyUsage does not allow key encipherment", ValidatorException.T_EE_EXTENSIONS, var1);
            }
        } else if (KU_SERVER_SIGNATURE.contains(var2)) {
            if (!this.checkKeyUsage(var1, 0)) {
                throw new ValidatorException("KeyUsage does not allow digital signatures", ValidatorException.T_EE_EXTENSIONS, var1);
            }
        } else {
            if (!KU_SERVER_KEY_AGREEMENT.contains(var2)) {
                throw new CertificateException("Unknown authType: " + var2);
            }

            if (!this.checkKeyUsage(var1, 4)) {
                throw new ValidatorException("KeyUsage does not allow key agreement", ValidatorException.T_EE_EXTENSIONS, var1);
            }
        }

        if (!this.checkEKU(var1, var3, "1.3.6.1.5.5.7.3.1") && !this.checkEKU(var1, var3, "1.3.6.1.4.1.311.10.3.3") && !this.checkEKU(var1, var3, "2.16.840.1.113730.4.1")) {
            throw new ValidatorException("Extended key usage does not permit use for TLS server authentication", ValidatorException.T_EE_EXTENSIONS, var1);
        } else if (!SimpleValidator.getNetscapeCertTypeBit(var1, "ssl_server")) {
            throw new ValidatorException("Netscape cert type does not permit use for SSL server", ValidatorException.T_EE_EXTENSIONS, var1);
        } else {
            var3.remove("2.5.29.15");
            var3.remove("2.5.29.37");
            var3.remove("2.16.840.1.113730.1.1");
        }
    }

想必你已经发现了报错的代码位置,就是log日志中的报错信息的位置。

if (KU_SERVER_ENCRYPTION.contains(var2)) {
            if (!this.checkKeyUsage(var1, 2)) {
                throw new ValidatorException("KeyUsage does not allow key encipherment", ValidatorException.T_EE_EXTENSIONS, var1);
            }

报错的原因是KU_SERVER_ENCRYPTION中包含了var2,但是调用checkKeyUsage方法时返回了false,验证失败,所以抛出异常。

 private static final Collection<String> KU_SERVER_ENCRYPTION = Arrays.asList("RSA");

不错var2就是字符串“RSA”.
var2是怎么取到的呢?
这个问题很好,通过小编的debug,发现是再向目标服务第二次握手后走就会走到这段验证的代码,所以应该是从目标服务中的加密套件组ciphers中取到的。
具体过程就不再赘述了。
接下来看认证过程。

 private boolean checkKeyUsage(X509Certificate var1, int var2) throws CertificateException {
        boolean[] var3 = var1.getKeyUsage();
        if (var3 == null) {
            return true;
        } else {
            return var3.length > var2 && var3[var2];
        }
    }

通过上面的认证代码不难看出参数所代表的含义。
var1代表证书信息,var2 就是调用时所传入的int值 2
通过***var1.getKeyUsage()***取出证书KeyUsage中的配置信息。
如果没有配置,不需要验证直接返回true。
如果有配置,则长度要大于var2的值,因为var2是下标,防止数组下标越界的判断。var3的长度是9准定大于2.
数组的值[true, false, false, false …] index 2所对应的数值是false。
所以代入方法,var3[var2] = false, 因为是&&所以返回false。

**var3[var2]**就是KeyUsage中的某个配置项,那是什么呢?怎么配置呢?

package sun.security.x509;
...
public class X509CertImpl extends X509Certificate implements DerEncoder {
...
 public boolean[] getKeyUsage() {
        try {
            String var1 = OIDMap.getName(PKIXExtensions.KeyUsage_Id);
            if (var1 == null) {
                return null;
            } else {
                KeyUsageExtension var2 = (KeyUsageExtension)this.get(var1);
                if (var2 == null) {
                    return null;
                } else {
                    boolean[] var3 = var2.getBits();
                    if (var3.length < 9) {
                        boolean[] var4 = new boolean[9];
                        System.arraycopy(var3, 0, var4, 0, var3.length);
                        var3 = var4;
                    }

                    return var3;
                }
            }
        } catch (Exception var5) {
            return null;
        }
    }
package sun.security.x509;
...
public class KeyUsageExtension extends Extension implements CertAttrSet<String> {
...
   public boolean[] getBits() {
        return (boolean[])this.bitString.clone();
    }
...
 public void set(String var1, Object var2) throws IOException {
        if (!(var2 instanceof Boolean)) {
            throw new IOException("Attribute must be of type Boolean.");
        } else {
            boolean var3 = (Boolean)var2;
            if (var1.equalsIgnoreCase("digital_signature")) {
                this.set(0, var3);
            } else if (var1.equalsIgnoreCase("non_repudiation")) {
                this.set(1, var3);
            } else if (var1.equalsIgnoreCase("key_encipherment")) {
                this.set(2, var3);
            } else if (var1.equalsIgnoreCase("data_encipherment")) {
                this.set(3, var3);
            } else if (var1.equalsIgnoreCase("key_agreement")) {
                this.set(4, var3);
            } else if (var1.equalsIgnoreCase("key_certsign")) {
                this.set(5, var3);
            } else if (var1.equalsIgnoreCase("crl_sign")) {
                this.set(6, var3);
            } else if (var1.equalsIgnoreCase("encipher_only")) {
                this.set(7, var3);
            } else {
                if (!var1.equalsIgnoreCase("decipher_only")) {
                    throw new IOException("Attribute name not recognized by CertAttrSet:KeyUsage.");
                }

                this.set(8, var3);
            }

            this.encodeThis();
        }
    }

通过代码的调用我们找到了KeyUsageExtension 类。
我们通过这个set方法来看,因为我们获取的值准定是先在读取证书的时候已经set了,所以看这块代码才能知道如何控制着数组。
index 为2的值是 key_encipherment
所以说明需要在证书的KeyUsage中添加此项才能通过此项认证,还有一种方式就是不配置KeyUsage.
解决方式在制作证书脚本中增加keyUsage = keyEncipherment

...
keyUsage = Digital Signature, keyEncipherment
...

验证通过!亲测!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值