~brian-thomason/+junk/bouncycastle

« back to all changes in this revision

Viewing changes to src/org/bouncycastle/jce/provider/JDKKeyStore.java

  • Committer: Brian Thomason
  • Date: 2011-12-20 17:20:32 UTC
  • Revision ID: brian.thomason@canonical.com-20111220172032-rdtm13jgdxtksacr
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package org.bouncycastle.jce.provider;
 
2
 
 
3
import java.io.ByteArrayInputStream;
 
4
import java.io.ByteArrayOutputStream;
 
5
import java.io.DataInputStream;
 
6
import java.io.DataOutputStream;
 
7
import java.io.IOException;
 
8
import java.io.InputStream;
 
9
import java.io.OutputStream;
 
10
import java.security.Key;
 
11
import java.security.KeyFactory;
 
12
import java.security.KeyStoreException;
 
13
import java.security.KeyStoreSpi;
 
14
import java.security.NoSuchAlgorithmException;
 
15
import java.security.NoSuchProviderException;
 
16
import java.security.PrivateKey;
 
17
import java.security.PublicKey;
 
18
import java.security.SecureRandom;
 
19
import java.security.UnrecoverableKeyException;
 
20
import java.security.cert.Certificate;
 
21
import java.security.cert.CertificateEncodingException;
 
22
import java.security.cert.CertificateException;
 
23
import java.security.cert.CertificateFactory;
 
24
import java.security.spec.KeySpec;
 
25
import java.security.spec.PKCS8EncodedKeySpec;
 
26
import java.security.spec.X509EncodedKeySpec;
 
27
import java.util.Date;
 
28
import java.util.Enumeration;
 
29
import java.util.Hashtable;
 
30
 
 
31
import javax.crypto.Cipher;
 
32
import javax.crypto.CipherInputStream;
 
33
import javax.crypto.CipherOutputStream;
 
34
import javax.crypto.SecretKeyFactory;
 
35
import javax.crypto.spec.PBEKeySpec;
 
36
import javax.crypto.spec.PBEParameterSpec;
 
37
import javax.crypto.spec.SecretKeySpec;
 
38
 
 
39
import org.bouncycastle.crypto.CipherParameters;
 
40
import org.bouncycastle.crypto.Digest;
 
41
import org.bouncycastle.crypto.PBEParametersGenerator;
 
42
import org.bouncycastle.crypto.digests.SHA1Digest;
 
43
import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator;
 
44
import org.bouncycastle.crypto.io.DigestInputStream;
 
45
import org.bouncycastle.crypto.io.DigestOutputStream;
 
46
import org.bouncycastle.crypto.io.MacInputStream;
 
47
import org.bouncycastle.crypto.io.MacOutputStream;
 
48
import org.bouncycastle.crypto.macs.HMac;
 
49
import org.bouncycastle.jce.interfaces.BCKeyStore;
 
50
import org.bouncycastle.util.Arrays;
 
51
import org.bouncycastle.util.io.Streams;
 
52
 
 
53
public class JDKKeyStore
 
54
    extends KeyStoreSpi
 
55
    implements BCKeyStore
 
