001/* 002 * Copyright 2017-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2017-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.util.ssl.cert; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.LinkedHashSet; 029import java.util.List; 030import java.util.Set; 031 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1ObjectIdentifier; 034import com.unboundid.asn1.ASN1Sequence; 035import com.unboundid.util.Debug; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.OID; 038import com.unboundid.util.StaticUtils; 039import com.unboundid.util.ThreadSafety; 040import com.unboundid.util.ThreadSafetyLevel; 041 042import static com.unboundid.util.ssl.cert.CertMessages.*; 043 044 045 046/** 047 * This class provides an implementation of the extended key usage X.509 048 * certificate extension as described in 049 * <A HREF="https://www.ietf.org/rfc/rfc5280.txt">RFC 5280</A> section 4.2.1.12. 050 * This can be used to provide an extensible list of OIDs that identify ways 051 * that a certificate is intended to be used. 052 * <BR><BR> 053 * The OID for this extension is 2.5.29.37 and the value has the following 054 * encoding: 055 * <PRE> 056 * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId 057 * 058 * KeyPurposeId ::= OBJECT IDENTIFIER 059 * </PRE> 060 * 061 * @see ExtendedKeyUsageID 062 */ 063@NotMutable() 064@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 065public final class ExtendedKeyUsageExtension 066 extends X509CertificateExtension 067{ 068 /** 069 * The OID (2.5.29.37) for extended key usage extensions. 070 */ 071 public static final OID EXTENDED_KEY_USAGE_OID = new OID("2.5.29.37"); 072 073 074 075 /** 076 * The serial version UID for this serializable class. 077 */ 078 private static final long serialVersionUID = -8208115548961483723L; 079 080 081 082 // The set of key purpose IDs included in this extension. 083 private final Set<OID> keyPurposeIDs; 084 085 086 087 /** 088 * Creates a new extended key usage extension with the provided set of key 089 * purpose IDs. 090 * 091 * @param isCritical Indicates whether this extension should be 092 * considered critical. 093 * @param keyPurposeIDs The set of key purpose IDs included in this 094 * extension. It must not be {@code null}. 095 * 096 * @throws CertException If a problem is encountered while encoding the 097 * value for this extension. 098 */ 099 ExtendedKeyUsageExtension(final boolean isCritical, 100 final List<OID> keyPurposeIDs) 101 throws CertException 102 { 103 super(EXTENDED_KEY_USAGE_OID, isCritical, encodeValue(keyPurposeIDs)); 104 105 this.keyPurposeIDs = 106 Collections.unmodifiableSet(new LinkedHashSet<>(keyPurposeIDs)); 107 } 108 109 110 111 /** 112 * Creates a new extended key usage extension from the provided generic 113 * extension. 114 * 115 * @param extension The extension to decode as an extended key usage 116 * extension. 117 * 118 * @throws CertException If the provided extension cannot be decoded as an 119 * extended key usage extension. 120 */ 121 ExtendedKeyUsageExtension(final X509CertificateExtension extension) 122 throws CertException 123 { 124 super(extension); 125 126 try 127 { 128 final ASN1Element[] elements = 129 ASN1Sequence.decodeAsSequence(extension.getValue()).elements(); 130 final LinkedHashSet<OID> ids = new LinkedHashSet<>(elements.length); 131 for (final ASN1Element e : elements) 132 { 133 ids.add(e.decodeAsObjectIdentifier().getOID()); 134 } 135 136 keyPurposeIDs = Collections.unmodifiableSet(ids); 137 } 138 catch (final Exception e) 139 { 140 Debug.debugException(e); 141 throw new CertException( 142 ERR_EXTENDED_KEY_USAGE_EXTENSION_CANNOT_PARSE.get( 143 String.valueOf(extension), StaticUtils.getExceptionMessage(e)), 144 e); 145 } 146 } 147 148 149 150 /** 151 * Encodes the provided information for use as the value of this extension. 152 * 153 * @param keyPurposeIDs The set of key purpose IDs included in this 154 * extension. It must not be {@code null}. 155 * 156 * @return The encoded value for this extension. 157 * 158 * @throws CertException If a problem is encountered while encoding the 159 * value. 160 */ 161 private static byte[] encodeValue(final List<OID> keyPurposeIDs) 162 throws CertException 163 { 164 try 165 { 166 final ArrayList<ASN1Element> elements = 167 new ArrayList<>(keyPurposeIDs.size()); 168 for (final OID oid : keyPurposeIDs) 169 { 170 elements.add(new ASN1ObjectIdentifier(oid)); 171 } 172 173 return new ASN1Sequence(elements).encode(); 174 } 175 catch (final Exception e) 176 { 177 Debug.debugException(e); 178 throw new CertException( 179 ERR_EXTENDED_KEY_USAGE_EXTENSION_CANNOT_ENCODE.get( 180 String.valueOf(keyPurposeIDs), 181 StaticUtils.getExceptionMessage(e)), 182 e); 183 } 184 } 185 186 187 188 /** 189 * Retrieves the OIDs of the key purpose values contained in this extension. 190 * Some, all, or none of the OIDs contained in this extension may correspond 191 * to values in the {@link ExtendedKeyUsageID} enumeration. 192 * 193 * @return The OIDs of the key purpose values contained in this extension. 194 */ 195 public Set<OID> getKeyPurposeIDs() 196 { 197 return keyPurposeIDs; 198 } 199 200 201 202 /** 203 * {@inheritDoc} 204 */ 205 @Override() 206 public String getExtensionName() 207 { 208 return INFO_EXTENDED_KEY_USAGE_EXTENSION_NAME.get(); 209 } 210 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override() 217 public void toString(final StringBuilder buffer) 218 { 219 buffer.append("ExtendedKeyUsageExtension(oid='"); 220 buffer.append(getOID()); 221 buffer.append("', isCritical="); 222 buffer.append(isCritical()); 223 buffer.append(", keyPurposeIDs={"); 224 225 final Iterator<OID> oidIterator = keyPurposeIDs.iterator(); 226 while (oidIterator.hasNext()) 227 { 228 buffer.append('\''); 229 buffer.append(ExtendedKeyUsageID.getNameOrOID(oidIterator.next())); 230 buffer.append('\''); 231 232 if (oidIterator.hasNext()) 233 { 234 buffer.append(", "); 235 } 236 } 237 238 buffer.append("})"); 239 } 240}