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