001/* 002 * nimbus-jose-jwt 003 * 004 * Copyright 2012-2016, Connect2id Ltd. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.jose.jwk; 019 020 021import java.io.Serializable; 022import java.security.cert.X509Certificate; 023import java.text.ParseException; 024import java.util.HashSet; 025import java.util.Objects; 026import java.util.Set; 027 028import com.nimbusds.jose.HeaderParameterNames; 029 030 031/** 032 * Enumeration of public key uses. Represents the {@code use} parameter in a 033 * JSON Web Key (JWK). 034 * 035 * <p>Public JWK use values: 036 * 037 * <ul> 038 * <li>{@link #SIGNATURE sig} 039 * <li>{@link #ENCRYPTION enc} 040 * </ul> 041 * 042 * @author Vladimir Dzhuvinov 043 * @version 2019-10-03 044 */ 045public final class KeyUse implements Serializable { 046 047 048 private static final long serialVersionUID = 1L; 049 050 051 /** 052 * Signature. 053 */ 054 public static final KeyUse SIGNATURE = new KeyUse("sig"); 055 056 057 /** 058 * Encryption. 059 */ 060 public static final KeyUse ENCRYPTION = new KeyUse(HeaderParameterNames.ENCRYPTION_ALGORITHM); 061 062 063 /** 064 * The public key use identifier. 065 */ 066 private final String identifier; 067 068 069 /** 070 * Creates a new public key use with the specified identifier. 071 * 072 * @param identifier The public key use identifier. Must not be 073 * {@code null}. 074 */ 075 public KeyUse(final String identifier) { 076 077 if (identifier == null) 078 throw new IllegalArgumentException("The key use identifier must not be null"); 079 080 this.identifier = identifier; 081 } 082 083 084 /** 085 * Returns the identifier of this public key use. 086 * 087 * @return The identifier. 088 */ 089 public String identifier() { 090 091 return identifier; 092 } 093 094 095 /** 096 * @see #identifier() 097 */ 098 public String getValue() { 099 100 return identifier(); 101 } 102 103 104 /** 105 * @see #identifier() 106 */ 107 @Override 108 public String toString() { 109 110 return identifier(); 111 } 112 113 114 @Override 115 public boolean equals(Object o) { 116 if (this == o) return true; 117 if (!(o instanceof KeyUse)) return false; 118 KeyUse keyUse = (KeyUse) o; 119 return Objects.equals(identifier, keyUse.identifier); 120 } 121 122 123 @Override 124 public int hashCode() { 125 return Objects.hash(identifier); 126 } 127 128 129 /** 130 * Parses a public key use from the specified JWK {@code use} parameter 131 * value. 132 * 133 * @param s The string to parse. May be {@code null}. 134 * 135 * @return The public key use, {@code null} if none. 136 * 137 * @throws ParseException If the string couldn't be parsed to a valid 138 * public key use. 139 */ 140 public static KeyUse parse(final String s) 141 throws ParseException { 142 143 if (s == null) { 144 return null; 145 } 146 147 if (s.equals(SIGNATURE.identifier())) { 148 return SIGNATURE; 149 } 150 151 if (s.equals(ENCRYPTION.identifier())) { 152 return ENCRYPTION; 153 } 154 155 if (s.trim().isEmpty()) { 156 throw new ParseException("JWK use value must not be empty or blank", 0); 157 } 158 159 return new KeyUse(s); 160 } 161 162 163 /** 164 * Infers the use of the public key in the specified X.509 certificate. 165 * Note that there is no standard algorithm for mapping PKIX key usage 166 * to JWK use. See RFC 2459, section 4.2.1.3, as well as the underlying 167 * code for the chosen algorithm to infer JWK use. 168 * 169 * @param cert The X.509 certificate. Must not be {@code null}. 170 * 171 * @return The inferred public key use, {@code null} if the use of the 172 * public key is not specified by the X.509 certificate or the 173 * use maps to both {@link KeyUse#SIGNATURE} and 174 * {@link KeyUse#ENCRYPTION}. 175 */ 176 public static KeyUse from(final X509Certificate cert) { 177 178 if (cert.getKeyUsage() == null) { 179 return null; 180 } 181 182 Set<KeyUse> foundUses = new HashSet<>(); 183 184 // https://datatracker.ietf.org/doc/html/rfc2459#section-4.2.1.3 185 186 // digitalSignature || nonRepudiation 187 if (cert.getKeyUsage()[0] || cert.getKeyUsage()[1]) { 188 foundUses.add(SIGNATURE); 189 } 190 191 // digitalSignature && keyEncipherment 192 // (e.g. RSA TLS certificate for authenticated encryption) 193 if (cert.getKeyUsage()[0] && cert.getKeyUsage()[2]) { 194 foundUses.add(KeyUse.ENCRYPTION); 195 } 196 197 // digitalSignature && keyAgreement 198 // (e.g. EC TLS certificate for authenticated encryption) 199 if (cert.getKeyUsage()[0] && cert.getKeyUsage()[4]) { 200 foundUses.add(KeyUse.ENCRYPTION); 201 } 202 203 // keyEncipherment || dataEncipherment || keyAgreement 204 if (cert.getKeyUsage()[2] || cert.getKeyUsage()[3] || cert.getKeyUsage()[4]) { 205 foundUses.add(ENCRYPTION); 206 } 207 208 // keyCertSign || cRLSign 209 if (cert.getKeyUsage()[5] || cert.getKeyUsage()[6]) { 210 foundUses.add(SIGNATURE); 211 } 212 213 if (foundUses.size() == 1) { 214 return foundUses.iterator().next(); 215 } else { 216 // Cannot map cert usage to singular JWK use value 217 return null; 218 } 219 } 220}