简体   繁体   English

在没有 BouncyCastle 的情况下用 Java 创建 X509 证书?

[英]Creating an X509 Certificate in Java without BouncyCastle?

是否可以在不使用 Bouncy Castle X509V*CertificateGenerator类的情况下用 Java 代码X509V*CertificateGenerator创建 X509 证书?

Yes, but not with publicly documented classes.是的,但不是公开记录的课程。 I've documented the process in this article .在这篇文章中记录了这个过程。

import sun.security.x509.*;
import java.security.cert.*;
import java.security.*;
import java.math.BigInteger;
import java.util.Date;
import java.io.IOException

/** 
 * Create a self-signed X.509 Certificate
 * @param dn the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB"
 * @param pair the KeyPair
 * @param days how many days from now the Certificate is valid for
 * @param algorithm the signing algorithm, eg "SHA1withRSA"
 */ 
X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm)
  throws GeneralSecurityException, IOException
{
  PrivateKey privkey = pair.getPrivate();
  X509CertInfo info = new X509CertInfo();
  Date from = new Date();
  Date to = new Date(from.getTime() + days * 86400000l);
  CertificateValidity interval = new CertificateValidity(from, to);
  BigInteger sn = new BigInteger(64, new SecureRandom());
  X500Name owner = new X500Name(dn);
 
  info.set(X509CertInfo.VALIDITY, interval);
  info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
  info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
  info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
  info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
  info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
  AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
  info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
 
  // Sign the cert to identify the algorithm that's used.
  X509CertImpl cert = new X509CertImpl(info);
  cert.sign(privkey, algorithm);
 
  // Update the algorith, and resign.
  algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
  info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
  cert = new X509CertImpl(info);
  cert.sign(privkey, algorithm);
  return cert;
}   

The ability to sign certificates is not part of a standard Java library or extension.签署证书的能力不是标准 Java 库或扩展的一部分。

A lot of the code that is needed to do it yourself is part of the core.很多需要自己做的代码是核心的一部分。 There are classes to encode and decode X.500 names, X.509 certificate extensions, public keys for various algorithms, and of course, for actually performing the digital signature.有一些类可以编码和解码 X.500 名称、X.509 证书扩展、各种算法的公钥,当然还有用于实际执行数字签名的类。

Implementing this yourself is not trivial, but it is definitely doable—I probably spent 4 or 5 full days the first time I made a working prototype for certificate signing.自己实现这一点并非易事,但绝对可行——我第一次制作证书签名的工作原型时可能花了 4 到 5 天的时间。 It was a fantastic learning exercise for me, but it's hard to justify that expense when there are usable libraries available for free.这对我来说是一个很棒的学习练习,但是当有可用的免费图书馆时,很难证明这笔费用是合理的。

import sun.security.x509.*;

import java.security.cert.*;
import java.security.*;
import java.math.BigInteger;
import java.security.cert.Certificate;
import java.util.Date;
import java.io.IOException;

public class Example {
    /**
     * Create a self-signed X.509 Example
     *
     * @param dn        the X.509 Distinguished Name, eg "CN=Test, L=London, C=GB"
     * @param pair      the KeyPair
     * @param days      how many days from now the Example is valid for
     * @param algorithm the signing algorithm, eg "SHA1withRSA"
     */
    public X509Certificate generateCertificate(String dn, KeyPair pair, int days, String algorithm)
            throws GeneralSecurityException, IOException {
        PrivateKey privkey = pair.getPrivate();
        X509CertInfo info = new X509CertInfo();
        Date from = new Date();
        Date to = new Date(from.getTime() + days * 86400000l);
        CertificateValidity interval = new CertificateValidity(from, to);
        BigInteger sn = new BigInteger(64, new SecureRandom());
        X500Name owner = new X500Name(dn);

        info.set(X509CertInfo.VALIDITY, interval);
        info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
        info.set(X509CertInfo.SUBJECT, owner);
        info.set(X509CertInfo.ISSUER, owner);
        info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
        info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
        AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
        info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

        // Sign the cert to identify the algorithm that's used.
        X509CertImpl cert = new X509CertImpl(info);
        cert.sign(privkey, algorithm);

        // Update the algorith, and resign.
        algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG);
        info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
        cert = new X509CertImpl(info);
        cert.sign(privkey, algorithm);
        return cert;
    }

    public static void main (String[] argv) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        Example example = new Example();
        String distinguishedName = "CN=Test, L=London, C=GB";
        Certificate certificate = example.generateCertificateOriginal(distinguishedName, keyPair, 365, "SHA256withRSA");
        System.out.println("it worked!");
    }
}

I liked vbence's answer, but I kept getting the following exception:我喜欢 vbence 的回答,但我不断收到以下异常:

java.security.cert.CertificateException: Subject class type invalid. java.security.cert.CertificateException:主题类类型无效。

After lots of attempts to find out was a valid subject class I found out that X509CerInfo wanted an instance of X500Name.许多试图找出一个有效的主题类后,我发现,X509CerInfo想X500Name的一个实例。

1 info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
2 info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
3 info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
4 info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));

So lines 2 & 3 needed to change to所以第 2 行和第 3 行需要更改为

2 info.set(X509CertInfo.SUBJECT, owner);
3 info.set(X509CertInfo.ISSUER, owner);

All the basic components to make a self-signed certificate (signing, X509 encoding etc) are available in JRE. JRE 中提供了制作自签名证书的所有基本组件(签名、X509 编码等)。 Unlike BC, Sun's JCE doesn't provide any public calls to sign a certificate.与 BC 不同,Sun 的 JCE 不提供任何公共调用来签署证书。 However, all the functions are available in Keytool.但是,Keytool 中提供了所有功能。 You can simply copy the code from keytool to do this.您可以简单地从 keytool 复制代码来执行此操作。 The method you need to copy is doSelfCert() .您需要复制的方法是doSelfCert()

Depends on what exactly you want to do (and probably your definition of "Sanely").取决于你到底想做什么(可能还有你对“理智”的定义)。 As ZZ Coder pointed out, you can create a self-signed certificate directly by copying keytool .正如 ZZ Coder 所指出的,您可以通过复制keytool直接创建自签名证书。 But I don't believe you can create a PKCS10 certificate request object with the standard JCE, which you probably need to do if you want to create standard CA-signed EECs.但我不相信您可以使用标准 JCE 创建 PKCS10 证书请求对象,如果您想创建标准的 CA 签名 EEC,您可能需要这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM