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}