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