《Java 加密工具与技术》8: X.509证书和属性证书
X.500 可辨别名称
X.500 可辨别名称(Distinguished Name,简称DN),是用于在X.500目录服务中唯一标识一个条目(Entry)的名称。它由一系列相对可辨别名称(Relative Distinguished Names,简称RDNs)组成,每个RDN由一个或多个属性值对构成,这些属性值对在不同的目录级别上提供了条目的唯一标识。在X.500目录服务中,DN用于确保每个条目都可以被唯一地识别和访问。
例如,一个X.500 DN可能看起来像这样:
CN=John Doe,OU=Users,DC=example,DC=com
在这个例子中:
-
CN=John Doe
是一个RDN,表示通用名称(Common Name)为“John Doe”。 -
OU=Users
是另一个RDN,表示组织单位(Organizational Unit)为“Users”。 -
DC=example
和DC=com
是额外的RDNs,表示域组件(Domain Component)分别为“example”和“com”。
X.500 DN在多种应用中被使用,包括在公钥基础设施(PKI)中标识证书持有者,以及在轻量级目录访问协议(LDAP)中标识目录条目。
X500Principal
使用示例
X500Principal
类用于表示X.500可辨别名称(Distinguished Name, DN)。它通常用于处理与安全相关的操作,例如在公钥基础设施(PKI)中处理X.509证书的主题或颁发者名称。
import javax.security.auth.x500.X500Principal;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import java.io.FileInputStream;
public class X500PrincipalExample {
public static void main(String[] args) {
try {
// 加载X.509证书
FileInputStream fis = new FileInputStream("path/to/your/certificate.crt");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
fis.close();
// 获取证书的主题DN
X500Principal subjectDN = cert.getSubjectX500Principal();
System.out.println("Subject DN: " + subjectDN.getName());
// 获取证书的颁发者DN
X500Principal issuerDN = cert.getIssuerX500Principal();
System.out.println("Issuer DN: " + issuerDN.getName());
// 使用X500Principal的构造函数创建一个新的DN
X500Principal newDN = new X500Principal("CN=John Doe, OU=Users, O=Example Corp, C=US");
System.out.println("New DN: " + newDN.getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
X500Name (BouncyCastle)使用示例
X500Name
类是用于表示X.500可辨别名称(Distinguished Name, DN)的另一种方式。它通常用于处理与安全相关的操作,例如在公钥基础设施(PKI)中处理X.509证书的主题或颁发者名称。X500Name
类通常与BouncyCastle库一起使用,因为它是BouncyCastle库的一部分,而不是标准JDK的一部分。
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;
import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateFactory;
import java.io.FileInputStream;
public class X500NameExample {
public static void main(String[] args) {
try {
// 加载X.509证书
FileInputStream fis = new FileInputStream("path/to/your/certificate.crt");
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
fis.close();
// 将X509Certificate转换为X500Name
JcaX509CertificateHolder certHolder = new JcaX509CertificateHolder(cert);
X500Name subjectDN = certHolder.getSubject();
X500Name issuerDN = certHolder.getIssuer();
// 输出主题DN
System.out.println("Subject DN: " + subjectDN.toString());
// 输出颁发者DN
System.out.println("Issuer DN: " + issuerDN.toString());
// 获取主题DN中的特定属性(例如通用名称CN)
String commonName = IETFUtils.valueToString(subjectDN.getRDNs(BCStyle.CN)[0].getFirst().getValue());
System.out.println("Common Name (CN): " + commonName);
// 创建一个新的X500Name
X500Name newDN = new X500Name("CN=John Doe, OU=Users, O=Example Corp, C=US");
System.out.println("New DN: " + newDN.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
X500NameBuilder 新建X500Name 示例
X500NameBuilder
是BouncyCastle库提供的一个工具类,用于构建 X500Name
对象。X500NameBuilder
提供了一种灵活的方式来创建和自定义X.500可辨别名称(Distinguished Name, DN)。
-
使用
addRDN
方法添加相对可辨别名称(RDN)。 -
每个RDN由一个属性类型(如
BCStyle.CN
)和一个属性值(如"John Doe"
)组成。 -
常用的属性类型包括:
-
BCStyle.CN
:通用名称(Common Name) -
BCStyle.OU
:组织单位(Organizational Unit) -
BCStyle.O
:组织名称(Organization) -
BCStyle.L
:地点(Locality) -
BCStyle.ST
:州/省(State or Province) -
BCStyle.C
:国家代码(Country Code)
-
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
public class X500NameBuilderExample {
public static void main(String[] args) {
// 创建X500NameBuilder实例
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
// 添加属性到DN
builder.addRDN(BCStyle.CN, "John Doe"); // 通用名称 (Common Name)
builder.addRDN(BCStyle.OU, "Engineering"); // 组织单位 (Organizational Unit)
builder.addRDN(BCStyle.O, "Example Corp"); // 组织名称 (Organization)
builder.addRDN(BCStyle.L, "New York"); // 地点 (Locality)
builder.addRDN(BCStyle.ST, "NY"); // 州/省 (State or Province)
builder.addRDN(BCStyle.C, "US"); // 国家代码 (Country Code)
// 构建X500Name对象
X500Name x500Name = builder.build();
// 输出X500Name
System.out.println("X500Name: " + x500Name.toString());
}
}
X500NameBuilder 修改X500Name 示例
X500Name originalName = new X500Name("CN=Alice, OU=Development, O=Example Corp, C=US");
// 使用X500NameBuilder从现有名称构建
X500NameBuilder builder = new X500NameBuilder(BCStyle.INSTANCE);
for (var rdn : originalName.getRDNs()) {
builder.addRDN(rdn.getFirst());
}
// 添加新属性
builder.addRDN(BCStyle.OU, "Engineering");
// 构建新的X500Name
X500Name newName = builder.build();
System.out.println("New X500Name: " + newName.toString());
公钥证书
1. X509v1CertificateBuilder
-
用途:用于构建X.509版本1(v1)证书。
-
特点:
-
X.509 v1 证书是最简单的证书格式,仅包含基本字段,如主题名称、颁发者名称、有效期和公钥。
-
不支持扩展:X.509 v1 证书没有扩展字段,因此无法添加自定义扩展(如密钥用途、扩展密钥用途等)。
-
使用场景:由于功能有限,X.509 v1 证书在现代应用中较少使用,通常用于简单的场景或兼容旧系统。
-
2. X509v2AttributeCertificateBuilder
-
用途:用于构建X.509版本2(v2)属性证书。
-
特点:
-
属性证书(Attribute Certificate)与传统的公钥证书不同,它不包含公钥,而是包含与某个实体(通常是公钥证书的持有者)相关的属性信息。
-
属性证书通常用于基于角色的访问控制(RBAC)或其他与权限相关的场景。
-
支持扩展:X.509 v2 属性证书可以包含扩展字段。
-
3. X509v3CertificateBuilder
-
用途:用于构建X.509版本3(v3)证书。
-
特点:
-
X.509 v3 证书是最常用的证书格式,支持扩展字段。
-
支持扩展:X.509 v3 证书可以包含各种扩展字段,如密钥用途(Key Usage)、扩展密钥用途(Extended Key Usage)、主题备用名称(Subject Alternative Name)等。
-
使用场景:X.509 v3 证书广泛应用于现代PKI(公钥基础设施)中,例如SSL/TLS证书、代码签名证书、客户端身份验证证书等。
-
创建一个基础的公钥证书
/**
* Calculate a date in seconds (suitable for the PKIX profile - RFC 5280)
*
* @param hoursInFuture hours ahead of now, may be negative.
* @return a Date set to now + (hoursInFuture * 60 * 60) seconds
*/
public static Date calculateDate(int hoursInFuture)
{
long secs = System.currentTimeMillis() / 1000;
return new Date((secs + (hoursInFuture * 60 * 60)) * 1000);
}
private static long serialNumberBase = System.currentTimeMillis();
/**
* Calculate a serial number using a monotonically increasing value.
*
* @return a BigInteger representing the next serial number in the sequence.
*/
public static synchronized BigInteger calculateSerialNumber()
{
return BigInteger.valueOf(serialNumberBase++);
}
/**
* Build a sample self-signed V1 certificate to use as a trust anchor, or
* root certificate.
*
* @param keyPair the key pair to use for signing and providing the
* public key.
* @param sigAlg the signature algorithm to sign the certificate with.
* @return an X509CertificateHolder containing the V1 certificate.
*/
public static X509CertificateHolder createTrustAnchor(
KeyPair keyPair, String sigAlg)
throws OperatorCreationException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo Root Certificate");
X500Name name = x500NameBld.build();
X509v1CertificateBuilder certBldr = new JcaX509v1CertificateBuilder(
name,
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
name,
keyPair.getPublic());
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(keyPair.getPrivate());
return certBldr.build(signer);
}
调用示例
public static X509KeyCertPair createTrustCert()
throws GeneralSecurityException, OperatorCreationException
{
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair trustKp = kpGen.generateKeyPair();
X509CertificateHolder trustCert =
createTrustAnchor(trustKp, "SHA256withECDSA");
return new X509KeyCertPair(trustKp, trustCert);
}
转换X509CertificateHolder为X509Certificate
/**
* Simple method to convert an X509CertificateHolder to an X509Certificate
* using the java.security.cert.CertificateFactory class.
*/
public static X509Certificate convertX509CertificateHolder(
X509CertificateHolder certHolder)
throws GeneralSecurityException, IOException
{
CertificateFactory cFact = CertificateFactory.getInstance("X.509", "BC");
return (X509Certificate)cFact.generateCertificate(
new ByteArrayInputStream(
certHolder.getEncoded()));
}
创建带有扩展的CA证书
/**
* Build a sample V3 intermediate certificate that can be used as a CA
* certificate.
*
* @param signerCert certificate carrying the public key that will later
* be used to verify this certificate's signature.
* @param signerKey private key used to generate the signature in the
* certificate.
* @param sigAlg the signature algorithm to sign the certificate with.
* @param certKey public key to be installed in the certificate.
* @param followingCACerts
* @return an X509CertificateHolder containing the V3 certificate.
*/
public static X509CertificateHolder createIntermediateCertificate(
X509CertificateHolder signerCert, PrivateKey signerKey,
String sigAlg, PublicKey certKey, int followingCACerts)
throws CertIOException, GeneralSecurityException,
OperatorCreationException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo Intermediate Certificate");
X500Name subject = x500NameBld.build();
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
signerCert.getSubject(),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
subject,
certKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier,
false, extUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier,
false, extUtils.createSubjectKeyIdentifier(certKey))
.addExtension(Extension.basicConstraints,
true, new BasicConstraints(followingCACerts))
.addExtension(Extension.keyUsage,
true, new KeyUsage(
KeyUsage.digitalSignature
| KeyUsage.keyCertSign
| KeyUsage.cRLSign));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(signerKey);
return certBldr.build(signer);
}
调用示例
public static X509KeyCertPair createInterCert(X509KeyCertPair trustPair)
throws GeneralSecurityException, OperatorCreationException, CertIOException
{
PrivateKey trustAnchorKey = trustPair.getKeyPair().getPrivate();
X509CertificateHolder trustCert = trustPair.getCert();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair caKp = kpGen.generateKeyPair();
X509CertificateHolder caCert =
createIntermediateCertificate(trustCert,
trustAnchorKey,
"SHA256withECDSA", caKp.getPublic(), 0);
return new X509KeyCertPair(caKp, caCert);
}
创建终端实体证书
/**
* Create a general end-entity certificate for use in verifying digital
* signatures.
*
* @param signerCert certificate carrying the public key that will later
* be used to verify this certificate's signature.
* @param signerKey private key used to generate the signature in the
* certificate.
* @param sigAlg the signature algorithm to sign the certificate with.
* @param certKey public key to be installed in the certificate.
* @return an X509CertificateHolder containing the V3 certificate.
*/
public static X509CertificateHolder createEndEntity(
X509CertificateHolder signerCert, PrivateKey signerKey,
String sigAlg, PublicKey certKey)
throws CertIOException, GeneralSecurityException,
OperatorCreationException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo End-Entity Certificate");
X500Name subject = x500NameBld.build();
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
signerCert.getSubject(),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
subject,
certKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier,
false, extUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier,
false, extUtils.createSubjectKeyIdentifier(certKey))
.addExtension(Extension.basicConstraints,
true, new BasicConstraints(false))
.addExtension(Extension.keyUsage,
true, new KeyUsage(KeyUsage.digitalSignature));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(signerKey);
return certBldr.build(signer);
}
调用示例
public static X509KeyCertPair createEECert(X509KeyCertPair caPair)
throws GeneralSecurityException, OperatorCreationException, CertIOException
{
PrivateKey caPrivKey = caPair.getKeyPair().getPrivate();
X509CertificateHolder caCert = caPair.getCert();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair eeKp = kpGen.generateKeyPair();
X509CertificateHolder eeCert =
createEndEntity(caCert, caPrivKey, "SHA256withECDSA", eeKp.getPublic());
return new X509KeyCertPair(eeKp, eeCert);
}
使用ExtendedKeyUsage
/**
* Create a special purpose end entity cert which is associated with a
* particular key purpose.
*
* @param signerCert certificate carrying the public key that will later
* be used to verify this certificate's signature.
* @param signerKey private key used to generate the signature in the
* certificate.
* @param sigAlg the signature algorithm to sign the certificate with.
* @param certKey public key to be installed in the certificate.
* @param keyPurpose the specific KeyPurposeId to associate with this
* certificate's public key.
* @return an X509CertificateHolder containing the V3 certificate.
*/
public static X509CertificateHolder createSpecialPurposeEndEntity(
X509CertificateHolder signerCert, PrivateKey signerKey,
String sigAlg, PublicKey certKey, KeyPurposeId keyPurpose)
throws OperatorCreationException, CertIOException,
GeneralSecurityException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo End-Entity Certificate");
X500Name subject = x500NameBld.build();
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
signerCert.getSubject(),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
subject,
certKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier,
false, extUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier,
false, extUtils.createSubjectKeyIdentifier(certKey))
.addExtension(Extension.basicConstraints,
true, new BasicConstraints(false))
.addExtension(Extension.extendedKeyUsage,
true, new ExtendedKeyUsage(keyPurpose));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(signerKey);
return certBldr.build(signer);
}
调用示例
public static X509KeyCertPair createEESpecCert(X509KeyCertPair caPair)
throws GeneralSecurityException, OperatorCreationException, CertIOException
{
PrivateKey caPrivKey = caPair.getKeyPair().getPrivate();
X509CertificateHolder caCert = caPair.getCert();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair specEEKp = kpGen.generateKeyPair();
X509CertificateHolder specEECert =
createSpecialPurposeEndEntity(caCert, caPrivKey,
"SHA256withECDSA",
specEEKp.getPublic(),
KeyPurposeId.id_kp_timeStamping);
return new X509KeyCertPair(specEEKp, specEECert);
}
属性证书
public static X509AttributeCertificateHolder createAttributeCertificate(
X509CertificateHolder issuerCert, PrivateKey issuerKey, String sigAlg,
X509CertificateHolder holderCert, String holderRoleUri)
throws OperatorCreationException
{
X509v2AttributeCertificateBuilder acBldr =
new X509v2AttributeCertificateBuilder(
new AttributeCertificateHolder(holderCert),
new AttributeCertificateIssuer(issuerCert.getSubject()),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 7));
GeneralName roleName = new GeneralName(
GeneralName.uniformResourceIdentifier, holderRoleUri);
acBldr.addAttribute(
X509AttributeIdentifiers.id_at_role, new RoleSyntax(roleName));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(issuerKey);
return acBldr.build(signer);
}
调用示例
public static X509AttributeCertificateHolder createAttrCertSample(PrivateKey issuerSigningKey, X509CertificateHolder issuerCert, X509CertificateHolder holderCert)
throws OperatorCreationException
{
X509AttributeCertificateHolder attrCert =
createAttributeCertificate(issuerCert,
issuerSigningKey,
"SHA256withECDSA", holderCert, "id://DAU123456789");
return attrCert;
}
附录(完整代码)
package chapter8;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.*;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v1CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date;
/**
* Example methods showing generation and verification of X.509 certificates
*/
public class JcaX509Certificate
{
/**
* Calculate a date in seconds (suitable for the PKIX profile - RFC 5280)
*
* @param hoursInFuture hours ahead of now, may be negative.
* @return a Date set to now + (hoursInFuture * 60 * 60) seconds
*/
public static Date calculateDate(int hoursInFuture)
{
long secs = System.currentTimeMillis() / 1000;
return new Date((secs + (hoursInFuture * 60 * 60)) * 1000);
}
private static long serialNumberBase = System.currentTimeMillis();
/**
* Calculate a serial number using a monotonically increasing value.
*
* @return a BigInteger representing the next serial number in the sequence.
*/
public static synchronized BigInteger calculateSerialNumber()
{
return BigInteger.valueOf(serialNumberBase++);
}
/**
* Simple method to convert an X509CertificateHolder to an X509Certificate
* using the java.security.cert.CertificateFactory class.
*/
public static X509Certificate convertX509CertificateHolder(
X509CertificateHolder certHolder)
throws GeneralSecurityException, IOException
{
CertificateFactory cFact = CertificateFactory.getInstance("X.509", "BC");
return (X509Certificate)cFact.generateCertificate(
new ByteArrayInputStream(
certHolder.getEncoded()));
}
/**
* Convert an X500Name to use the IETF style.
*/
public static X500Name toIETFName(X500Name name)
{
return X500Name.getInstance(RFC4519Style.INSTANCE, name);
}
public static X509KeyCertPair createTrustCert()
throws GeneralSecurityException, OperatorCreationException
{
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair trustKp = kpGen.generateKeyPair();
X509CertificateHolder trustCert =
createTrustAnchor(trustKp, "SHA256withECDSA");
return new X509KeyCertPair(trustKp, trustCert);
}
/**
* Build a sample self-signed V1 certificate to use as a trust anchor, or
* root certificate.
*
* @param keyPair the key pair to use for signing and providing the
* public key.
* @param sigAlg the signature algorithm to sign the certificate with.
* @return an X509CertificateHolder containing the V1 certificate.
*/
public static X509CertificateHolder createTrustAnchor(
KeyPair keyPair, String sigAlg)
throws OperatorCreationException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo Root Certificate");
X500Name name = x500NameBld.build();
X509v1CertificateBuilder certBldr = new JcaX509v1CertificateBuilder(
name,
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
name,
keyPair.getPublic());
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(keyPair.getPrivate());
return certBldr.build(signer);
}
public static X509KeyCertPair createInterCert(X509KeyCertPair trustPair)
throws GeneralSecurityException, OperatorCreationException, CertIOException
{
PrivateKey trustAnchorKey = trustPair.getKeyPair().getPrivate();
X509CertificateHolder trustCert = trustPair.getCert();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair caKp = kpGen.generateKeyPair();
X509CertificateHolder caCert =
createIntermediateCertificate(trustCert,
trustAnchorKey,
"SHA256withECDSA", caKp.getPublic(), 0);
return new X509KeyCertPair(caKp, caCert);
}
/**
* Build a sample V3 intermediate certificate that can be used as a CA
* certificate.
*
* @param signerCert certificate carrying the public key that will later
* be used to verify this certificate's signature.
* @param signerKey private key used to generate the signature in the
* certificate.
* @param sigAlg the signature algorithm to sign the certificate with.
* @param certKey public key to be installed in the certificate.
* @param followingCACerts
* @return an X509CertificateHolder containing the V3 certificate.
*/
public static X509CertificateHolder createIntermediateCertificate(
X509CertificateHolder signerCert, PrivateKey signerKey,
String sigAlg, PublicKey certKey, int followingCACerts)
throws CertIOException, GeneralSecurityException,
OperatorCreationException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo Intermediate Certificate");
X500Name subject = x500NameBld.build();
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
signerCert.getSubject(),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
subject,
certKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier,
false, extUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier,
false, extUtils.createSubjectKeyIdentifier(certKey))
.addExtension(Extension.basicConstraints,
true, new BasicConstraints(followingCACerts))
.addExtension(Extension.keyUsage,
true, new KeyUsage(
KeyUsage.digitalSignature
| KeyUsage.keyCertSign
| KeyUsage.cRLSign));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(signerKey);
return certBldr.build(signer);
}
public static X509KeyCertPair createEECert(X509KeyCertPair caPair)
throws GeneralSecurityException, OperatorCreationException, CertIOException
{
PrivateKey caPrivKey = caPair.getKeyPair().getPrivate();
X509CertificateHolder caCert = caPair.getCert();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair eeKp = kpGen.generateKeyPair();
X509CertificateHolder eeCert =
createEndEntity(caCert, caPrivKey, "SHA256withECDSA", eeKp.getPublic());
return new X509KeyCertPair(eeKp, eeCert);
}
/**
* Create a general end-entity certificate for use in verifying digital
* signatures.
*
* @param signerCert certificate carrying the public key that will later
* be used to verify this certificate's signature.
* @param signerKey private key used to generate the signature in the
* certificate.
* @param sigAlg the signature algorithm to sign the certificate with.
* @param certKey public key to be installed in the certificate.
* @return an X509CertificateHolder containing the V3 certificate.
*/
public static X509CertificateHolder createEndEntity(
X509CertificateHolder signerCert, PrivateKey signerKey,
String sigAlg, PublicKey certKey)
throws CertIOException, GeneralSecurityException,
OperatorCreationException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo End-Entity Certificate");
X500Name subject = x500NameBld.build();
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
signerCert.getSubject(),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
subject,
certKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier,
false, extUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier,
false, extUtils.createSubjectKeyIdentifier(certKey))
.addExtension(Extension.basicConstraints,
true, new BasicConstraints(false))
.addExtension(Extension.keyUsage,
true, new KeyUsage(KeyUsage.digitalSignature));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(signerKey);
return certBldr.build(signer);
}
public static X509KeyCertPair createEESpecCert(X509KeyCertPair caPair)
throws GeneralSecurityException, OperatorCreationException, CertIOException
{
PrivateKey caPrivKey = caPair.getKeyPair().getPrivate();
X509CertificateHolder caCert = caPair.getCert();
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("EC", "BC");
KeyPair specEEKp = kpGen.generateKeyPair();
X509CertificateHolder specEECert =
createSpecialPurposeEndEntity(caCert, caPrivKey,
"SHA256withECDSA",
specEEKp.getPublic(),
KeyPurposeId.id_kp_timeStamping);
return new X509KeyCertPair(specEEKp, specEECert);
}
/**
* Create a special purpose end entity cert which is associated with a
* particular key purpose.
*
* @param signerCert certificate carrying the public key that will later
* be used to verify this certificate's signature.
* @param signerKey private key used to generate the signature in the
* certificate.
* @param sigAlg the signature algorithm to sign the certificate with.
* @param certKey public key to be installed in the certificate.
* @param keyPurpose the specific KeyPurposeId to associate with this
* certificate's public key.
* @return an X509CertificateHolder containing the V3 certificate.
*/
public static X509CertificateHolder createSpecialPurposeEndEntity(
X509CertificateHolder signerCert, PrivateKey signerKey,
String sigAlg, PublicKey certKey, KeyPurposeId keyPurpose)
throws OperatorCreationException, CertIOException,
GeneralSecurityException
{
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE)
.addRDN(BCStyle.C, "AU")
.addRDN(BCStyle.ST, "Victoria")
.addRDN(BCStyle.L, "Melbourne")
.addRDN(BCStyle.O, "The Legion of the Bouncy Castle")
.addRDN(BCStyle.CN, "Demo End-Entity Certificate");
X500Name subject = x500NameBld.build();
X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
signerCert.getSubject(),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 31),
subject,
certKey);
JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
certBldr.addExtension(Extension.authorityKeyIdentifier,
false, extUtils.createAuthorityKeyIdentifier(signerCert))
.addExtension(Extension.subjectKeyIdentifier,
false, extUtils.createSubjectKeyIdentifier(certKey))
.addExtension(Extension.basicConstraints,
true, new BasicConstraints(false))
.addExtension(Extension.extendedKeyUsage,
true, new ExtendedKeyUsage(keyPurpose));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(signerKey);
return certBldr.build(signer);
}
/**
* Extract the DER encoded value octets of an extension from a JCA
* X509Certificate.
*
* @param cert the certificate of interest.
* @param extensionOID the OID associated with the extension of interest.
* @return the DER encoding inside the extension, null if extension missing.
*/
public static byte[] extractExtensionValue(
X509Certificate cert,
ASN1ObjectIdentifier extensionOID)
{
byte[] octString = cert.getExtensionValue(extensionOID.getId());
if (octString == null)
{
return null;
}
return ASN1OctetString.getInstance(octString).getOctets();
}
public static X509AttributeCertificateHolder createAttrCertSample(PrivateKey issuerSigningKey, X509CertificateHolder issuerCert, X509CertificateHolder holderCert)
throws OperatorCreationException
{
X509AttributeCertificateHolder attrCert =
createAttributeCertificate(issuerCert,
issuerSigningKey,
"SHA256withECDSA", holderCert, "id://DAU123456789");
return attrCert;
}
public static X509AttributeCertificateHolder createAttributeCertificate(
X509CertificateHolder issuerCert, PrivateKey issuerKey, String sigAlg,
X509CertificateHolder holderCert, String holderRoleUri)
throws OperatorCreationException
{
X509v2AttributeCertificateBuilder acBldr =
new X509v2AttributeCertificateBuilder(
new AttributeCertificateHolder(holderCert),
new AttributeCertificateIssuer(issuerCert.getSubject()),
calculateSerialNumber(),
calculateDate(0),
calculateDate(24 * 7));
GeneralName roleName = new GeneralName(
GeneralName.uniformResourceIdentifier, holderRoleUri);
acBldr.addAttribute(
X509AttributeIdentifiers.id_at_role, new RoleSyntax(roleName));
ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
.setProvider("BC").build(issuerKey);
return acBldr.build(signer);
}
}