com.venafi.vcert.sdk.certificate.CertificateRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vcert-java Show documentation
Show all versions of vcert-java Show documentation
VCert is a Java library, SDK, designed to simplify key generation and enrollment of machine identities (also known as SSL/TLS certificates and keys) that comply with enterprise security policy by using the Venafi Platform or Venafi Cloud.
package com.venafi.vcert.sdk.certificate;
import static java.lang.String.format;
import static java.time.temporal.ChronoUnit.MINUTES;
import static java.util.Collections.emptyList;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.net.InetAddress;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import javax.security.auth.x500.X500Principal;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.AlgorithmNameFinder;
import org.bouncycastle.operator.DefaultAlgorithmNameFinder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemReader;
import com.google.common.annotations.VisibleForTesting;
import com.venafi.vcert.sdk.SignatureAlgorithm;
import com.venafi.vcert.sdk.VCertException;
import lombok.Data;
@Data
public class CertificateRequest {
private PKIXName subject; // TODO change to X500Name
private Collection dnsNames;
private Collection emailAddresses;
private Collection ipAddresses;
private Collection attributes;
private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.UnknownSignatureAlgorithm;
private String friendlyName;
private KeyType keyType;
private int keyLength;
private EllipticCurve keyCurve;
private byte[] csr;
private KeyPair keyPair;
private CsrOriginOption csrOrigin = CsrOriginOption.defaultCsrOrigin();
private String pickupId;
private ChainOption chainOption;
private String keyPassword;
private boolean fetchPrivateKey;
private String thumbprint;
private Duration timeout;
private int validityHours;
private String issuerHint;
private Collection customFields;
public CertificateRequest() {
this.dnsNames = emptyList();
this.emailAddresses = emptyList();
this.ipAddresses = emptyList();
this.attributes = emptyList();
}
public Duration timeout() {
return (!Objects.isNull(timeout)) ? timeout : Duration.of(5, MINUTES);
}
public ChainOption chainOption() {
return (!Objects.isNull(chainOption)) ? chainOption : ChainOption.ChainOptionRootFirst;
}
public PrivateKey privateKey() {
return (!Objects.isNull(keyPair)) ? keyPair.getPrivate() : null;
}
public void generatePrivateKey() throws VCertException {
if (keyPair != null) {
return;
}
switch (keyType) {
case ECDSA: {
keyPair = generateECDSAKeyPair(keyCurve);
break;
}
case RSA: {
if (keyLength == 0) {
keyLength = KeyType.defaultRsaLength();
}
keyPair = generateRSAKeyPair(keyLength);
break;
}
default:
throw new VCertException(
format("Unable to generate certificate request, key type %s is not supported",
keyType.name()));
}
}
public void generateCSR() throws VCertException {
try {
List sans = new ArrayList<>();
PKCS10CertificationRequestBuilder requestBuilder =
new JcaPKCS10CertificationRequestBuilder(subject.toX500Principal(), keyPair.getPublic());
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm.standardName());
ContentSigner signer = signerBuilder.build(keyPair.getPrivate());
for (String san : dnsNames) {
sans.add(new GeneralName(GeneralName.dNSName, san));
}
for (InetAddress san : ipAddresses) {
sans.add(new GeneralName(GeneralName.iPAddress, new DEROctetString(san.getAddress())));
}
for (String san : emailAddresses) {
sans.add(new GeneralName(GeneralName.rfc822Name, san));
}
if (!sans.isEmpty()){
GeneralNames names = new GeneralNames(sans.toArray(new GeneralName[]{}));
ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(Extension.subjectAlternativeName, false, names);
requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());
}
PKCS10CertificationRequest certificationRequest = requestBuilder.build(signer);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write("-----BEGIN CERTIFICATE REQUEST-----".getBytes());
outputStream.write(System.lineSeparator().getBytes());
outputStream.write(Base64.getMimeEncoder().encode(certificationRequest.getEncoded()));
outputStream.write(System.lineSeparator().getBytes());
outputStream.write("-----END CERTIFICATE REQUEST-----".getBytes());
csr = outputStream.toByteArray();
} catch (Exception e) {
throw new VCertException("Unable to generate CSR", e);
}
}
@VisibleForTesting
KeyPair generateECDSAKeyPair(EllipticCurve keyCurve) throws VCertException {
try {
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
ECGenParameterSpec spec = new ECGenParameterSpec(keyCurve.bcName());
g.initialize(spec);
return g.generateKeyPair();
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
throw new VCertException("No security provider found for KeyFactory.EC", e);
} catch (InvalidAlgorithmParameterException e) {
throw new VCertException(format("No algorithmn provider for curve %s", keyCurve.bcName()), e);
}
}
@VisibleForTesting
KeyPair generateRSAKeyPair(Integer keyLength) throws VCertException {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC");
keyPairGenerator.initialize(keyLength);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException e) {
throw new VCertException("No security provider found for KeyFactory.RSA", e);
} catch (NoSuchProviderException e) {
throw new VCertException(
format("No algorithm provider for RSA with key length %s", Integer.toString(keyLength)),
e);
}
}
@Data
public static class PKIXName {
private static void addAll(X500NameBuilder builder, ASN1ObjectIdentifier identifier,
Collection values) {
if (values != null) {
values.stream().filter(Objects::nonNull)
.forEach(value -> builder.addRDN(identifier, value));
}
}
private String commonName;
private String serialNumber;
private List country;
private List organization;
private List organizationalUnit;
private List locality;
private List province;
private List streetAddress;
private List postalCode;
private Collection names;
private Collection extraNames;
public X500Principal toX500Principal() throws VCertException {
if (isBlank(commonName)) {
throw new VCertException("common name must not be null or emtpy");
}
X500NameBuilder x500NameBuilder = new X500NameBuilder();
x500NameBuilder.addRDN(BCStyle.CN, commonName);
addAll(x500NameBuilder, BCStyle.C, country);
addAll(x500NameBuilder, BCStyle.O, organization);
addAll(x500NameBuilder, BCStyle.OU, organizationalUnit);
addAll(x500NameBuilder, BCStyle.L, locality);
addAll(x500NameBuilder, BCStyle.ST, province);
addAll(x500NameBuilder, BCStyle.STREET, streetAddress);
addAll(x500NameBuilder, BCStyle.POSTAL_CODE, postalCode);
// todo: serialNumber, names, extraNames
return new X500Principal(x500NameBuilder.build().toString());
}
}
// Todo do we need this?
@Data
public static class AttributeTypeAndValue {
private Collection type;
private Object value;
}
// Todo do we need this?
@Data
public static class AttributeTypeAndValueSET {
private Collection type;
private Collection> value;
}
public boolean checkCertificate(Certificate certificate) throws VCertException {
PublicKeyAlgorithm publicKeyAlgorithm =
KeyType.from(certificate.getPublicKey().getAlgorithm()).X509Type();
if (keyPair != null && keyPair.getPublic() != null && keyPair.getPrivate() != null) {
keyType = keyType == null ? KeyType.defaultKeyType() : keyType;
if (keyType.X509Type() != publicKeyAlgorithm) {
throw new VCertException(
format("unmatched key type: %s, %s", keyType.X509Type(), publicKeyAlgorithm.name()));
}
switch (publicKeyAlgorithm) {
case RSA:
RSAPublicKey certPublicKey = (RSAPublicKey) certificate.getPublicKey();
RSAPublicKey reqPublicKey = (RSAPublicKey) keyPair.getPublic();
// TODO can be equals?
if (certPublicKey.getModulus().compareTo(reqPublicKey.getModulus()) != 0) {
throw new VCertException("unmatched key modules");
}
break;
case ECDSA:
ECPublicKey certEcPublicKey = (ECPublicKey) certificate.getPublicKey();
ECPublicKey reqEcPublicKey = (ECPublicKey) keyPair.getPublic();
// https://stackoverflow.com/questions/24121801/how-to-verify-if-the-private-key-matches-with-the-certificate
java.security.spec.ECParameterSpec certSpec = certEcPublicKey.getParams(),
csrSpec = reqEcPublicKey.getParams();
java.security.spec.EllipticCurve certCurve = certSpec.getCurve(),
csrCurve = csrSpec.getCurve();
java.security.spec.ECField certField = certCurve.getField(),
csrField = csrCurve.getField();
if (certSpec != csrSpec //
&& (certSpec.getCofactor() != csrSpec.getCofactor() //
|| !certSpec.getOrder().equals(csrSpec.getOrder()) //
|| !certSpec.getGenerator().equals(csrSpec.getGenerator()) //
|| certCurve != csrCurve //
&& (!certCurve.getA().equals(csrCurve.getA()) //
|| !certCurve.getB().equals(csrCurve.getB()) //
|| certField.getFieldSize() != csrField.getFieldSize()))) {
throw new VCertException("unmatched parameters for elliptic keys");
}
break;
default:
throw new VCertException(format("unknown key algorithm %s", publicKeyAlgorithm.name()));
}
} else if (Objects.nonNull(csr) && csr.length != 0) {
try {
PemReader pemReader = new PemReader(new StringReader(new String(csr)));
PKCS10CertificationRequest csr =
new PKCS10CertificationRequest(pemReader.readPemObject().getContent());
pemReader.close();
AlgorithmNameFinder nameFinder = new DefaultAlgorithmNameFinder();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
PublicKeyAlgorithm csrPublicKeyAlgorithm =
PublicKeyAlgorithm.valueOf(String.valueOf(nameFinder.getAlgorithmName(csr.getSubjectPublicKeyInfo().getAlgorithm())));
if (publicKeyAlgorithm != csrPublicKeyAlgorithm) {
throw new VCertException(
format("unmatched key type: %s, %s", publicKeyAlgorithm, csrPublicKeyAlgorithm));
}
switch (csrPublicKeyAlgorithm) {
case RSA:
RSAPublicKey certPublicKey = (RSAPublicKey) certificate.getPublicKey();
RSAPublicKey reqPublicKey = (RSAPublicKey) converter.getPublicKey(csr.getSubjectPublicKeyInfo());
if (certPublicKey.getModulus().compareTo(reqPublicKey.getModulus()) != 0) {
throw new VCertException("unmatched key modules");
}
break;
case ECDSA:
ECPublicKey certEcPublicKey = (ECPublicKey) certificate.getPublicKey();
ECPublicKey reqEcPublicKey = (ECPublicKey) converter.getPublicKey(csr.getSubjectPublicKeyInfo());
// https://stackoverflow.com/questions/24121801/how-to-verify-if-the-private-key-matches-with-the-certificate
java.security.spec.ECParameterSpec certSpec = certEcPublicKey.getParams(),
csrSpec = reqEcPublicKey.getParams();
java.security.spec.EllipticCurve certCurve = certSpec.getCurve(),
csrCurve = csrSpec.getCurve();
java.security.spec.ECField certField = certCurve.getField(),
csrField = csrCurve.getField();
if (certSpec != csrSpec //
&& (certSpec.getCofactor() != csrSpec.getCofactor() //
|| !certSpec.getOrder().equals(csrSpec.getOrder()) //
|| !certSpec.getGenerator().equals(csrSpec.getGenerator()) //
|| certCurve != csrCurve //
&& (!certCurve.getA().equals(csrCurve.getA()) //
|| !certCurve.getB().equals(csrCurve.getB()) //
|| certField.getFieldSize() != csrField.getFieldSize()))) {
throw new VCertException("unmatched parameters for elliptic keys");
}
break;
}
} catch (IOException e) {
throw new VCertException(format("bad csr: %s", e.getMessage()), e);
}
}
return true;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy