在 java 中以编程方式生成 CSR 时添加 SAN 名称
Add SAN names while generating a CSR in java Programmatically
我正在使用以下代码在 java 中生成 CSR:
package demo;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import sun.security.pkcs10.PKCS10;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.GeneralName;
import sun.security.x509.GeneralNames;
import sun.security.x509.SubjectAlternativeNameExtension;
import sun.security.x509.X500Name;
/**
* This class generates PKCS10 certificate signing request
*
* @author Pankaj@JournalDev.com
* @version 1.0
*/
public class GenerateCSR {
private static PublicKey publicKey = null;
private static PrivateKey privateKey = null;
private static KeyPairGenerator keyGen = null;
private static GenerateCSR gcsr = null;
private GenerateCSR() {
try {
keyGen = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keyGen.initialize(2048, new SecureRandom());
KeyPair keypair = keyGen.generateKeyPair();
publicKey = keypair.getPublic();
privateKey = keypair.getPrivate();
}
public static GenerateCSR getInstance() {
if (gcsr == null)
gcsr = new GenerateCSR();
return gcsr;
}
public String getCSR(String cn) throws Exception {
byte[] csr = generatePKCS10(cn, "Java", "JournalDev", "Cupertino", "California", "USA");
return new String(csr);
}
/**
*
* @param CN Common Name, is X.509 speak for the name that distinguishes the Certificate best, and ties it to your
* Organization
* @param OU Organizational unit
* @param O Organization NAME
* @param L Location
* @param S State
* @param C Country
* @return
* @throws Exception
*/
private static byte[] generatePKCS10(String CN, String OU, String O, String L, String S, String C)
throws Exception {
GeneralNames generalNames = new GeneralNames();
generalNames.add(new GeneralName(new DerValue("b")));
generalNames.add(new GeneralName(new DerValue("a")));
CertificateExtensions ext = new CertificateExtensions();
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
// generate PKCS10 certificate request
String sigAlg = "MD5WithRSA";
PKCS10 pkcs10 = new PKCS10(publicKey);
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
// common, orgUnit, org, locality, state, country
X500Name x500Name = new X500Name(CN, OU, O, L, S, C);
pkcs10.encodeAndSign(x500Name, signature);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
pkcs10.print(ps);
byte[] c = bs.toByteArray();
try {
if (ps != null)
ps.close();
if (bs != null)
bs.close();
} catch (Throwable th) {
}
return c;
}
public PublicKey getPublicKey() {
return publicKey;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public static void main(String[] args) throws Exception {
GenerateCSR gcsr = GenerateCSR.getInstance();
System.out.println("Public Key:\n" + gcsr.getPublicKey().toString());
System.out.println("Private Key:\n" + gcsr.getPrivateKey().toString());
String csr = gcsr.getCSR("journaldev.com <https://www.journaldev.com>");
System.out.println("CSR Request Generated!!");
System.out.println(csr);
}
}
如您所见,我正在使用以下代码添加 SAN 名称
GeneralNames generalNames = new GeneralNames();
generalNames.add(new GeneralName(new DerValue("b")));
generalNames.add(new GeneralName(new DerValue("a")));
CertificateExtensions ext = new CertificateExtensions();
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
我的问题是:
- 这是正确的方法吗?
- 如果是如何使用CertificateExtensions的对象以及传递到哪里。
我指的是 问题,那里提到我必须在证书的构造函数中传递它,但我不能让 X500Name 的构造函数只允许字符串值。
正如我在评论中所述,sun.*
类 不打算被其他程序员使用,我个人永远不会使用它们。 Bouncycastle 库可以做这一切,甚至更多。但是,如果您坚持使用这些 类,那么您将需要使用更多才能达到预期效果。注意:由于 类 没有记录在案,我这里的内容主要是实验结果。
请注意,MD5 不能用于签名,对于此类应用程序来说,它是完全不安全的。我已经用 SHA256 替换了它。
考虑一下我修改过的这段代码。名称本身只是示例:
// ...
import sun.security.pkcs10.PKCS10Attribute;
import sun.security.pkcs10.PKCS10Attributes;
import sun.security.x509.*;
import sun.security.pkcs10.PKCS10;
import sun.security.pkcs.PKCS9Attribute;
// ....
GeneralNames generalNames = new GeneralNames();
generalNames.add(new GeneralName(new DNSName("a.example.com")));
generalNames.add(new GeneralName(new DNSName("never.ever.example.com")));
generalNames.add(new GeneralName(new IPAddressName("192.168.1.250")));
CertificateExtensions ext = new CertificateExtensions();
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
var pkcs9Attr = new PKCS9Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext);
var pkcs10Attrs = new PKCS10Attributes(new PKCS10Attribute[] {
new PKCS10Attribute(pkcs9Attr)
});
// generate PKCS10 certificate request
String sigAlg = "SHA256WithRSA";
PKCS10 pkcs10 = new PKCS10(publicKey, pkcs10Attrs);
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
// common, orgUnit, org, locality, state, country
X500Name x500Name = new X500Name(CN, OU, O, L, S, C);
pkcs10.encodeAndSign(x500Name, signature);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
pkcs10.print(ps);
这应该会生成一个带有正确 subjectAltName 扩展名的 PKCS10 证书请求。
如果有人想在以后引用它,请发布使用充气城堡的代码片段。它包括将 SAN 名称添加到证书中。
创建 CSR 还包括以下步骤:
生成一对非对称密钥(private+public),通常,您将私钥保存在服务器的安全位置。
创建一个证书,添加有关您的域名的信息(也可以使用通配符或 SAN 名称)、有关您的组织、城市、州等的信息(您可以通过以下代码执行此操作)或命令行工具,如 OpenSSL 或 java keytool)。
通常,您的 CSR 还将包含您的 public 密钥,并且由您的私钥签名。
生成 CSR 后,您会将其提交给受信任且知名的证书颁发机构进行签名,或者如果您在专用网络或开发环境中,您可以自行签名还有。
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.IPAddress;
public class BouncyCastle {
public static void main(String[] args)
throws OperatorCreationException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal(
"CN=my.example.com, C=US, ST=Texas, L=Austin, O=Example ORG, OU=Engineering"),
keyPair.getPublic());
List<String> extensions = new ArrayList<>();
extensions.add("tx9918-otr01.my.example.com");
extensions.add("tx9918-otr02.my.example.com");
extensions.add("tx9918-otr03.my.example.com");
extensions.add("tx9918-otr04.my.example.com");
p10Builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
createDomainAlternativeNamesExtensions(extensions));
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(keyPair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(new FileWriter(new File("D:\Titans\cert\test.csr")));
jcaPEMWriter.writeObject(csr);
jcaPEMWriter.close();
}
public static Extensions createDomainAlternativeNamesExtensions(List<String> domainAlternativeNames)
throws IOException {
List<GeneralName> namesList = new ArrayList<>();
if (domainAlternativeNames != null) {
for (String alternativeName : domainAlternativeNames) {
namesList.add(new GeneralName(
IPAddress.isValid(alternativeName) ? GeneralName.iPAddress : GeneralName.dNSName,
alternativeName));
}
}
GeneralNames subjectAltNames = new GeneralNames(namesList.toArray(new GeneralName[] {}));
ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
return extGen.generate();
}
}
我正在使用以下代码在 java 中生成 CSR:
package demo;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import sun.security.pkcs10.PKCS10;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import sun.security.x509.CertificateExtensions;
import sun.security.x509.GeneralName;
import sun.security.x509.GeneralNames;
import sun.security.x509.SubjectAlternativeNameExtension;
import sun.security.x509.X500Name;
/**
* This class generates PKCS10 certificate signing request
*
* @author Pankaj@JournalDev.com
* @version 1.0
*/
public class GenerateCSR {
private static PublicKey publicKey = null;
private static PrivateKey privateKey = null;
private static KeyPairGenerator keyGen = null;
private static GenerateCSR gcsr = null;
private GenerateCSR() {
try {
keyGen = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keyGen.initialize(2048, new SecureRandom());
KeyPair keypair = keyGen.generateKeyPair();
publicKey = keypair.getPublic();
privateKey = keypair.getPrivate();
}
public static GenerateCSR getInstance() {
if (gcsr == null)
gcsr = new GenerateCSR();
return gcsr;
}
public String getCSR(String cn) throws Exception {
byte[] csr = generatePKCS10(cn, "Java", "JournalDev", "Cupertino", "California", "USA");
return new String(csr);
}
/**
*
* @param CN Common Name, is X.509 speak for the name that distinguishes the Certificate best, and ties it to your
* Organization
* @param OU Organizational unit
* @param O Organization NAME
* @param L Location
* @param S State
* @param C Country
* @return
* @throws Exception
*/
private static byte[] generatePKCS10(String CN, String OU, String O, String L, String S, String C)
throws Exception {
GeneralNames generalNames = new GeneralNames();
generalNames.add(new GeneralName(new DerValue("b")));
generalNames.add(new GeneralName(new DerValue("a")));
CertificateExtensions ext = new CertificateExtensions();
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
// generate PKCS10 certificate request
String sigAlg = "MD5WithRSA";
PKCS10 pkcs10 = new PKCS10(publicKey);
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
// common, orgUnit, org, locality, state, country
X500Name x500Name = new X500Name(CN, OU, O, L, S, C);
pkcs10.encodeAndSign(x500Name, signature);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
pkcs10.print(ps);
byte[] c = bs.toByteArray();
try {
if (ps != null)
ps.close();
if (bs != null)
bs.close();
} catch (Throwable th) {
}
return c;
}
public PublicKey getPublicKey() {
return publicKey;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public static void main(String[] args) throws Exception {
GenerateCSR gcsr = GenerateCSR.getInstance();
System.out.println("Public Key:\n" + gcsr.getPublicKey().toString());
System.out.println("Private Key:\n" + gcsr.getPrivateKey().toString());
String csr = gcsr.getCSR("journaldev.com <https://www.journaldev.com>");
System.out.println("CSR Request Generated!!");
System.out.println(csr);
}
}
如您所见,我正在使用以下代码添加 SAN 名称
GeneralNames generalNames = new GeneralNames();
generalNames.add(new GeneralName(new DerValue("b")));
generalNames.add(new GeneralName(new DerValue("a")));
CertificateExtensions ext = new CertificateExtensions();
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
我的问题是:
- 这是正确的方法吗?
- 如果是如何使用CertificateExtensions的对象以及传递到哪里。
我指的是
正如我在评论中所述,sun.*
类 不打算被其他程序员使用,我个人永远不会使用它们。 Bouncycastle 库可以做这一切,甚至更多。但是,如果您坚持使用这些 类,那么您将需要使用更多才能达到预期效果。注意:由于 类 没有记录在案,我这里的内容主要是实验结果。
请注意,MD5 不能用于签名,对于此类应用程序来说,它是完全不安全的。我已经用 SHA256 替换了它。
考虑一下我修改过的这段代码。名称本身只是示例:
// ...
import sun.security.pkcs10.PKCS10Attribute;
import sun.security.pkcs10.PKCS10Attributes;
import sun.security.x509.*;
import sun.security.pkcs10.PKCS10;
import sun.security.pkcs.PKCS9Attribute;
// ....
GeneralNames generalNames = new GeneralNames();
generalNames.add(new GeneralName(new DNSName("a.example.com")));
generalNames.add(new GeneralName(new DNSName("never.ever.example.com")));
generalNames.add(new GeneralName(new IPAddressName("192.168.1.250")));
CertificateExtensions ext = new CertificateExtensions();
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
var pkcs9Attr = new PKCS9Attribute(PKCS9Attribute.EXTENSION_REQUEST_OID, ext);
var pkcs10Attrs = new PKCS10Attributes(new PKCS10Attribute[] {
new PKCS10Attribute(pkcs9Attr)
});
// generate PKCS10 certificate request
String sigAlg = "SHA256WithRSA";
PKCS10 pkcs10 = new PKCS10(publicKey, pkcs10Attrs);
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
// common, orgUnit, org, locality, state, country
X500Name x500Name = new X500Name(CN, OU, O, L, S, C);
pkcs10.encodeAndSign(x500Name, signature);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs);
pkcs10.print(ps);
这应该会生成一个带有正确 subjectAltName 扩展名的 PKCS10 证书请求。
如果有人想在以后引用它,请发布使用充气城堡的代码片段。它包括将 SAN 名称添加到证书中。
创建 CSR 还包括以下步骤:
生成一对非对称密钥(private+public),通常,您将私钥保存在服务器的安全位置。
创建一个证书,添加有关您的域名的信息(也可以使用通配符或 SAN 名称)、有关您的组织、城市、州等的信息(您可以通过以下代码执行此操作)或命令行工具,如 OpenSSL 或 java keytool)。
通常,您的 CSR 还将包含您的 public 密钥,并且由您的私钥签名。
生成 CSR 后,您会将其提交给受信任且知名的证书颁发机构进行签名,或者如果您在专用网络或开发环境中,您可以自行签名还有。
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.ArrayList;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.IPAddress;
public class BouncyCastle {
public static void main(String[] args)
throws OperatorCreationException, NoSuchAlgorithmException, NoSuchProviderException, IOException {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal(
"CN=my.example.com, C=US, ST=Texas, L=Austin, O=Example ORG, OU=Engineering"),
keyPair.getPublic());
List<String> extensions = new ArrayList<>();
extensions.add("tx9918-otr01.my.example.com");
extensions.add("tx9918-otr02.my.example.com");
extensions.add("tx9918-otr03.my.example.com");
extensions.add("tx9918-otr04.my.example.com");
p10Builder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,
createDomainAlternativeNamesExtensions(extensions));
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(keyPair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(new FileWriter(new File("D:\Titans\cert\test.csr")));
jcaPEMWriter.writeObject(csr);
jcaPEMWriter.close();
}
public static Extensions createDomainAlternativeNamesExtensions(List<String> domainAlternativeNames)
throws IOException {
List<GeneralName> namesList = new ArrayList<>();
if (domainAlternativeNames != null) {
for (String alternativeName : domainAlternativeNames) {
namesList.add(new GeneralName(
IPAddress.isValid(alternativeName) ? GeneralName.iPAddress : GeneralName.dNSName,
alternativeName));
}
}
GeneralNames subjectAltNames = new GeneralNames(namesList.toArray(new GeneralName[] {}));
ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
return extGen.generate();
}
}