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.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.PrintStream;
024import org.jpos.util.Loggeable;
025
026import java.io.Serializable;
027import java.util.LinkedHashMap;
028import java.util.Map;
029import java.util.Map.Entry;
030import org.jpos.iso.ISOUtil;
031
032/**
033 * This class contains a set of desirable key properties that can be passed to
034 * an HSM device for example to generate a key or import it.
035 * <p>
036 * This class is not intended to use for key storage. It can contain
037 * confidentional data like key length. That is why they should not be kept
038 * persistently anywhere.
039 *
040 * @author Robert Demski
041 */
042public class SecureKeySpec
043        implements Serializable, Loggeable {
044
045    private static final long serialVersionUID = -3145281298749096305L;
046
047    /**
048     * Key scheme indicates protection metchod appiled to this key by
049     * a security module.
050     */
051    protected KeyScheme scheme;
052
053    /**
054     * The key length is expressed in bits and refers to clear key <i>(before
055     * LMK protection)</i>.
056     */
057    protected int keyLength;
058
059    /**
060     * Key Type is useful for stating what this key can be used for.
061     * <p>
062     * The value of Key Type specifies whether this encryped key is a
063     * <ul>
064     *   <li>{@link SMAdapter#TYPE_TMK} Terminal Master Key
065     *   <li>{@link SMAdapter#TYPE_ZPK} Zone PIN Key
066     *   <li>or others
067     * </ul>
068     */
069    protected String keyType;
070
071    /**
072     * Indicates key protection variant metchod appiled to this key by a security module.
073     */
074    protected int variant;
075
076    /**
077     * Secure Key Bytes.
078     */
079    protected byte[] keyBytes = null;
080
081    /**
082     * The keyCheckValue allows identifying which clear key does this
083     * secure key represent.
084     */
085    protected byte[] keyCheckValue;
086
087    /**
088     * Identifies the method by which the key block is cryptographically
089     * protected and the content layout of the block.
090     */
091    protected char keyBlockVersion;
092
093    /**
094     * The primary usage of the key contained in the key block.
095     */
096    protected KeyUsage keyUsage;
097
098    /**
099     * The cryptographic algorithm with which the key contained in key block
100     * will be used.
101     */
102    protected Algorithm algorithm;
103
104    /**
105     * The operation that the key contained in the key block can perform.
106     */
107    protected ModeOfUse modeOfUse;
108
109    /**
110     * Version number to optionally indicate that the contents of the key block
111     * is a component (key part), or to prevent re-injection of an old key.
112     */
113    protected String keyVersion;
114
115    /**
116     * The conditions under which the key can be exported outside the
117     * cryptographic domain.
118     */
119    protected Exportability exportability;
120
121    /**
122     * This element is not specified by TR-31 (should contain two ASCII zeros).
123     * <p>
124     * In proprietary derivatives can be used as e.g: LMK identifier.
125     */
126    protected String reserved;
127
128    /**
129     * The TR-31 Key Block format allows a key block to contain up to 99
130     * Optional Header Blocks which can be used to include additional (optional)
131     * data within the Key Block.
132     */
133    protected final Map<String, String> optionalHeaders = new LinkedHashMap<>();
134
135    /**
136     * The key block MAC ensures the integrity of the key block, and is
137     * calculated over the Header, Optional Header Blocks and the encrypted Key
138     * Data.
139     */
140    protected byte[] keyBlockMAC;
141
142    /**
143     * Optional key name.
144     */
145    protected String keyName;
146
147    public SecureKeySpec() {
148        super();
149    }
150
151    /**
152     * Key scheme indicates protection metchod appiled to this key by
153     * the security module.
154     *
155     * @param scheme key scheme used to protect this key.
156     */
157    public void setScheme(KeyScheme scheme) {
158        this.scheme = scheme;
159    }
160
161    /**
162     * Gets the key scheme used to protect this key.
163     *
164     * @return key scheme used to protect this key.
165     */
166    public KeyScheme getScheme() {
167        return scheme;
168    }
169
170    /**
171     * Sets the length of the key.
172     * <p>
173     * The key length is expressed in bits and refers to clear key <i>(before
174     * LMK protection)</i>
175     * This might be different than the bit length of the secureKeyBytes.
176     *
177     * @param keyLength
178     */
179    public void setKeyLength(int keyLength) {
180        this.keyLength = keyLength;
181    }
182
183    /**
184     * Gets the length of the key.
185     * <p>
186     * The key length is expressed in bits and refers to clear key <i>(before
187     * LMK protection)</i>
188     *
189     * @return The length of the clear key
190     */
191    public int getKeyLength() {
192        return keyLength;
193    }
194
195    /**
196     * Key Type is useful for stating what this key can be used for.
197     * <p>
198     * The value of Key Type specifies whether this secure key is a
199     * <ul>
200     *   <li>{@link SMAdapter#TYPE_TMK} Terminal Master Key
201     *   <li>{@link SMAdapter#TYPE_ZPK} Zone PIN Key
202     *   <li>or others
203     * </ul>
204     *
205     * @param keyType type of the key
206     */
207    public void setKeyType(String keyType) {
208        this.keyType = keyType;
209    }
210
211    /**
212     * Key Type is useful for stating what this key can be used for.
213     * <p>
214     * The value of Key Type specifies whether this secure key is a
215     * <ul>
216     *   <li>{@link SMAdapter#TYPE_TMK} Terminal Master Key
217     *   <li>{@link SMAdapter#TYPE_ZPK} Zone PIN Key
218     *   <li>or others
219     * </ul>
220     *
221     * @return keyType type of the key
222     */
223    public String getKeyType() {
224        return this.keyType;
225    }
226
227    /**
228     * Sets key protection variant metchod appiled to this key by the security module.
229     *
230     * @param variant key variant method used to protect this key.
231     */
232    public void setVariant(int variant) {
233        this.variant = variant;
234    }
235
236    /**
237     * Gets the key variant method used to protect this key.
238     *
239     * @return key variant method used to protect this key.
240     */
241    public int getVariant() {
242        return this.variant;
243    }
244
245    /**
246     * Identifies the method by which the key block is cryptographically
247     * protected and the content layout of the block.
248     *
249     * @return The key block version that corresponds to byte 0 of the key block.
250     */
251    public char getKeyBlockVersion() {
252        return keyBlockVersion;
253    }
254
255    public void setKeyBlockVersion(char keyBlockVersion) {
256        this.keyBlockVersion = keyBlockVersion;
257    }
258
259    /**
260     * The primary usage of the key contained in the key block.
261     *
262     * @return The key usage that corresponds to bytes 5-6 of the key block.
263     */
264    public KeyUsage getKeyUsage() {
265        return keyUsage;
266    }
267
268    public void setKeyUsage(KeyUsage keyUsage) {
269        this.keyUsage = keyUsage;
270    }
271
272    /**
273     * The cryptographic algorithm with which the key contained in key block
274     * will be used.
275     *
276     * @return The key algorithm that corresponds to byte 7 of the key block.
277     */
278    public Algorithm getAlgorithm() {
279        return algorithm;
280    }
281
282    public void setAlgorithm(Algorithm algorithm) {
283        this.algorithm = algorithm;
284    }
285
286    /**
287     * The operation that the key contained in the key block can perform.
288     *
289     * @return The mode of use that corresponds to byte 8 of the key block.
290     */
291    public ModeOfUse getModeOfUse() {
292        return modeOfUse;
293    }
294
295    public void setModeOfUse(ModeOfUse modeOfUse) {
296        this.modeOfUse = modeOfUse;
297    }
298
299    /**
300     * Version number to optionally indicate that the contents of the key block
301     * is a component (key part), or to prevent re-injection of an old key.
302     *
303     * @return The key version that corresponds to bytes 9-10 of the key block.
304     */
305    public String getKeyVersion() {
306        return keyVersion;
307    }
308
309    public void setKeyVersion(String keyVersion) {
310        this.keyVersion = keyVersion;
311    }
312
313    /**
314     * The conditions under which the key can be exported outside the
315     * cryptographic domain.
316     *
317     * @return The key exportability that corresponds to byte 11 of the key block.
318     */
319    public Exportability getExportability() {
320        return exportability;
321    }
322
323    public void setExportability(Exportability exportability) {
324        this.exportability = exportability;
325    }
326
327    /**
328     * This element is not specified by TR-31 (should contain two ASCII zeros).
329     * <p>
330     * In proprietary derivatives can be used as e.g: LMK identifier.
331     *
332     * @return The reserved that corresponds to bytes 14-15 of the key block.
333     */
334    public String getReserved() {
335        return reserved;
336    }
337
338    public void setReserved(String reserved) {
339        this.reserved = reserved;
340    }
341
342    /**
343     * The key blok Optional Header Blocks.
344     * <p>
345     * The number of optional heders corresponds to bytes 12-13 of the key block.
346     * <p>
347     * The order of the elements in the map is preserved by {@code LinkedHashMap}
348     *
349     * @return map of Optional Key Blok Heders.
350     */
351    public Map<String, String> getOptionalHeaders() {
352        return optionalHeaders;
353    }
354
355    /**
356     * The key block MAC ensures the integrity of the key block.
357     * <p>
358     * It is calculated over the Header, Optional Header Blocks and the
359     * encrypted Key Data.
360     * The length of the MAC depends on the type of LMK key:
361     * <ul>
362     *   <li>4 bytes for DES Key Block LMK
363     *   <li>8 bytes for AES Key Block LMK
364     * </ul>
365     *
366     * @return calculated key block MAC value.
367     */
368    public byte[] getKeyBlockMAC() {
369        return keyBlockMAC;
370    }
371
372    public void setKeyBlockMAC(byte[] keyBlockMAC) {
373        this.keyBlockMAC = keyBlockMAC;
374    }
375
376    /**
377     * Sets the secure key bytes.
378     *
379     * @param keyBytes bytes representing the secured key
380     */
381    public void setKeyBytes(byte[] keyBytes) {
382        this.keyBytes = keyBytes;
383    }
384
385    /**
386     * @return The bytes representing the secured key
387     */
388    public byte[] getKeyBytes() {
389        return keyBytes;
390    }
391
392    /**
393     * The Key Check Value is typically a 24-bits (3 bytes) formed by encrypting a
394     * block of zeros under the secure key when the secure key is clear.
395     * <p>
396     * This check value allows identifying if two secure keys map to the
397     * same clear key.
398     *
399     * @param keyCheckValue the Key Check Value
400     */
401    public void setKeyCheckValue(byte[] keyCheckValue) {
402        this.keyCheckValue = keyCheckValue;
403    }
404
405    /**
406     * The Key Check Value is typically a 24-bits (3 bytes) formed by encrypting
407     * a block of zeros under the secure key when the secure key is clear.
408     *
409     * @return the Key Check Value
410     */
411    public byte[] getKeyCheckValue() {
412        return keyCheckValue;
413    }
414
415    /**
416     * Gets optional key name.
417     *
418     * @return name of the key
419     */
420    public String getKeyName() {
421        return this.keyName;
422    }
423
424    /**
425     * Sets optional key name.
426     *
427     * @param keyName name of the key
428     */
429    public void setKeyName(String keyName) {
430        this.keyName = keyName;
431    }
432
433    /**
434     * Dumps SecureKeySpec information.
435     *
436     * @param p a print stream usually supplied by Logger
437     * @param indent indention string, usually suppiled by Logger
438     * @see org.jpos.util.Loggeable
439     */
440    @Override
441    public void dump(PrintStream p, String indent) {
442        String inner = indent + "  ";
443        p.print(indent + "<secure-key-spec");
444
445        if (scheme != null)
446            p.print(" scheme=\"" + scheme + "\"");
447
448        if (keyName != null)
449            p.print(" name=\"" + keyName + "\"");
450
451        p.println(">");
452
453        if (getKeyLength() > 0)
454            p.println(inner + "<length>" + getKeyLength() + "</length>");
455
456        if (getKeyType() != null) {
457            p.println(inner + "<type>" + getKeyType() + "</type>");
458            p.println(inner + "<variant>" + getVariant() + "</variant>");
459        }
460
461        String keyblok = formKeyHeader(inner);
462        if (keyblok != null) {
463            p.println(inner + "<header>");
464            p.print(keyblok);
465            p.println(inner + "</header>");
466        }
467
468        if (!optionalHeaders.isEmpty()) {
469            p.println(inner + "<optional-header>");
470            String inner2 = inner + "  ";
471            for (Entry<String, String> ent : optionalHeaders.entrySet())
472                p.println(inner2 + "<entry id=\""+ ent.getKey() + "\" value=\""+ ent.getValue()+ "\"/>");
473            p.println(inner + "</optional-header>");
474        }
475
476        if (getKeyBytes() != null)
477            p.println(inner + "<data>" + ISOUtil.hexString(getKeyBytes()) + "</data>");
478
479        if (getKeyBlockMAC() != null)
480            p.println(inner + "<mac>" + ISOUtil.hexString(getKeyBlockMAC()) + "</mac>");
481
482        if (getKeyCheckValue() != null)
483            p.println(inner + "<check-value>" + ISOUtil.hexString(getKeyCheckValue()) + "</check-value>");
484
485        p.println(indent + "</secure-key-spec>");
486    }
487
488    protected String formKeyHeader(String indent) {
489        String inner = indent + "  ";
490        try (
491                ByteArrayOutputStream os = new ByteArrayOutputStream();
492                PrintStream p = new PrintStream(os);
493        ) {
494            if (keyBlockVersion != 0)
495                p.println(inner + "<version>" + keyBlockVersion + "</version>");
496
497            if (keyUsage != null)
498                p.println(inner + "<key-usage>" + keyUsage.getCode() + "</key-usage>");
499
500            if (algorithm != null)
501                p.println(inner + "<algorithm>" + algorithm.getCode() + "</algorithm>");
502
503            if (modeOfUse != null)
504                p.println(inner + "<mode-of-use>" + modeOfUse.getCode() + "</mode-of-use>");
505
506            if (keyVersion != null)
507                p.println(inner + "<key-version>" + keyVersion + "</key-version>");
508
509            if (exportability != null)
510                p.println(inner + "<exportability>" + exportability.getCode() + "</exportability>");
511
512            if (reserved != null)
513                p.println(inner + "<reserved>" + reserved + "</reserved>");
514
515            String ret = os.toString();
516            if (ret.isEmpty())
517                return null;
518
519            return ret;
520        } catch (IOException ex) {
521            // for close(), it should never happens
522            return null;
523        }
524    }
525
526}
527