56
{
 
57
    private static final int    STORE_VERSION = 1;
 
58
 
 
59
    private static final int    STORE_SALT_SIZE = 20;
 
60
    private static final String STORE_CIPHER = "PBEWithSHAAndTwofish-CBC";
 
61
 
 
62
    private static final int    KEY_SALT_SIZE = 20;
 
63
    private static final int    MIN_ITERATIONS = 1024;
 
64
 
 
65
    private static final String KEY_CIPHER = "PBEWithSHAAnd3-KeyTripleDES-CBC";
 
66
 
 
67
    //
 
68
    // generic object types
 
69
    //
 
70
    static final int NULL           = 0;
 
71
    static final int CERTIFICATE    = 1;
 
72
    static final int KEY            = 2;
 
73
    static final int SECRET         = 3;
 
74
    static final int SEALED         = 4;
 
75
 
 
76
    //
 
77
    // key types
 
78
    //
 
79
    static final int    KEY_PRIVATE = 0;
 
80
    static final int    KEY_PUBLIC  = 1;
 
81
    static final int    KEY_SECRET  = 2;
 
82
 
 
83
    protected Hashtable       table = new Hashtable();
 
84
 
 
85
    protected SecureRandom    random = new SecureRandom();
 
86
 
 
87
    public JDKKeyStore()
 
88
    {
 
89
    }
 
90
 
 
91
    private class StoreEntry
 
92
    {
 
93
        int             type;
 
94
        String          alias;
 
95
        Object          obj;
 
96
        Certificate[]   certChain;
 
97
        Date            date = new Date();
 
98
 
 
99
        StoreEntry(
 
100
            String       alias,
 
101
            Certificate  obj)
 
102
        {
 
103
            this.type = CERTIFICATE;
 
104
            this.alias = alias;
 
105
            this.obj = obj;
 
106
            this.certChain = null;
 
107
        }
 
108
 
 
109
        StoreEntry(
 
110
            String          alias,
 
111
            byte[]          obj,
 
112
            Certificate[]   certChain)
 
113
        {
 
114
            this.type = SECRET;
 
115
            this.alias = alias;
 
116
            this.obj = obj;
 
117
            this.certChain = certChain;
 
118
        }
 
119
 
 
120
        StoreEntry(
 
121
            String          alias,
 
122
            Key             key,
 
123
            char[]          password,
 
124
            Certificate[]   certChain)
 
125
            throws Exception
 
126
        {
 
127
            this.type = SEALED;
 
128
            this.alias = alias;
 
129
            this.certChain = certChain;
 
130
 
 
131
            byte[] salt = new byte[KEY_SALT_SIZE];
 
132
 
 
133
            random.setSeed(System.currentTimeMillis());
 
134
            random.nextBytes(salt);
 
135
 
 
136
            int iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
 
137
 
 
138
 
 
139
            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
 
140
            DataOutputStream        dOut = new DataOutputStream(bOut);
 
141
 
 
142
            dOut.writeInt(salt.length);
 
143
            dOut.write(salt);
 
144
            dOut.writeInt(iterationCount);
 
145
 
 
146
            Cipher              cipher = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
 
147
            CipherOutputStream  cOut = new CipherOutputStream(dOut, cipher);
 
148
 
 
149
            dOut = new DataOutputStream(cOut);
 
150
 
 
151
            encodeKey(key, dOut);
 
152
 
 
153
            dOut.close();
 
154
 
 
155
            obj = bOut.toByteArray();
 
156
        }
 
157
 
 
158
        StoreEntry(
 
159
            String          alias,
 
160
            Date            date,
 
161
            int             type,
 
162
            Object          obj)
 
163
        {
 
164
            this.alias = alias;
 
165
            this.date = date;
 
166
            this.type = type;
 
167
            this.obj = obj;
 
168
        }
 
169
 
 
170
        StoreEntry(
 
171
            String          alias,
 
172
            Date            date,
 
173
            int             type,
 
174
            Object          obj,
 
175
            Certificate[]   certChain)
 
176
        {
 
177
            this.alias = alias;
 
178
            this.date = date;
 
179
            this.type = type;
 
180
            this.obj = obj;
 
181
            this.certChain = certChain;
 
182
        }
 
183
 
 
184
        int getType()
 
185
        {
 
186
            return type;
 
187
        }
 
188
 
 
189
        String getAlias()
 
190
        {
 
191
            return alias;
 
192
        }
 
193
 
 
194
        Object getObject()
 
195
        {
 
196
            return obj;
 
197
        }
 
198
 
 
199
        Object getObject(
 
200
            char[]  password)
 
201
            throws NoSuchAlgorithmException, UnrecoverableKeyException
 
202
        {
 
203
            if (password == null || password.length == 0)
 
204
            {
 
205
                if (obj instanceof Key)
 
206
                {
 
207
                    return obj;
 
208
                }
 
209
            }
 
210
 
 
211
            if (type == SEALED)
 
212
            {
 
213
                ByteArrayInputStream    bIn = new ByteArrayInputStream((byte[])obj);
 
214
                DataInputStream         dIn = new DataInputStream(bIn);
 
215
            
 
216
                try
 
217
                {
 
218
                    byte[]      salt = new byte[dIn.readInt()];
 
219
 
 
220
                    dIn.readFully(salt);
 
221
 
 
222
                    int     iterationCount = dIn.readInt();
 
223
                
 
224
                    Cipher      cipher = makePBECipher(KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
 
225
 
 
226
                    CipherInputStream cIn = new CipherInputStream(dIn, cipher);
 
227
 
 
228
                    try
 
229
                    {
 
230
                        return decodeKey(new DataInputStream(cIn));
 
231
                    }
 
232
                    catch (Exception x)
 
233
                    {
 
234
                        bIn = new ByteArrayInputStream((byte[])obj);
 
235
                        dIn = new DataInputStream(bIn);
 
236
            
 
237
                        salt = new byte[dIn.readInt()];
 
238
 
 
239
                        dIn.readFully(salt);
 
240
 
 
241
                        iterationCount = dIn.readInt();
 
242
 
 
243
                        cipher = makePBECipher("Broken" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
 
244
 
 
245
                        cIn = new CipherInputStream(dIn, cipher);
 
246
 
 
247
                        Key k = null;
 
248
 
 
249
                        try
 
250
                        {
 
251
                            k = decodeKey(new DataInputStream(cIn));
 
252
                        }
 
253
                        catch (Exception y)
 
254
                        {
 
255
                            bIn = new ByteArrayInputStream((byte[])obj);
 
256
                            dIn = new DataInputStream(bIn);
 
257
                
 
258
                            salt = new byte[dIn.readInt()];
 
259
 
 
260
                            dIn.readFully(salt);
 
261
 
 
262
                            iterationCount = dIn.readInt();
 
263
 
 
264
                            cipher = makePBECipher("Old" + KEY_CIPHER, Cipher.DECRYPT_MODE, password, salt, iterationCount);
 
265
 
 
266
                            cIn = new CipherInputStream(dIn, cipher);
 
267
 
 
268
                            k = decodeKey(new DataInputStream(cIn));
 
269
                        }
 
270
 
 
271
                        //
 
272
                        // reencrypt key with correct cipher.
 
273
                        //
 
274
                        if (k != null)
 
275
                        {
 
276
                            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
 
277
                            DataOutputStream        dOut = new DataOutputStream(bOut);
 
278
 
 
279
                            dOut.writeInt(salt.length);
 
280
                            dOut.write(salt);
 
281
                            dOut.writeInt(iterationCount);
 
282
 
 
283
                            Cipher              out = makePBECipher(KEY_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
 
284
                            CipherOutputStream  cOut = new CipherOutputStream(dOut, out);
 
285
 
 
286
                            dOut = new DataOutputStream(cOut);
 
287
 
 
288
                            encodeKey(k, dOut);
 
289
 
 
290
                            dOut.close();
 
291
 
 
292
                            obj = bOut.toByteArray();
 
293
 
 
294
                            return k;
 
295
                        }
 
296
                        else
 
297
                        {
 
298
                            throw new UnrecoverableKeyException("no match");
 
299
                        }
 
300
                    }
 
301
                }
 
302
                catch (Exception e)
 
303
                {
 
304
                    throw new UnrecoverableKeyException("no match");
 
305
                }
 
306
            }
 
307
            else
 
308
            {
 
309
                throw new RuntimeException("forget something!");
 
310
                // TODO
 
311
                // if we get to here key was saved as byte data, which
 
312
                // according to the docs means it must be a private key
 
313
                // in EncryptedPrivateKeyInfo (PKCS8 format), later...
 
314
                //
 
315
            }
 
316
        }
 
317
 
 
318
        Certificate[] getCertificateChain()
 
319
        {
 
320
            return certChain;
 
321
        }
 
322
 
 
323
        Date getDate()
 
324
        {
 
325
            return date;
 
326
        }
 
327
    }
 
328
 
 
329
    private void encodeCertificate(
 
330
        Certificate         cert,
 
331
        DataOutputStream    dOut)
 
332
        throws IOException
 
333
    {
 
334
        try
 
335
        {
 
336
            byte[]      cEnc = cert.getEncoded();
 
337
 
 
338
            dOut.writeUTF(cert.getType());
 
339
            dOut.writeInt(cEnc.length);
 
340
            dOut.write(cEnc);
 
341
        }
 
342
        catch (CertificateEncodingException ex)
 
343
        {
 
344
            throw new IOException(ex.toString());
 
345
        }
 
346
    }
 
347
 
 
348
    private Certificate decodeCertificate(
 
349
        DataInputStream   dIn)
 
350
        throws IOException
 
351
    {
 
352
        String      type = dIn.readUTF();
 
353
        byte[]      cEnc = new byte[dIn.readInt()];
 
354
 
 
355
        dIn.readFully(cEnc);
 
356
 
 
357
        try
 
358
        {
 
359
            CertificateFactory cFact = CertificateFactory.getInstance(type, BouncyCastleProvider.PROVIDER_NAME);
 
360
            ByteArrayInputStream bIn = new ByteArrayInputStream(cEnc);
 
361
 
 
362
            return cFact.generateCertificate(bIn);
 
363
        }
 
364
        catch (NoSuchProviderException ex)
 
365
        {
 
366
            throw new IOException(ex.toString());
 
367
        }
 
368
        catch (CertificateException ex)
 
369
        {
 
370
            throw new IOException(ex.toString());
 
371
        }
 
372
    }
 
373
 
 
374
    private void encodeKey(
 
375
        Key                 key,
 
376
        DataOutputStream    dOut)
 
377
        throws IOException
 
378
    {
 
379
        byte[]      enc = key.getEncoded();
 
380
 
 
381
        if (key instanceof PrivateKey)
 
382
        {
 
383
            dOut.write(KEY_PRIVATE);
 
384
        }
 
385
        else if (key instanceof PublicKey)
 
386
        {
 
387
            dOut.write(KEY_PUBLIC);
 
388
        }
 
389
        else
 
390
        {
 
391
            dOut.write(KEY_SECRET);
 
392
        }
 
393
    
 
394
        dOut.writeUTF(key.getFormat());
 
395
        dOut.writeUTF(key.getAlgorithm());
 
396
        dOut.writeInt(enc.length);
 
397
        dOut.write(enc);
 
398
    }
 
399
 
 
400
    private Key decodeKey(
 
401
        DataInputStream dIn)
 
402
        throws IOException
 
403
    {
 
404
        int         keyType = dIn.read();
 
405
        String      format = dIn.readUTF();
 
406
        String      algorithm = dIn.readUTF();
 
407
        byte[]      enc = new byte[dIn.readInt()];
 
408
        KeySpec     spec;
 
409
 
 
410
        dIn.readFully(enc);
 
411
 
 
412
        if (format.equals("PKCS#8") || format.equals("PKCS8"))
 
413
        {
 
414
            spec = new PKCS8EncodedKeySpec(enc);
 
415
        }
 
416
        else if (format.equals("X.509") || format.equals("X509"))
 
417
        {
 
418
            spec = new X509EncodedKeySpec(enc);
 
419
        }
 
420
        else if (format.equals("RAW"))
 
421
        {
 
422
            return new SecretKeySpec(enc, algorithm);
 
423
        }
 
424
        else
 
425
        {
 
426
            throw new IOException("Key format " + format + " not recognised!");
 
427
        }
 
428
 
 
429
        try
 
430
        {
 
431
            switch (keyType)
 
432
            {
 
433
            case KEY_PRIVATE:
 
434
                return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePrivate(spec);
 
435
            case KEY_PUBLIC:
 
436
                return KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generatePublic(spec);
 
437
            case KEY_SECRET:
 
438
                return SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME).generateSecret(spec);
 
439
            default:
 
440
                throw new IOException("Key type " + keyType + " not recognised!");
 
441
            }
 
442
        }
 
443
        catch (Exception e)
 
444
        {
 
445
            throw new IOException("Exception creating key: " + e.toString());
 
446
        }
 
447
    }
 
448
 
 
449
    protected Cipher makePBECipher(
 
450
        String  algorithm,
 
451
        int     mode,
 
452
        char[]  password,
 
453
        byte[]  salt,
 
454
        int     iterationCount)
 
455
        throws IOException
 
456
    {
 
457
        try
 
458
        {
 
459
            PBEKeySpec          pbeSpec = new PBEKeySpec(password);
 
460
            SecretKeyFactory    keyFact = SecretKeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
 
461
            PBEParameterSpec    defParams = new PBEParameterSpec(salt, iterationCount);
 
462
 
 
463
            Cipher cipher = Cipher.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME);
 
464
 
 
465
            cipher.init(mode, keyFact.generateSecret(pbeSpec), defParams);
 
466
 
 
467
            return cipher;
 
468
        }
 
469
        catch (Exception e)
 
470
        {
 
471
            throw new IOException("Error initialising store of key store: " + e);
 
472
        }
 
473
    }
 
474
 
 
475
    public void setRandom(
 
476
            SecureRandom    rand)
 
477
    {
 
478
        this.random = rand;
 
479
    }
 
480
 
 
481
    public Enumeration engineAliases() 
 
482
    {
 
483
        return table.keys();
 
484
    }
 
485
 
 
486
    public boolean engineContainsAlias(
 
487
        String  alias) 
 
488
    {
 
489
        return (table.get(alias) != null);
 
490
    }
 
491
 
 
492
    public void engineDeleteEntry(
 
493
        String  alias) 
 
494
        throws KeyStoreException
 
495
    {
 
496
        Object  entry = table.get(alias);
 
497
 
 
498
        if (entry == null)
 
499
        {
 
500
            throw new KeyStoreException("no such entry as " + alias);
 
501
        }
 
502
 
 
503
        table.remove(alias);
 
504
    }
 
505
 
 
506
    public Certificate engineGetCertificate(
 
507
        String alias) 
 
508
    {
 
509
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
510
 
 
511
        if (entry != null)
 
512
        {
 
513
            if (entry.getType() == CERTIFICATE)
 
514
            {
 
515
                return (Certificate)entry.getObject();
 
516
            }
 
517
            else
 
518
            {
 
519
                Certificate[]   chain = entry.getCertificateChain();
 
520
 
 
521
                if (chain != null)
 
522
                {
 
523
                    return chain[0];
 
524
                }
 
525
            }
 
526
        }
 
527
 
 
528
        return null;
 
529
    }
 
530
 
 
531
    public String engineGetCertificateAlias(
 
532
        Certificate cert) 
 
533
    {
 
534
        Enumeration e = table.elements();
 
535
        while (e.hasMoreElements())
 
536
        {
 
537
            StoreEntry  entry = (StoreEntry)e.nextElement();
 
538
 
 
539
            if (entry.getObject() instanceof Certificate)
 
540
            {
 
541
                Certificate c = (Certificate)entry.getObject();
 
542
 
 
543
                if (c.equals(cert))
 
544
                {
 
545
                    return entry.getAlias();
 
546
                }
 
547
            }
 
548
            else
 
549
            {
 
550
                Certificate[]   chain = entry.getCertificateChain();
 
551
 
 
552
                if (chain != null && chain[0].equals(cert))
 
553
                {
 
554
                    return entry.getAlias();
 
555
                }
 
556
            }
 
557
        }
 
558
 
 
559
        return null;
 
560
    }
 
561
    
 
562
    public Certificate[] engineGetCertificateChain(
 
563
        String alias) 
 
564
    {
 
565
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
566
 
 
567
        if (entry != null)
 
568
        {
 
569
            return entry.getCertificateChain();
 
570
        }
 
571
 
 
572
        return null;
 
573
    }
 
574
    
 
575
    public Date engineGetCreationDate(String alias) 
 
576
    {
 
577
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
578
 
 
579
        if (entry != null)
 
580
        {
 
581
            return entry.getDate();
 
582
        }
 
583
 
 
584
        return null;
 
585
    }
 
586
 
 
587
    public Key engineGetKey(
 
588
        String alias,
 
589
        char[] password) 
 
590
        throws NoSuchAlgorithmException, UnrecoverableKeyException
 
591
    {
 
592
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
593
 
 
594
        if (entry == null || entry.getType() == CERTIFICATE)
 
595
        {
 
596
            return null;
 
597
        }
 
598
 
 
599
        return (Key)entry.getObject(password);
 
600
    }
 
601
 
 
602
    public boolean engineIsCertificateEntry(
 
603
        String alias) 
 
604
    {
 
605
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
606
 
 
607
        if (entry != null && entry.getType() == CERTIFICATE)
 
608
        {
 
609
            return true;
 
610
        }
 
611
    
 
612
        return false;
 
613
    }
 
614
 
 
615
    public boolean engineIsKeyEntry(
 
616
        String alias) 
 
617
    {
 
618
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
619
 
 
620
        if (entry != null && entry.getType() != CERTIFICATE)
 
621
        {
 
622
            return true;
 
623
        }
 
624
    
 
625
        return false;
 
626
    }
 
627
 
 
628
    public void engineSetCertificateEntry(
 
629
        String      alias,
 
630
        Certificate cert) 
 
631
        throws KeyStoreException
 
632
    {
 
633
        StoreEntry  entry = (StoreEntry)table.get(alias);
 
634
 
 
635
        if (entry != null && entry.getType() != CERTIFICATE)
 
636
        {
 
637
            throw new KeyStoreException("key store already has a key entry with alias " + alias);
 
638
        }
 
639
 
 
640
        table.put(alias, new StoreEntry(alias, cert));
 
641
    }
 
642
 
 
643
    public void engineSetKeyEntry(
 
644
        String alias,
 
645
        byte[] key,
 
646
        Certificate[] chain) 
 
647
        throws KeyStoreException
 
648
    {
 
649
        table.put(alias, new StoreEntry(alias, key, chain));
 
650
    }
 
651
 
 
652
    public void engineSetKeyEntry(
 
653
        String          alias,
 
654
        Key             key,
 
655
        char[]          password,
 
656
        Certificate[]   chain) 
 
657
        throws KeyStoreException
 
658
    {
 
659
        if ((key instanceof PrivateKey) && (chain == null))
 
660
        {
 
661
            throw new KeyStoreException("no certificate chain for private key");
 
662
        }
 
663
 
 
664
        try
 
665
        {
 
666
            table.put(alias, new StoreEntry(alias, key, password, chain));
 
667
        }
 
668
        catch (Exception e)
 
669
        {
 
670
            throw new KeyStoreException(e.toString());
 
671
        }
 
672
    }
 
673
 
 
674
    public int engineSize() 
 
675
    {
 
676
        return table.size();
 
677
    }
 
678
 
 
679
    protected void loadStore(
 
680
        InputStream in)
 
681
        throws IOException
 
682
    {
 
683
        DataInputStream     dIn = new DataInputStream(in);
 
684
        int                 type = dIn.read();
 
685
 
 
686
        while (type > NULL)
 
687
        {
 
688
            String          alias = dIn.readUTF();
 
689
            Date            date = new Date(dIn.readLong());
 
690
            int             chainLength = dIn.readInt();
 
691
            Certificate[]   chain = null;
 
692
 
 
693
            if (chainLength != 0)
 
694
            {
 
695
                chain = new Certificate[chainLength];
 
696
 
 
697
                for (int i = 0; i != chainLength; i++)
 
698
                {
 
699
                    chain[i] = decodeCertificate(dIn);
 
700
                }
 
701
            }
 
702
 
 
703
            switch (type)
 
704
            {
 
705
            case CERTIFICATE:
 
706
                    Certificate     cert = decodeCertificate(dIn);
 
707
 
 
708
                    table.put(alias, new StoreEntry(alias, date, CERTIFICATE, cert));
 
709
                    break;
 
710
            case KEY:
 
711
                    Key     key = decodeKey(dIn);
 
712
                    table.put(alias, new StoreEntry(alias, date, KEY, key, chain));
 
713
                    break;
 
714
            case SECRET:
 
715
            case SEALED:
 
716
                    byte[]      b = new byte[dIn.readInt()];
 
717
 
 
718
                    dIn.readFully(b);
 
719
                    table.put(alias, new StoreEntry(alias, date, type, b, chain));
 
720
                    break;
 
721
            default:
 
722
                    throw new RuntimeException("Unknown object type in store.");
 
723
            }
 
724
 
 
725
            type = dIn.read();
 
726
        }
 
727
    }
 
728
 
 
729
    protected void saveStore(
 
730
        OutputStream    out)
 
731
        throws IOException
 
732
    {
 
733
        Enumeration         e = table.elements();
 
734
        DataOutputStream    dOut = new DataOutputStream(out);
 
735
 
 
736
        while (e.hasMoreElements())
 
737
        {
 
738
            StoreEntry  entry = (StoreEntry)e.nextElement();
 
739
 
 
740
            dOut.write(entry.getType());
 
741
            dOut.writeUTF(entry.getAlias());
 
742
            dOut.writeLong(entry.getDate().getTime());
 
743
 
 
744
            Certificate[]   chain = entry.getCertificateChain();
 
745
            if (chain == null)
 
746
            {
 
747
                dOut.writeInt(0);
 
748
            }
 
749
            else
 
750
            {
 
751
                dOut.writeInt(chain.length);
 
752
                for (int i = 0; i != chain.length; i++)
 
753
                {
 
754
                    encodeCertificate(chain[i], dOut);
 
755
                }
 
756
            }
 
757
 
 
758
            switch (entry.getType())
 
759
            {
 
760
            case CERTIFICATE:
 
761
                    encodeCertificate((Certificate)entry.getObject(), dOut);
 
762
                    break;
 
763
            case KEY:
 
764
                    encodeKey((Key)entry.getObject(), dOut);
 
765
                    break;
 
766
            case SEALED:
 
767
            case SECRET:
 
768
                    byte[]  b = (byte[])entry.getObject();
 
769
 
 
770
                    dOut.writeInt(b.length);
 
771
                    dOut.write(b);
 
772
                    break;
 
773
            default:
 
774
                    throw new RuntimeException("Unknown object type in store.");
 
775
            }
 
776
        }
 
777
 
 
778
        dOut.write(NULL);
 
779
    }
 
780
 
 
781
    public void engineLoad(
 
782
        InputStream stream,
 
783
        char[]      password) 
 
784
        throws IOException
 
785
    {
 
786
        table.clear();
 
787
 
 
788
        if (stream == null)     // just initialising
 
789
        {
 
790
            return;
 
791
        }
 
792
 
 
793
        DataInputStream     dIn = new DataInputStream(stream);
 
794
        int                 version = dIn.readInt();
 
795
 
 
796
        if (version != STORE_VERSION)
 
797
        {
 
798
            if (version != 0)
 
799
            {
 
800
                throw new IOException("Wrong version of key store.");
 
801
            }
 
802
        }
 
803
 
 
804
        byte[]      salt = new byte[dIn.readInt()];
 
805
 
 
806
        dIn.readFully(salt);
 
807
 
 
808
        int         iterationCount = dIn.readInt();
 
809
 
 
810
        //
 
811
        // we only do an integrity check if the password is provided.
 
812
        //
 
813
        HMac hMac = new HMac(new SHA1Digest());
 
814
        if (password != null && password.length != 0)
 
815
        {
 
816
            byte[] passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
 
817
 
 
818
            PBEParametersGenerator pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
 
819
            pbeGen.init(passKey, salt, iterationCount);
 
820
            CipherParameters macParams = pbeGen.generateDerivedMacParameters(hMac.getMacSize());
 
821
            Arrays.fill(passKey, (byte)0);
 
822
 
 
823
            hMac.init(macParams);
 
824
            MacInputStream mIn = new MacInputStream(dIn, hMac);
 
825
 
 
826
            loadStore(mIn);
 
827
 
 
828
            // Finalise our mac calculation
 
829
            byte[] mac = new byte[hMac.getMacSize()];
 
830
            hMac.doFinal(mac, 0);
 
831
 
 
832
            // TODO Should this actually be reading the remainder of the stream?
 
833
            // Read the original mac from the stream
 
834
            byte[] oldMac = new byte[hMac.getMacSize()];
 
835
            dIn.readFully(oldMac);
 
836
 
 
837
            if (!Arrays.constantTimeAreEqual(mac, oldMac))
 
838
            {
 
839
                table.clear();
 
840
                throw new IOException("KeyStore integrity check failed.");
 
841
            }
 
842
        }
 
843
        else
 
844
        {
 
845
            loadStore(dIn);
 
846
 
 
847
            // TODO Should this actually be reading the remainder of the stream?
 
848
            // Parse the original mac from the stream too
 
849
            byte[] oldMac = new byte[hMac.getMacSize()];
 
850
            dIn.readFully(oldMac);
 
851
        }
 
852
    }
 
853
 
 
854
 
 
855
    public void engineStore(OutputStream stream, char[] password) 
 
856
        throws IOException
 
857
    {
 
858
        DataOutputStream    dOut = new DataOutputStream(stream);
 
859
        byte[]              salt = new byte[STORE_SALT_SIZE];
 
860
        int                 iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
 
861
 
 
862
        random.nextBytes(salt);
 
863
 
 
864
        dOut.writeInt(STORE_VERSION);
 
865
        dOut.writeInt(salt.length);
 
866
        dOut.write(salt);
 
867
        dOut.writeInt(iterationCount);
 
868
 
 
869
        HMac                    hMac = new HMac(new SHA1Digest());
 
870
        MacOutputStream         mOut = new MacOutputStream(dOut, hMac);
 
871
        PBEParametersGenerator  pbeGen = new PKCS12ParametersGenerator(new SHA1Digest());
 
872
        byte[]                  passKey = PBEParametersGenerator.PKCS12PasswordToBytes(password);
 
873
 
 
874
        pbeGen.init(passKey, salt, iterationCount);
 
875
 
 
876
        hMac.init(pbeGen.generateDerivedMacParameters(hMac.getMacSize()));
 
877
 
 
878
        for (int i = 0; i != passKey.length; i++)
 
879
        {
 
880
            passKey[i] = 0;
 
881
        }
 
882
 
 
883
        saveStore(mOut);
 
884
 
 
885
        byte[]  mac = new byte[hMac.getMacSize()];
 
886
 
 
887
        hMac.doFinal(mac, 0);
 
888
 
 
889
        dOut.write(mac);
 
890
 
 
891
        dOut.close();
 
892
    }
 
893
 
 
894
    /**
 
895
     * the BouncyCastle store. This wont work with the key tool as the
 
896
     * store is stored encrypteed on disk, so the password is mandatory,
 
897
     * however if you hard drive is in a bad part of town and you absolutely,
 
898
     * positively, don't want nobody peeking at your things, this is the
 
899
     * one to use, no problem! After all in a Bouncy Castle nothing can
 
900
     * touch you.
 
901
     *
 
902
     * Also referred to by the alias UBER.
 
903
     */
 
904
    public static class BouncyCastleStore
 
905
        extends JDKKeyStore
 
906
    {
 
907
        public void engineLoad(
 
908
            InputStream stream,
 
909
            char[]      password) 
 
910
            throws IOException
 
911
        {
 
912
            table.clear();
 
913
    
 
914
            if (stream == null)     // just initialising
 
915
            {
 
916
                return;
 
917
            }
 
918
    
 
919
            DataInputStream     dIn = new DataInputStream(stream);
 
920
            int                 version = dIn.readInt();
 
921
    
 
922
            if (version != STORE_VERSION)
 
923
            {
 
924
                if (version != 0)
 
925
                {
 
926
                    throw new IOException("Wrong version of key store.");
 
927
                }
 
928
            }
 
929
    
 
930
            byte[]      salt = new byte[dIn.readInt()];
 
931
 
 
932
            if (salt.length != STORE_SALT_SIZE)
 
933
            {
 
934
                throw new IOException("Key store corrupted.");
 
935
            }
 
936
    
 
937
            dIn.readFully(salt);
 
938
    
 
939
            int         iterationCount = dIn.readInt();
 
940
    
 
941
            if ((iterationCount < 0) || (iterationCount > 4 *  MIN_ITERATIONS))
 
942
            {
 
943
                throw new IOException("Key store corrupted.");
 
944
            }
 
945
    
 
946
            String cipherAlg;
 
947
            if (version == 0)
 
948
            {
 
949
                cipherAlg = "Old" + STORE_CIPHER;
 
950
            }
 
951
            else
 
952
            {
 
953
                cipherAlg = STORE_CIPHER;
 
954
            }
 
955
 
 
956
            Cipher cipher = this.makePBECipher(cipherAlg, Cipher.DECRYPT_MODE, password, salt, iterationCount);
 
957
            CipherInputStream cIn = new CipherInputStream(dIn, cipher);
 
958
 
 
959
            Digest dig = new SHA1Digest();
 
960
            DigestInputStream  dgIn = new DigestInputStream(cIn, dig);
 
961
    
 
962
            this.loadStore(dgIn);
 
963
 
 
964
            // Finalise our digest calculation
 
965
            byte[] hash = new byte[dig.getDigestSize()];
 
966
            dig.doFinal(hash, 0);
 
967
 
 
968
            // TODO Should this actually be reading the remainder of the stream?
 
969
            // Read the original digest from the stream
 
970
            byte[] oldHash = new byte[dig.getDigestSize()];
 
971
            Streams.readFully(cIn, oldHash);
 
972
 
 
973
            if (!Arrays.constantTimeAreEqual(hash, oldHash))
 
974
            {
 
975
                table.clear();
 
976
                throw new IOException("KeyStore integrity check failed.");
 
977
            }
 
978
        }
 
979
    
 
980
    
 
981
        public void engineStore(OutputStream stream, char[] password) 
 
982
            throws IOException
 
983
        {
 
984
            Cipher              cipher;
 
985
            DataOutputStream    dOut = new DataOutputStream(stream);
 
986
            byte[]              salt = new byte[STORE_SALT_SIZE];
 
987
            int                 iterationCount = MIN_ITERATIONS + (random.nextInt() & 0x3ff);
 
988
    
 
989
            random.nextBytes(salt);
 
990
    
 
991
            dOut.writeInt(STORE_VERSION);
 
992
            dOut.writeInt(salt.length);
 
993
            dOut.write(salt);
 
994
            dOut.writeInt(iterationCount);
 
995
    
 
996
            cipher = this.makePBECipher(STORE_CIPHER, Cipher.ENCRYPT_MODE, password, salt, iterationCount);
 
997
    
 
998
            CipherOutputStream  cOut = new CipherOutputStream(dOut, cipher);
 
999
            DigestOutputStream  dgOut = new DigestOutputStream(cOut, new SHA1Digest());
 
1000
    
 
1001
            this.saveStore(dgOut);
 
1002
    
 
1003
            Digest  dig = dgOut.getDigest();
 
1004
            byte[]  hash = new byte[dig.getDigestSize()];
 
1005
    
 
1006
            dig.doFinal(hash, 0);
 
1007
    
 
1008
            cOut.write(hash);
 
1009
    
 
1010
            cOut.close();
 
1011
        }
 
1012
    }
 
1013
}