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 com.nimbusds.jose.HeaderParameterNames;
022
023import java.io.Serializable;
024import java.security.cert.X509Certificate;
025import java.text.ParseException;
026import java.util.Objects;
027
028
029/**
030 * Enumeration of public key uses. Represents the {@code use} parameter in a
031 * JSON Web Key (JWK).
032 *
033 * <p>Public JWK use values:
034 *
035 * <ul>
036 *     <li>{@link #SIGNATURE sig}
037 *     <li>{@link #ENCRYPTION enc}
038 * </ul>
039 *
040 * @author Vladimir Dzhuvinov
041 * @version 2019-10-03
042 */
043public final class KeyUse implements Serializable {
044        
045        
046        private static final long serialVersionUID = 1L;
047
048
049        /**
050         * Signature.
051         */
052        public static final KeyUse SIGNATURE = new KeyUse("sig");
053
054
055        /**
056         * Encryption.
057         */
058        public static final KeyUse ENCRYPTION = new KeyUse(HeaderParameterNames.ENCRYPTION_ALGORITHM);
059
060
061        /**
062         * The public key use identifier.
063         */
064        private final String identifier;
065
066
067        /**
068         * Creates a new public key use with the specified identifier.
069         *
070         * @param identifier The public key use identifier. Must not be
071         *                   {@code null}.
072         */
073        public KeyUse(final String identifier) {
074
075                if (identifier == null)
076                        throw new IllegalArgumentException("The key use identifier must not be null");
077
078                this.identifier = identifier;
079        }
080
081
082        /**
083         * Returns the identifier of this public key use.
084         *
085         * @return The identifier.
086         */
087        public String identifier() {
088
089                return identifier;
090        }
091        
092        
093        /**
094         * @see #identifier()
095         */
096        public String getValue() {
097                
098                return identifier();
099        }
100
101
102        /**
103         * @see #identifier()
104         */
105        @Override
106        public String toString() {
107
108                return identifier();
109        }
110        
111        
112        @Override
113        public boolean equals(Object o) {
114                if (this == o) return true;
115                if (!(o instanceof KeyUse)) return false;
116                KeyUse keyUse = (KeyUse) o;
117                return Objects.equals(identifier, keyUse.identifier);
118        }
119        
120        
121        @Override
122        public int hashCode() {
123                return Objects.hash(identifier);
124        }
125        
126        
127        /**
128         * Parses a public key use from the specified JWK {@code use} parameter
129         * value.
130         *
131         * @param s The string to parse. May be {@code null}.
132         *
133         * @return The public key use, {@code null} if none.
134         *
135         * @throws ParseException If the string couldn't be parsed to a valid
136         *                        public key use.
137         */
138        public static KeyUse parse(final String s)
139                throws ParseException {
140
141                if (s == null) {
142                        return null;
143                }
144                
145                if (s.equals(SIGNATURE.identifier())) {
146                        return SIGNATURE;
147                }
148                
149                if (s.equals(ENCRYPTION.identifier())) {
150                        return ENCRYPTION;
151                }
152                
153                if (s.trim().isEmpty()) {
154                        throw new ParseException("JWK use value must not be empty or blank", 0);
155                }
156                
157                return new KeyUse(s);
158        }
159        
160        
161        /**
162         * Infers the public key use of the specified X.509 certificate. Note
163         * that there is no standard algorithm for mapping PKIX key usage to
164         * JWK use. See RFC 2459, section 4.2.1.3, as well as the underlying
165         * code for the chosen algorithm to infer JWK use.
166         *
167         * @param cert The X.509 certificate. Must not be {@code null}.
168         *
169         * @return The public key use, {@code null} if the key use couldn't be
170         *         reliably determined.
171         */
172        public static KeyUse from(final X509Certificate cert) {
173                
174                if (cert.getKeyUsage() == null) {
175                        return null;
176                }
177                
178                // nonRepudiation
179                if (cert.getKeyUsage()[1]) {
180                        return SIGNATURE;
181                }
182                
183                // digitalSignature && keyEncipherment
184                // (e.g. RSA TLS certificate for authenticated encryption)
185                if (cert.getKeyUsage()[0] && cert.getKeyUsage()[2]) {
186                        return KeyUse.ENCRYPTION;
187                }
188                
189                // digitalSignature && keyAgreement
190                // (e.g. EC TLS certificate for authenticated encryption)
191                if (cert.getKeyUsage()[0] && cert.getKeyUsage()[4]) {
192                        return KeyUse.ENCRYPTION;
193                }
194                
195                // keyEncipherment || dataEncipherment || keyAgreement
196                if (cert.getKeyUsage()[2] || cert.getKeyUsage()[3] || cert.getKeyUsage()[4]) {
197                        return ENCRYPTION;
198                }
199                
200                // keyCertSign || cRLSign
201                if (cert.getKeyUsage()[5] || cert.getKeyUsage()[6]) {
202                        return SIGNATURE;
203                }
204                
205                return null;
206        }
207}