org.wildfly.security.x500.cert.CertUtil Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.security.x500.cert;
import static org.wildfly.security.x500.cert._private.ElytronMessages.log;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.wildfly.common.iteration.CodePointIterator;
import org.wildfly.security.x500.GeneralName;
import org.wildfly.security.x500.X500;
/**
* A utility class with common methods used for generating certificate signing requests and self-signed certificates.
*
* @author Farah Juma
* @since 1.2.0
*/
class CertUtil {
private static final String BASIC_CONSTRAINTS = "BasicConstraints";
private static final String KEY_USAGE = "KeyUsage";
private static final String CE_EXT_KEY_USAGE = "ExtendedKeyUsage";
private static final String CE_SUBJECT_ALT_NAME = "SubjectAlternativeName";
private static final String CE_ISSUER_ALT_NAME = "IssuerAlternativeName";
private static final String PE_AUTHORITY_INFO_ACCESS = "AuthorityInfoAccess";
private static final String PE_SUBJECT_INFO_ACCESS = "SubjectInfoAccess";
private static final String CA = "ca";
private static final String PATH_LEN = "pathlen";
private static final String KP_SERVER_AUTH = "serverAuth";
private static final String KP_CLIENT_AUTH = "clientAuth";
private static final String KP_CODE_SIGNING = "codeSigning";
private static final String KP_EMAIL_PROTECTION = "emailProtection";
private static final String KP_TIME_STAMPING = "timeStamping";
private static final String KP_OCSP_SIGNING = "OCSPSigning";
private static final String AD_OCSP = "ocsp";
private static final String AD_CA_ISSUERS = "caIssuers";
private static final String AD_TIME_STAMPING = "timeStamping";
private static final String AD_CA_REPOSITORY = "caRepository";
private static final String EMAIL = "EMAIL";
private static final String URI = "URI";
private static final String DNS = "DNS";
private static final String IP = "IP";
private static final String OID = "OID";
private static final String[] ALT_NAMES_TYPES = new String[] { EMAIL, URI, DNS, IP, OID };
private static final int[] DELIMS = new int[] {',', ' '};
/**
* Create an {@code X509CertificateExtension} using the given extension name and string value.
*
* @param critical whether the extension should be marked as critical
* @param extensionName the extension name
* @param extensionValue the extension value, as a string
* @return the newly created {@code X509CertificateExtension}
* @throws IllegalArgumentException if the given extension name or value is invalid or if creating an {@code X509CertificateExtension}
* from a string value is not supported for the given extension name
*/
public static X509CertificateExtension getX509CertificateExtension(final boolean critical, final String extensionName, final String extensionValue) throws IllegalArgumentException {
final X509CertificateExtension extension;
try {
if (extensionName.equalsIgnoreCase(BASIC_CONSTRAINTS)) {
// ca:{true|false}[,pathlen:]
final CodePointIterator cpi = CodePointIterator.ofString(extensionValue);
final CodePointIterator di = cpi.delimitedBy(DELIMS);
final boolean ca = Boolean.parseBoolean(getKeyValue(CA, di.drainToString()));
skipDelims(di, cpi, DELIMS);
int pathLen = -1;
if (di.hasNext()) {
pathLen = Integer.parseInt(getKeyValue(PATH_LEN, di.drainToString()));
}
extension = new BasicConstraintsExtension(critical, ca, pathLen);
} else if (extensionName.equalsIgnoreCase(KEY_USAGE)) {
// usage(,usage)*
final CodePointIterator cpi = CodePointIterator.ofString(extensionValue);
final CodePointIterator di = cpi.delimitedBy(DELIMS);
if (! di.hasNext()) {
throw log.invalidCertificateExtensionStringValue(extensionValue);
}
final List keyUsages = new ArrayList<>();
while (di.hasNext()) {
final KeyUsage keyUsage = KeyUsage.forName(di.drainToString());
if (keyUsage == null) {
throw log.invalidCertificateExtensionStringValue(extensionValue);
}
keyUsages.add(keyUsage);
skipDelims(di, cpi, DELIMS);
}
extension = new KeyUsageExtension(critical, keyUsages.toArray(new KeyUsage[keyUsages.size()]));
} else if (extensionName.equalsIgnoreCase(CE_EXT_KEY_USAGE)) {
// usage(,usage)*
final CodePointIterator cpi = CodePointIterator.ofString(extensionValue);
final CodePointIterator di = cpi.delimitedBy(DELIMS);
if (! di.hasNext()) {
throw log.invalidCertificateExtensionStringValue(extensionValue);
}
final List keyPurposeIds = new ArrayList<>();
while (di.hasNext()) {
final String keyPurposeId = oidFromKeyPurpose(di.drainToString());
keyPurposeIds.add(keyPurposeId);
skipDelims(di, cpi, DELIMS);
}
extension = new ExtendedKeyUsageExtension(critical, keyPurposeIds);
} else if (extensionName.equalsIgnoreCase(CE_SUBJECT_ALT_NAME)) {
extension = new SubjectAlternativeNamesExtension(critical, getGeneralNames(extensionValue));
} else if (extensionName.equalsIgnoreCase(CE_ISSUER_ALT_NAME)) {
extension = new IssuerAlternativeNamesExtension(critical, getGeneralNames(extensionValue));
} else if (extensionName.equalsIgnoreCase(PE_AUTHORITY_INFO_ACCESS)) {
if (critical) {
throw log.certificateExtensionMustBeNonCritical(extensionName);
}
extension = new AuthorityInformationAccessExtension(getAccessDescriptions(extensionValue));
} else if (extensionName.equalsIgnoreCase(PE_SUBJECT_INFO_ACCESS)) {
if (critical) {
throw log.certificateExtensionMustBeNonCritical(extensionName);
}
extension = new SubjectInformationAccessExtension(getAccessDescriptions(extensionValue));
} else {
throw log.certificateExtensionCreationFromStringNotSupported(extensionName);
}
} catch (Exception e) {
throw log.certificateExtensionCreationFromStringFailed(e);
}
return extension;
}
private static void skipDelims(CodePointIterator di, CodePointIterator cpi, int...delims) throws IllegalArgumentException {
while ((! di.hasNext()) && cpi.hasNext()) {
if (! isDelim(cpi.next(), delims)) {
throw log.invalidCertificateExtensionStringValue();
}
}
}
private static boolean isDelim(int c, int... delims) {
for (int delim : delims) {
if (delim == c) {
return true;
}
}
return false;
}
private static String getKeyValue(final String requiredKey, final String keyAndValue) throws IllegalArgumentException {
// key:value
final CodePointIterator cpi = CodePointIterator.ofString(keyAndValue);
final CodePointIterator di = cpi.delimitedBy(':');
if (! requiredKey.equalsIgnoreCase(di.drainToString())) {
throw log.invalidCertificateExtensionStringValue(keyAndValue);
}
skipDelims(di, cpi, ':');
return di.drainToString();
}
private static String oidFromKeyPurpose(final String keyPurpose) {
switch (keyPurpose) {
case KP_SERVER_AUTH: return X500.OID_KP_SERVER_AUTH;
case KP_CLIENT_AUTH: return X500.OID_KP_CLIENT_AUTH;
case KP_CODE_SIGNING: return X500.OID_KP_CODE_SIGNING;
case KP_EMAIL_PROTECTION: return X500.OID_KP_EMAIL_PROTECTION;
case KP_TIME_STAMPING: return X500.OID_KP_TIME_STAMPING;
case KP_OCSP_SIGNING: return X500.OID_KP_OCSP_SIGNING;
default: return keyPurpose; // must be an oid already
}
}
private static List getGeneralNames(final String extensionValue) throws IllegalArgumentException {
// type:val(,type:val)*
final CodePointIterator cpi = CodePointIterator.ofString(extensionValue);
final CodePointIterator di = cpi.delimitedBy(DELIMS);
if (! di.hasNext()) {
throw log.invalidCertificateExtensionStringValue(extensionValue);
}
List generalNames = new ArrayList<>();
while (di.hasNext()) {
generalNames.add(getGeneralName(di.drainToString()));
skipDelims(di, cpi, DELIMS);
}
return generalNames;
}
private static GeneralName getGeneralName(final String typeAndValue) throws IllegalArgumentException {
// type:val
final CodePointIterator cpi = CodePointIterator.ofString(typeAndValue);
final CodePointIterator di = cpi.delimitedBy(':');
final String type = di.drainToString();
for (String requiredType : ALT_NAMES_TYPES) {
if (requiredType.equalsIgnoreCase(type)) {
skipDelims(di, cpi, ':');
final String value = cpi.drainToString();
switch (type.toUpperCase(Locale.ENGLISH)) {
case EMAIL:
return new GeneralName.RFC822Name(value);
case URI:
return new GeneralName.URIName(value);
case DNS:
return new GeneralName.DNSName(value);
case IP:
return new GeneralName.IPAddress(value);
case OID:
return new GeneralName.RegisteredID(value);
default:
throw log.invalidCertificateExtensionStringValue(typeAndValue);
}
}
}
throw log.invalidCertificateExtensionStringValue(typeAndValue);
}
private static List getAccessDescriptions(final String extensionValue) throws IllegalArgumentException {
// method:location-type:location-value(,method:location-type:location-value)*
final CodePointIterator cpi = CodePointIterator.ofString(extensionValue);
final CodePointIterator di = cpi.delimitedBy(DELIMS);
if (! di.hasNext()) {
throw log.invalidCertificateExtensionStringValue(extensionValue);
}
List accessDescriptions = new ArrayList<>();
while (di.hasNext()) {
accessDescriptions.add(getAccessDescription(di.drainToString()));
skipDelims(di, cpi, DELIMS);
}
return accessDescriptions;
}
private static AccessDescription getAccessDescription(final String methodAndTypeAndValue) throws IllegalArgumentException {
// method:location-type:location-value
final CodePointIterator cpi = CodePointIterator.ofString(methodAndTypeAndValue);
final CodePointIterator di = cpi.delimitedBy(':');
if (! di.hasNext()) {
throw log.invalidCertificateExtensionStringValue(methodAndTypeAndValue);
}
final String accessMethodId = oidFromMethod(di.drainToString());
skipDelims(di, cpi, ':');
final String typeAndValue = cpi.drainToString();
final GeneralName accessLocation = getGeneralName(typeAndValue);
return new AccessDescription(accessMethodId, accessLocation);
}
private static String oidFromMethod(final String method) {
switch (method) {
case AD_OCSP: return X500.OID_AD_OCSP;
case AD_CA_ISSUERS: return X500.OID_AD_CA_ISSUERS;
case AD_TIME_STAMPING: return X500.OID_AD_TIME_STAMPING;
case AD_CA_REPOSITORY: return X500.OID_AD_CA_REPOSITORY;
default: return method; // must be an oid already
}
}
}