Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.puppetlabs.ssl_utils.ExtensionsUtils Maven / Gradle / Ivy
package com.puppetlabs.ssl_utils;
import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.Attribute;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509ExtensionUtils;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import java.io.EOFException;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.*;
/**
* Utilities for working with X509 extensions.
* Copied from https://github.com/puppetlabs/jvm-ssl-utils due to package-only visibility of useful methods.
*
* Original license is Apache License Version 2.0
*
* Copyright (C) 2015 Puppet Labs Inc
*
*/
public class ExtensionsUtils {
/**
* CRLNumber OID 2.5.29.20
*/
public static final String CRL_NUMBER_OID = Extension.cRLNumber.toString();
/**
* AuthorityKeyIdentifier OID 2.5.29.35
*/
public static final String AUTHORITY_KEY_IDENTIFIER_OID =
Extension.authorityKeyIdentifier.toString();
/**
* SubjectAlternativeName OID 2.5.29.17
*/
public static final String SUBJECT_ALTERNATIVE_NAME_OID =
Extension.subjectAlternativeName.toString();
/**
* Return true if the given OID is contained within the subtree of parent OID.
*
* @param parentOid The OID of the parent tree.
* @param oid The OID to compare.
* @return True if OID is a subtree
*/
public static boolean isSubtreeOf(String parentOid, String oid) {
String[] parentParts = parentOid.split("\\.");
String[] oidParts = oid.split("\\.");
if (parentParts.length >= oidParts.length) {
return false;
} else {
for (int i=0; i < parentParts.length; i++) {
if (!parentParts[i].equals(oidParts[i])) {
return false;
}
}
return true;
}
}
/**
* Given a Java X509Certificate object, return a list of maps representing
* all the X509 extensions embedded in the certificate. If no extensions
* exist on the certificate, then null is returned.
*
* @param cert The X509 certificate object.
* @return A list of maps describing each extensions in the provided
* certificate.
* @throws IOException
* @throws CertificateEncodingException
* @see #getExtensionList(Extensions)
*/
public static List> getExtensionList(X509Certificate cert)
throws IOException, CertificateEncodingException
{
Extensions extensions = getExtensionsFromCert(cert);
if (extensions != null) {
return getExtensionList(extensions);
} else {
return null;
}
}
/**
* Given a Java X509CRL object, return a list of maps representing
* all the X509 extensions embedded in the CRL. If no extensions
* exist on the CRL, then null is returned.
*
* @param crl The X509 CRL object.
* @return A list of maps describing each extensions in the provided CRL.
* @throws IOException
* @throws CRLException
* @see #getExtensionList(Extensions)
*/
public static List> getExtensionList(X509CRL crl)
throws IOException, CRLException
{
Extensions extensions = getExtensionsFromCRL(crl);
if (extensions != null) {
return getExtensionList(extensions);
} else {
return null;
}
}
/**
* Given a Bouncy Castle CSR object, return a list of maps representing
* all the X509 extensions embedded in the CSR. If no extensions exist on
* the CSR, then null is returned.
*
* @param csr The Bouncy Castle CertificationRequest object
* @return A list of maps describing each extensions in the provided
* certificate.
* @throws IOException
* @see #getExtensionList(Extensions)
*/
public static List> getExtensionList(PKCS10CertificationRequest csr)
throws IOException
{
Extensions extensions = getExtensionsFromCSR(csr);
if (extensions != null) {
return getExtensionList(extensions);
} else{
return null;
}
}
/**
* Given a Java certificate, get a map containing the value
* and criticality of the extensions described by the given OID. If the OID
* is not found in the certificate then null is returned.
*
* @param cert The Java X509 certificate object.
* @param oid The OID of the extension to be found.
* @return The map containing the extension value and critical flag.
* @throws IOException
* @throws CertificateEncodingException
*/
public static Map getExtension(X509Certificate cert, String oid)
throws IOException, CertificateEncodingException
{
Extensions extensions = getExtensionsFromCert(cert);
if (extensions != null) {
return makeExtensionMap(extensions, new ASN1ObjectIdentifier(oid));
} else {
return null;
}
}
/**
* Given a Java X509CRL object, get a map containing the value and
* criticality of the extensions described by the given OID. If the OID
* is not found in the CRL, then null is returned. If no extensions exist
* on the CRL, then null is returned.
*
* @param crl The X509 CRL object.
* @param oid The OID of the extension to be found.
* @return The map containing the extension value and critical flag.
* @throws IOException
* @throws CRLException
*/
public static Map getExtension(X509CRL crl, String oid)
throws IOException, CRLException
{
Extensions extensions = getExtensionsFromCRL(crl);
if (extensions != null) {
return makeExtensionMap(extensions, new ASN1ObjectIdentifier(oid));
} else {
return null;
}
}
/**
* Given a Bouncy Castle CSR, get a map describing an extension value and
* its criticality from its OID. If the extension is not found then null
* is returned.
*
* @param csr The Bouncy Castle CSR to extract an extension from.
* @param oid The OID of extension to find.
* @return A map describing the extension requested by its OID.
* @throws IOException
*/
public static Map getExtension(PKCS10CertificationRequest csr, String oid)
throws IOException
{
Extensions extensions = getExtensionsFromCSR(csr);
if (extensions != null) {
return makeExtensionMap(extensions, new ASN1ObjectIdentifier(oid));
} else {
return null;
}
}
/**
* Given a list of maps describing extensions, return a map containing
* the extensions described by the provided OID. Returns null if the OID
* doesn't exist in the provided list.
*
* @param extList A list of extensions returned by getExtensionList().
* @param oid The OID of the extension to find.
* @return The map describing the found extension, null if the oid doesn't exist.
* @see #getExtensionList(Extensions)
* @see #getExtensionList(X509Certificate)
*/
public static Map getExtension(List> extList,
String oid)
{
for (Map ext: extList) {
if (ext.get("oid").equals(oid)) {
return ext;
}
}
return null;
}
public static Object getExtensionValue(X509Certificate cert, String oid)
throws IOException, CertificateEncodingException
{
return getExtensionValue(getExtension(cert, oid));
}
public static Object getExtensionValue(X509CRL crl, String oid)
throws IOException, CRLException
{
return getExtensionValue(getExtension(crl, oid));
}
public static Object getExtensionValue(PKCS10CertificationRequest csr,
String oid)
throws IOException
{
return getExtensionValue(getExtension(csr, oid));
}
public static Object getExtensionValue(List> extList,
String oid)
{
return getExtensionValue(getExtension(extList, oid));
}
public static Object getExtensionValue(Map extMap) {
if (extMap != null) {
return extMap.get("value");
} else {
return null;
}
}
/**
* Given a Bouncy Castle Extensions container, return a list of maps
* representing all the X509 extensions embedded in the certificate.
*
* @param exts A Bouncy Castle Extensions container object.
* @return A list of maps describing each extensions in the provided
* certificate.
* @throws IOException
*/
private static List> getExtensionList(Extensions exts)
throws IOException
{
List> ret = new ArrayList<>();
for (ASN1ObjectIdentifier oid : exts.getCriticalExtensionOIDs()) {
ret.add(makeExtensionMap(exts, oid, true));
}
for (ASN1ObjectIdentifier oid : exts.getNonCriticalExtensionOIDs()) {
ret.add(makeExtensionMap(exts, oid, false));
}
return ret;
}
/**
* Given an extensions container and an OID, extract the value and
* criticality flag and return the values in a map. If the extension is not
* found then null is returned.
*
* @param exts The Bouncy Castle extensions container.
* @param oid The OID of the extension to find.
* @return A map
* @throws IOException
*/
private static Map makeExtensionMap(Extensions exts,
ASN1ObjectIdentifier oid)
throws IOException
{
boolean critical = Arrays.asList(exts.getCriticalExtensionOIDs()).contains(oid);
return makeExtensionMap(exts, oid, critical);
}
/**
* Find the X509 Extensions from CSR object. If no extensions
* attribute is found then null is returned.
*
* @param csr The CSR object to extract the Extensions container from.
* @return An extensions container extracted form the CSR.
*/
static Extensions getExtensionsFromCSR(PKCS10CertificationRequest csr) {
Attribute[] attrs = csr.getAttributes(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest);
for (Attribute attr : attrs) {
ASN1Set extsAsn1 = attr.getAttrValues();
if (extsAsn1 != null) {
ASN1Encodable extObj = extsAsn1.getObjectAt(0);
if (extObj instanceof Extensions) {
return (Extensions)extObj;
} else if (extObj instanceof ASN1Sequence || extObj instanceof ASN1Set) {
return Extensions.getInstance(extObj);
}
}
}
return null;
}
/**
* Given a list of maps which represent Extensions, produce a Bouncy Castle
* Extensions object which contains each extension parsed into Bouncy Castle
* Extension objects.
*
* @return The results Extensions container.
* @throws GeneralSecurityException
* @see #parseExtensionObject(Map)
*/
public static Extensions getExtensionsObjFromMap(List> extMapsList)
throws IOException, GeneralSecurityException {
if ((extMapsList != null) && (extMapsList.size() > 0)) {
List ret = new ArrayList<>();
for (Map extObj : extMapsList) {
ret.add(parseExtensionObject(extObj));
}
return new Extensions(ret.toArray(new Extension[ret.size()]));
} else {
return null;
}
}
/**
* Provided a map which describes an X509 extension, parse it into a
* Bouncy Castle Extension object.
*
* @param extMap Map describing an extension.
* @return A parsed Extension object.
* @throws IOException
* @throws GeneralSecurityException
*/
@SuppressWarnings("unchecked")
static Extension parseExtensionObject(Map extMap)
throws IOException, GeneralSecurityException
{
String oidString = (String)extMap.get("oid");
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(oidString);
Boolean isCritical = (Boolean) extMap.get("critical");
try {
if (oid.equals(Extension.subjectAlternativeName) ||
oid.equals(Extension.issuerAlternativeName)) {
@SuppressWarnings("unchecked")
Map> val = (Map>) extMap.get("value");
return new Extension(oid, isCritical, new DEROctetString(mapToGeneralNames(val)));
} else if (oid.equals(MiscObjectIdentifiers.netscapeCertComment)) {
DERIA5String ia5Str = new DERIA5String((String) extMap.get("value"));
return new Extension(oid, isCritical, new DEROctetString(ia5Str));
} else if (oid.equals(Extension.keyUsage)) {
Set val = (Set) extMap.get("value");
return new Extension(oid, isCritical, new DEROctetString(setToKeyUsage(val)));
} else if (oid.equals(Extension.extendedKeyUsage)) {
List list = (List) extMap.get("value");
return new Extension(oid, isCritical, new DEROctetString(listToExtendedKeyUsage(list)));
} else if (oid.equals(Extension.basicConstraints)) {
Map val = (Map) extMap.get("value");
return new Extension(oid, isCritical, new DEROctetString(mapToBasicConstraints(val)));
} else if (oid.equals(Extension.subjectKeyIdentifier)) {
PublicKey pubKey = (PublicKey) extMap.get("value");
return new Extension(oid, isCritical, new DEROctetString(publicKeyToSubjectKeyIdentifier(pubKey)));
} else if (oid.equals(Extension.authorityKeyIdentifier)) {
Map val = (Map) extMap.get("value");
return new Extension(oid, isCritical,
new DEROctetString(mapToAuthorityKeyIdentifier(val)));
} else if (oid.equals(Extension.cRLNumber)) {
BigInteger number = (BigInteger) extMap.get("value");
return new Extension(oid, false, new DEROctetString(
new CRLNumber(number)));
} else {
// If the OID isn't recognized, then just parse the value as a string
String value = (String) extMap.get("value");
return new Extension(oid, isCritical, new DEROctetString(
new DERUTF8String(value)));
}
} catch(OperatorCreationException oce) {
throw new GeneralSecurityException(oce.getLocalizedMessage());
}
}
/**
* Get a Bouncy Castle Extensions container from a Java X509 certificate
* object. If no extensions are found then null is returned.
*
* @param cert The Java X509 certificate object.
* @return A Bouncy Castle Extensions container object extracted from the
* certificate.
* @throws CertificateEncodingException
* @throws IOException
*/
private static Extensions getExtensionsFromCert(X509Certificate cert)
throws CertificateEncodingException, IOException
{
return new X509CertificateHolder(cert.getEncoded()).getExtensions();
}
/**
* Get a Bouncy Castle Extensions container from a Java X509 CRL
* object. If no extensions are found then null is returned.
*
* @param crl The Java X509 CRL object.
* @return A Bouncy Castle Extensions container object extracted from the
* CRL.
* @throws CRLException
* @throws IOException
*/
private static Extensions getExtensionsFromCRL(X509CRL crl)
throws CRLException, IOException
{
return new X509CRLHolder(crl.getEncoded()).getExtensions();
}
/**
* Given an Extensions container, an OID, and a critical flag, create a map
* of this extension's data with the following keys:
*
* - "oid" : The OID of the extensions
* - "value" : A String, or list of Strings of this OID's data
* - "critical" : A bool set to true if this extensions is critical,
* false if it isn't.
*
* If the given OID doesn't exist in the extensions container then null is
* returned.
*
* @param exts A Bouncy Castle Extensions container containing the provided OID
* @param oid The OID of the extension to create the map for.
* @param critical True if this extension is critical, false if it isn't.
* @return A map representing the extension with the given OID which exists
* in the provided Extensions container.
* @throws IOException
*/
private static Map makeExtensionMap(Extensions exts,
ASN1ObjectIdentifier oid,
boolean critical)
throws IOException
{
Extension ext = exts.getExtension(oid);
if (ext != null) {
byte[] extensionData = ext.getExtnValue().getOctets();
ASN1Object asn1Value = binaryToASN1Object(oid, extensionData);
HashMap ret = new HashMap<>();
ret.put("oid", oid.getId());
ret.put("critical", critical);
ret.put("value", asn1ObjToObj(asn1Value));
return ret;
} else {
return null;
}
}
/**
* Convert a chunk of binary data into the Bouncy Castle ASN1 data structure
* which represents the data it contains accord to its OID. I've searched
* all over the BouncyCastle API and I can't seem to find this mapping
* defined anywhere, so I've created it here.
*
* @param oid The extension OID.
* @param data The binary data value of the extension with the given OID.
* @return An ASN1Object which contains the data described by the
* provided OID.
* @throws IOException
*/
private static ASN1Object binaryToASN1Object(ASN1ObjectIdentifier oid,
byte[] data)
throws IOException
{
if (oid.equals(Extension.subjectAlternativeName) ||
oid.equals(Extension.issuerAlternativeName))
{
return GeneralNames.getInstance(data);
} else if (oid.equals(Extension.authorityKeyIdentifier)) {
return AuthorityKeyIdentifier.getInstance(data);
} else if (oid.equals(Extension.subjectKeyIdentifier)) {
return SubjectKeyIdentifier.getInstance(data);
} else if (oid.equals(Extension.basicConstraints)) {
return BasicConstraints.getInstance(data);
} else if (oid.equals(Extension.keyUsage)) {
DERBitString bs = (DERBitString) ASN1Primitive.fromByteArray(data);
return KeyUsage.getInstance(bs);
} else if (oid.equals(Extension.extendedKeyUsage)) {
return ExtendedKeyUsage.getInstance(data);
} else if (oid.equals(MiscObjectIdentifiers.netscapeCertComment)) {
try {
return ASN1Primitive.fromByteArray(data);
} catch (EOFException e) {
// Sometimes the comment field is not properly wrapped in an IA5String
return new DERIA5String(new String(data, StandardCharsets.US_ASCII));
}
} else if (oid.equals(Extension.cRLNumber)) {
return CRLNumber.getInstance(data);
} else {
try {
// If the oid is unknown, attempt to parse it as a UTF8 String
return DERUTF8String.getInstance(data);
} catch (Exception e) {
// This is required to maintain backwards compatibility with
// the erroneous method that Puppet previously used to sign
// trusted facts into the cert.
return new DERUTF8String(new String(data, StandardCharsets.US_ASCII));
}
}
}
/**
* Convert a Bouncy Castle ASN1Object into a Java data structure, which
* will generally be in the form of a string, map, list or combination thereof.
* If this method can't determine a method of converting the ASN1 object then
* the raw byte array is returned.
*
* @param asn1Prim The ASN1 object to
* @return A Java data structure which represents the provided ASN1Object.
* @throws IOException
*/
private static Object asn1ObjToObj(ASN1Encodable asn1Prim)
throws IOException
{
if (asn1Prim instanceof GeneralNames) {
return generalNamesToMap((GeneralNames) asn1Prim);
} else if (asn1Prim instanceof ASN1ObjectIdentifier) {
return ((ASN1ObjectIdentifier)asn1Prim).getId();
} else if (asn1Prim instanceof AuthorityKeyIdentifier) {
return authorityKeyIdToMap((AuthorityKeyIdentifier) asn1Prim);
} else if (asn1Prim instanceof BasicConstraints) {
return basicConstraintsToMap((BasicConstraints) asn1Prim);
} else if (asn1Prim instanceof CRLNumber) {
CRLNumber crlNumber = (CRLNumber) asn1Prim;
return crlNumber.getCRLNumber();
} else if (asn1Prim instanceof SubjectKeyIdentifier) {
SubjectKeyIdentifier ski = (SubjectKeyIdentifier) asn1Prim;
return ski.getKeyIdentifier();
} else if (asn1Prim instanceof ExtendedKeyUsage) {
return extKeyUsageToList((ExtendedKeyUsage) asn1Prim);
} else if (asn1Prim instanceof KeyPurposeId) {
KeyPurposeId kpi = (KeyPurposeId) asn1Prim;
return kpi.getId();
} else if (asn1Prim instanceof KeyUsage) {
KeyUsage ku = (KeyUsage)asn1Prim;
return keyUsageToSet(ku);
} else if (asn1Prim instanceof DERBitString) {
DERBitString bitString = (DERBitString)asn1Prim;
return bitString.getString();
} else if (asn1Prim instanceof ASN1TaggedObject) {
ASN1TaggedObject taggedObj = (ASN1TaggedObject)asn1Prim;
return asn1ObjToObj(taggedObj. parseExplicitBaseObject());
} else if (asn1Prim instanceof ASN1Sequence) {
return asn1SeqToList((ASN1Sequence) asn1Prim);
} else if (asn1Prim instanceof ASN1String) {
ASN1String str = (ASN1String)asn1Prim;
return str.getString();
} else if (asn1Prim instanceof ASN1OctetString) {
ASN1OctetString str = (ASN1OctetString) asn1Prim;
return new String(str.getOctets(), StandardCharsets.UTF_8);
} else if (asn1Prim instanceof X500Name) {
X500Name name = (X500Name) asn1Prim;
return name.toString();
} else {
// Return the raw data if there's no clear method of decoding
return asn1Prim.toASN1Primitive().getEncoded();
}
}
private static final Map keyUsageFlags =
new HashMap() {{
put("digital_signature", KeyUsage.digitalSignature);
put("non_repudiation", KeyUsage.nonRepudiation);
put("key_encipherment", KeyUsage.keyEncipherment);
put("data_encipherment", KeyUsage.dataEncipherment);
put("key_agreement", KeyUsage.keyAgreement);
put("key_cert_sign", KeyUsage.keyCertSign);
put("crl_sign", KeyUsage.cRLSign);
put("encipher_only", KeyUsage.encipherOnly);
put("decipher_only", KeyUsage.decipherOnly);
}};
private static Set keyUsageToSet(KeyUsage ku) {
Set ret = new HashSet<>();
for (String key : keyUsageFlags.keySet()) {
if (ku.hasUsages(keyUsageFlags.get(key))) {
ret.add(key);
}
}
return ret;
}
private static KeyUsage setToKeyUsage(Set flags) {
int usageBitString = 0;
for (String key: flags) {
Integer flagBit = keyUsageFlags.get(key);
if (flagBit == null) {
throw new IllegalArgumentException(
"The provided usage key does not exist: '" + key + "'");
}
usageBitString |= flagBit;
}
return new KeyUsage(usageBitString);
}
private static ExtendedKeyUsage listToExtendedKeyUsage(List oidList) {
List usages = new ArrayList<>();
for (String oid : oidList) {
usages.add(KeyPurposeId.getInstance(new ASN1ObjectIdentifier(oid)));
}
return new ExtendedKeyUsage(usages.toArray(new KeyPurposeId[usages.size()]));
}
private static List extKeyUsageToList(ExtendedKeyUsage eku)
throws IOException
{
List ret = new ArrayList<>();
for (KeyPurposeId kpid : eku.getUsages()) {
ret.add(asn1ObjToObj(kpid));
}
return ret;
}
private static Map basicConstraintsToMap(BasicConstraints bc) {
Map ret = new HashMap<>();
ret.put("is_ca", bc.isCA());
ret.put("path_len_constraint", bc.getPathLenConstraint());
return ret;
}
private static BasicConstraints mapToBasicConstraints(Map bcMap) {
Boolean isCa = (Boolean) bcMap.get("is_ca");
if (isCa == null) {
throw new IllegalArgumentException(
"The 'is_ca' key must be present in a basic constraint.");
}
BasicConstraints bc;
Integer pathLenConstraint = (Integer) bcMap.get("path_len_constraint");
if (pathLenConstraint != null) {
if (!isCa) {
throw new IllegalArgumentException(
"The 'path_len_constraint' key is not supported for " +
"an 'is_ca' value of 'false'");
}
bc = new BasicConstraints(pathLenConstraint);
}
else {
bc = new BasicConstraints(isCa);
}
return bc;
}
private static SubjectKeyIdentifier publicKeyToSubjectKeyIdentifier(PublicKey publicKey)
throws OperatorCreationException
{
SubjectPublicKeyInfo pubKeyInfo =
SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
DigestCalculator digCalc = new JcaDigestCalculatorProviderBuilder().build()
.get(new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
X509ExtensionUtils utils = new X509ExtensionUtils(digCalc);
return utils.createSubjectKeyIdentifier(pubKeyInfo);
}
private static AuthorityKeyIdentifier mapToAuthorityKeyIdentifier(
Map authKeyIdMap) throws OperatorCreationException {
AuthorityKeyIdentifier authorityKeyId = null;
PublicKey pubKey = (PublicKey) authKeyIdMap.get ("public_key");
if (pubKey != null) {
SubjectPublicKeyInfo authPubKeyInfo =
SubjectPublicKeyInfo.getInstance(
pubKey.getEncoded());
DigestCalculator digCalc =
new JcaDigestCalculatorProviderBuilder().build().get(
new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1));
X509ExtensionUtils utils = new X509ExtensionUtils(digCalc);
authorityKeyId = utils.createAuthorityKeyIdentifier(authPubKeyInfo);
}
BigInteger serialNumber = (BigInteger) authKeyIdMap.get
("serial_number");
if (pubKey == null && serialNumber == null) {
throw new IllegalArgumentException(
"Neither 'public_key' nor 'serial_number' provided for " +
"auth key identifier. At least one of these must be " +
"provided.");
}
String issuer = (String) authKeyIdMap.get ("issuer_dn");
if (issuer == null) {
if (serialNumber != null) {
throw new IllegalArgumentException(
"'issuer' not provided for auth key identifier " +
"but was expected since 'serial_number' was provided");
}
}
else {
if (serialNumber == null) {
throw new IllegalArgumentException(
"'serial_number' not provided for auth key identifier" +
"but was expected since 'issuer' was provided");
}
GeneralNames issuerAsGeneralNames =
new GeneralNames(new GeneralName(new X500Name(issuer)));
if (authorityKeyId != null) {
authorityKeyId = new AuthorityKeyIdentifier(
authorityKeyId.getKeyIdentifier(),
issuerAsGeneralNames,
serialNumber);
}
else {
authorityKeyId = new AuthorityKeyIdentifier(
issuerAsGeneralNames,
serialNumber);
}
}
return authorityKeyId;
}
private static Map authorityKeyIdToMap(AuthorityKeyIdentifier akid)
throws IOException
{
Map ret = new HashMap<>();
ret.put("issuer", generalNamesToMap(akid.getAuthorityCertIssuer()));
ret.put("serial_number", akid.getAuthorityCertSerialNumber());
ret.put("key_identifier", akid.getKeyIdentifier());
return ret;
}
/**
* Convert an ASN1 Sequence to a Java list.
*
* @param seq The ASN1 sequence to be converted.
* @return A List of parsed ASN1 objects contained in the provided sequence.
* @throws IOException
*/
private static List asn1SeqToList(ASN1Sequence seq)
throws IOException
{
List ret = new ArrayList<>();
for (int i=0; i < seq.size(); i++) {
ret.add(asn1ObjToObj(seq.getObjectAt(i)));
}
return ret;
}
/** The key name each tag number represents in a GeneralNames data structure */
private static final Map generalNameTags =
new HashMap() {{
put(0, "other_name");
put(1, "rfc822_name");
put(2, "dns_name");
put(3, "x400_address");
put(4, "directory_name");
put(5, "edi_party_name");
put(6, "uri");
put(7, "ip");
put(8, "registered_id");
}};
/**
* Given type name, return the general name tag value.
*
* @param name The GeneralName tag name defined in generalNameTags
* @return The tag number of the name, or null if the name doesn't exist.
*/
private static Integer getGnTagFromName(String name) {
for (int i=0; i < generalNameTags.size(); i++) {
if (generalNameTags.get(i).equalsIgnoreCase(name)) {
return i;
}
}
return null;
}
/**
* Convert the value of an IP address which is encoded in an
* ASN1OctetString to a string.
*
* @param ip IP address encoded in an octet string.
* @return A string representing the given IP address.
*/
public static String octetStringToIpString(ASN1OctetString ip)
throws UnknownHostException {
return InetAddress.getByAddress(ip.getOctets()).toString().split("/")[1];
}
/**
* Convert a Bouncy Castle GeneralNames object into a Java map where the key
* is the type of name defined, and the value is a list of names of that type.
*
* @param names The GeneralNames object to be parsed.
* @return A list of the names contained in each GeneralName in the
* GeneralNames data structure.
* @throws IOException
* @see GeneralName
*/
private static Map> generalNamesToMap(GeneralNames names)
throws IOException
{
if (names != null) {
Map> ret = new HashMap<>();
for (GeneralName generalName : names.getNames()) {
String type = generalNameTags.get(generalName.getTagNo());
if (ret.get(type) == null) {
ret.put(type, new ArrayList<>());
}
String name;
switch (generalName.getTagNo()) {
case GeneralName.iPAddress:
name = octetStringToIpString((ASN1OctetString)generalName.getName());
break;
default:
name = asn1ObjToObj(generalName.getName()).toString();
break;
}
ret.get(type).add(name);
}
return ret;
} else {
return null;
}
}
/**
* Convert a list of general name maps into a GeneralNames object.
*
* @param gnMap A map containing name types and a list of names.
* @return A Bouncy Castle GeneralNames object.
* @see #generalNamesToMap(GeneralNames)
*/
private static GeneralNames mapToGeneralNames(Map> gnMap) {
List ret = new ArrayList<>();
for (String type: gnMap.keySet()) {
Integer tag = getGnTagFromName(type);
if (tag == null) {
throw new IllegalArgumentException(
"Could not find a tag number for the type name '" +
type + '"');
}
for (String name: gnMap.get(type)) {
ret.add(new GeneralName(tag, name));
}
}
return new GeneralNames(ret.toArray(new GeneralName[ret.size()]));
}
}