001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.security;
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.util.Collections;
024import java.util.LinkedHashMap;
025import java.util.Map;
026import java.util.Properties;
027
028/**
029 * Defines the primary usage of the key contained in the key block.
030 * <p>
031 * Each value repesents bytes 5-6 of the Keyblok Header.
032 * <p>
033 * This class defines proprietary specific key usages. In the great majority of
034 * cases, the ones defined by TR-31 {@link KeyUsage} will be sufficient. There
035 * are no strong reasons for separating e.g. KEK keys to ZMK and TMK, or PINENC
036 * keys to ZPK and TPK. KEK and PINENC should be enough.
037 * <p>
038 * However, when it is necessary to use, for example: private/public RSA keys
039 * or HMAC keys, the only option is to use the proprietary key usages.
040 * <p>
041 * The proprietary key usages for {@code ExtKeyUsage} are optional and can be
042 * defined in your resources at {@value ExtKeyUsage#EXTERNAL_KEY_USAGES}. A file
043 * with the same name in the jPOS test resources can be used as an example.
044 */
045public class ExtKeyUsage extends KeyUsage {
046
047    /**
048     * Key usages by code key.
049     * <p>
050     * This field has to be first
051     */
052    private static final Map<String, KeyUsage> MAP = new LinkedHashMap<>();
053
054    private static final String EXTERNAL_KEY_USAGES = "META-INF/org/jpos/security/proprietary-hsm.properties";
055
056    private static final String KEY_USAGE_PREFIX    = "ku.";
057
058    /**
059     * Key usages by entry key.
060     * <p>
061     * This field has to be after {@link MAP} and before key usages;
062     */
063    private static final Map<String, KeyUsage> EXT_DEF = loadKeyUsagesFromClasspath(EXTERNAL_KEY_USAGES);
064
065    /**
066     * DEK - Data Encryption Key.
067     *
068     * @apiNote It is proprietary specific version of {@link KeyUsage#ENC}
069     */
070    public final static KeyUsage DEK        = getKeyUsage("DEK");
071
072    /**
073     * ZEK - Zone Encryption Key.
074     *
075     * @apiNote It is proprietary specific version of {@link KeyUsage#ENC}
076     */
077    public final static KeyUsage ZEK        = getKeyUsage("ZEK");
078
079    /**
080     * TEK - Terminal Encryption Key.
081     *
082     * @apiNote It is proprietary specific version of {@link KeyUsage#ENC}
083     */
084    public final static KeyUsage TEK        = getKeyUsage("TEK");
085
086    /**
087     * RSA Public Key.
088     *
089     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
090     */
091    public final static KeyUsage RSAPK      = getKeyUsage("RSAPK");
092
093    /**
094     * RSA Private Key for signing or key management.
095     *
096     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
097     */
098    public final static KeyUsage RSASK      = getKeyUsage("RSASK");
099
100    /**
101     * RSA Private Key for ICC personalization.
102     *
103     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
104     */
105    public final static KeyUsage RSASKICC   = getKeyUsage("RSASKICC");
106
107    /**
108     * RSA Private Key for PIN translation.
109     *
110     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
111     */
112    public final static KeyUsage RSASKPIN   = getKeyUsage("RSASKPIN");
113
114    /**
115     * RSA Private Key for TLS.
116     *
117     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
118     */
119    public final static KeyUsage RSASKTLS   = getKeyUsage("RSASKTLS");
120
121    /**
122     * TMK - Terminal Master Key.
123     *
124     * @apiNote It is proprietary specific version of {@link KeyUsage#KEK}
125     */
126    public final static KeyUsage TMK        = getKeyUsage("TMK");
127
128    /**
129     * ZMK - Zone Master Key.
130     *
131     * @apiNote It is proprietary specific version of {@link KeyUsage#KEK}
132     */
133    public final static KeyUsage ZMK        = getKeyUsage("ZMK");
134
135    /**
136     * HMAC key using SHA-1.
137     *
138     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
139     */
140    public final static KeyUsage HMACSHA1   = getKeyUsage("HMACSHA1");
141
142    /**
143     * HMAC key using SHA-224.
144     *
145     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
146     */
147    public final static KeyUsage HMACSHA224 = getKeyUsage("HMACSHA224");
148
149    /**
150     * HMAC key using SHA-256.
151     *
152     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
153     */
154    public final static KeyUsage HMACSHA256 = getKeyUsage("HMACSHA256");
155
156    /**
157     * HMAC key using SHA-384.
158     *
159     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
160     */
161    public final static KeyUsage HMACSHA384 = getKeyUsage("HMACSHA384");
162
163    /**
164     * HMAC key using SHA-512.
165     *
166     * @apiNote It is proprietary specific, there is no equivalent in TR-31.
167     */
168    public final static KeyUsage HMACSHA512 = getKeyUsage("HMACSHA512");
169
170    /**
171     * TPK - Terminal PIN Encryption Key.
172     *
173     * @apiNote It is proprietary specific version of {@link KeyUsage#PINENC}
174     */
175    public final static KeyUsage TPK        = getKeyUsage("TPK");
176
177    /**
178     * ZPK - Zone PIN Encryption Key.
179     *
180     * @apiNote It is proprietary specific version of {@link KeyUsage#PINENC}
181     */
182    public final static KeyUsage ZPK        = getKeyUsage("ZPK");
183
184
185    /**
186     * Internal constructor.
187     * <p>
188     * The constructor is protected to guarantee only one instance of the key
189     * usage in the entire JVM. This makes it possible to use the operator
190     * {@code ==} or {@code !=} as it does for enums.
191     *
192     * @param code the key usage code
193     * @param name the usage name
194     */
195    protected ExtKeyUsage(String code, String name) {
196        super(code, name);
197    }
198
199    private static KeyUsage getKeyUsage(String entry) {
200        if (EXT_DEF == null)
201            return null;
202
203        return EXT_DEF.get(entry);
204    }
205
206    /**
207     * Returns the enum constant of this type with the specified {@code code}.
208     *
209     * @param code the TR-31 key usage code to look up
210     * @return the enum constant with the specified processing code or
211     *         {@code null} if unknown.
212     */
213    public static KeyUsage valueOfByCode(String code) {
214        KeyUsage ku = MAP.get(code);
215        if (ku != null)
216            return ku;
217
218        return TR31MAP.get(code);
219    }
220
221    /**
222     * Returns an unmodifiable map of all KeyUsage entries (TR-31 and extended) keyed by code.
223     * @return unmodifiable map of code to KeyUsage
224     */
225    public static Map<String, KeyUsage> entries() {
226        Map ret = new LinkedHashMap(TR31MAP);
227        ret.putAll(MAP);
228        return Collections.unmodifiableMap(ret);
229    }
230
231    private static InputStream loadResourceAsStream(String name) {
232        InputStream in = null;
233
234        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
235        if (contextClassLoader != null)
236            in = contextClassLoader.getResourceAsStream(name);
237
238        if (in == null)
239            in = ExtKeyUsage.class.getClassLoader().getResourceAsStream(name);
240
241        return in;
242    }
243
244    static Map<String, KeyUsage> loadKeyUsagesFromClasspath(String resource) {
245        Properties props = new Properties();
246        try (InputStream in = loadResourceAsStream(resource)) {
247            props.load(in);
248            return registerKeyUsages(props);
249        } catch (IOException | NullPointerException ex) {
250            return null;
251        }
252    }
253
254    private static Map<String, KeyUsage> registerKeyUsages(Properties props) {
255        Map<String, KeyUsage> ret = new LinkedHashMap<>();
256        for (String key : props.stringPropertyNames()) {
257            if (!key.startsWith(KEY_USAGE_PREFIX))
258                continue;
259
260            String value = props.getProperty(key);
261            if (value == null || value.isEmpty())
262                continue;
263
264            String k = key.substring(KEY_USAGE_PREFIX.length());
265            String[] entry = value.split(",");
266            KeyUsage ku = new KeyUsage(entry[0], entry[1]);
267            // Override is disabled
268            if (MAP.containsKey(ku.getCode()))
269                continue;
270
271            ret.put(k, ku);
272            MAP.put(ku.getCode(), ku);
273        }
274
275        return ret;
276    }
277
278}