1 package org.bouncycastle.openpgp;
2 
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.util.ArrayList;
9 import java.util.Iterator;
10 import java.util.List;
11 
12 import org.bouncycastle.bcpg.BCPGInputStream;
13 import org.bouncycastle.bcpg.BCPGObject;
14 import org.bouncycastle.bcpg.BCPGOutputStream;
15 import org.bouncycastle.bcpg.ContainedPacket;
16 import org.bouncycastle.bcpg.DSASecretBCPGKey;
17 import org.bouncycastle.bcpg.ECSecretBCPGKey;
18 import org.bouncycastle.bcpg.EdSecretBCPGKey;
19 import org.bouncycastle.bcpg.ElGamalSecretBCPGKey;
20 import org.bouncycastle.bcpg.HashAlgorithmTags;
21 import org.bouncycastle.bcpg.PublicKeyPacket;
22 import org.bouncycastle.bcpg.PublicSubkeyPacket;
23 import org.bouncycastle.bcpg.RSASecretBCPGKey;
24 import org.bouncycastle.bcpg.S2K;
25 import org.bouncycastle.bcpg.SecretKeyPacket;
26 import org.bouncycastle.bcpg.SecretSubkeyPacket;
27 import org.bouncycastle.bcpg.SignatureSubpacketTags;
28 import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
29 import org.bouncycastle.bcpg.UserAttributePacket;
30 import org.bouncycastle.bcpg.UserIDPacket;
31 import org.bouncycastle.gpg.SExprParser;
32 import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
33 import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory;
34 import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
35 import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
36 import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
37 import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
38 
39 /**
40  * general class to handle and construct  a PGP secret key object.
41  */
42 public class PGPSecretKey
43 {
44     SecretKeyPacket secret;
45     PGPPublicKey pub;
46 
PGPSecretKey( SecretKeyPacket secret, PGPPublicKey pub)47     public PGPSecretKey(
48         SecretKeyPacket secret,
49         PGPPublicKey pub)
50     {
51         this.secret = secret;
52         this.pub = pub;
53     }
54 
PGPSecretKey( PGPPrivateKey privKey, PGPPublicKey pubKey, PGPDigestCalculator checksumCalculator, PBESecretKeyEncryptor keyEncryptor)55     PGPSecretKey(
56         PGPPrivateKey privKey,
57         PGPPublicKey pubKey,
58         PGPDigestCalculator checksumCalculator,
59         PBESecretKeyEncryptor keyEncryptor)
60         throws PGPException
61     {
62         this(privKey, pubKey, checksumCalculator, false, keyEncryptor);
63     }
64 
65     /**
66      * Construct a PGPSecretKey using the passed in private key and public key. This constructor will not add any
67      * certifications but assumes that pubKey already has what is required.
68      *
69      * @param privKey            the private key component.
70      * @param pubKey             the public key component.
71      * @param checksumCalculator a calculator for the private key checksum
72      * @param isMasterKey        true if the key is a master key, false otherwise.
73      * @param keyEncryptor       an encryptor for the key if required (null otherwise).
74      * @throws PGPException if there is an issue creating the secret key packet.
75      */
PGPSecretKey( PGPPrivateKey privKey, PGPPublicKey pubKey, PGPDigestCalculator checksumCalculator, boolean isMasterKey, PBESecretKeyEncryptor keyEncryptor)76     public PGPSecretKey(
77         PGPPrivateKey privKey,
78         PGPPublicKey pubKey,
79         PGPDigestCalculator checksumCalculator,
80         boolean isMasterKey,
81         PBESecretKeyEncryptor keyEncryptor)
82         throws PGPException
83     {
84         this.pub = pubKey;
85         this.secret = buildSecretKeyPacket(isMasterKey, privKey, pubKey, keyEncryptor, checksumCalculator);
86     }
87 
buildSecretKeyPacket(boolean isMasterKey, PGPPrivateKey privKey, PGPPublicKey pubKey, PBESecretKeyEncryptor keyEncryptor, PGPDigestCalculator checksumCalculator)88     private static SecretKeyPacket buildSecretKeyPacket(boolean isMasterKey, PGPPrivateKey privKey, PGPPublicKey pubKey, PBESecretKeyEncryptor keyEncryptor, PGPDigestCalculator checksumCalculator)
89         throws PGPException
90     {
91         BCPGObject secKey = (BCPGObject)privKey.getPrivateKeyDataPacket();
92 
93         if (secKey == null)
94         {
95             if (isMasterKey)
96             {
97                 return new SecretKeyPacket(pubKey.publicPk, SymmetricKeyAlgorithmTags.NULL, null, null, new byte[0]);
98             }
99             else
100             {
101                 return new SecretSubkeyPacket(pubKey.publicPk, SymmetricKeyAlgorithmTags.NULL, null, null, new byte[0]);
102             }
103         }
104 
105         try
106         {
107             ByteArrayOutputStream bOut = new ByteArrayOutputStream();
108             BCPGOutputStream pOut = new BCPGOutputStream(bOut);
109 
110             pOut.writeObject(secKey);
111 
112             byte[] keyData = bOut.toByteArray();
113 
114             int encAlgorithm = (keyEncryptor != null) ? keyEncryptor.getAlgorithm() : SymmetricKeyAlgorithmTags.NULL;
115 
116             if (encAlgorithm != SymmetricKeyAlgorithmTags.NULL)
117             {
118                 pOut.write(checksum(checksumCalculator, keyData, keyData.length));
119 
120                 keyData = bOut.toByteArray(); // include checksum
121 
122                 byte[] encData = keyEncryptor.encryptKeyData(keyData, 0, keyData.length);
123                 byte[] iv = keyEncryptor.getCipherIV();
124 
125                 S2K s2k = keyEncryptor.getS2K();
126 
127                 int s2kUsage;
128 
129                 if (checksumCalculator != null)
130                 {
131                     if (checksumCalculator.getAlgorithm() != HashAlgorithmTags.SHA1)
132                     {
133                         throw new PGPException("only SHA1 supported for key checksum calculations.");
134                     }
135                     s2kUsage = SecretKeyPacket.USAGE_SHA1;
136                 }
137                 else
138                 {
139                     s2kUsage = SecretKeyPacket.USAGE_CHECKSUM;
140                 }
141 
142                 if (isMasterKey)
143                 {
144                     return new SecretKeyPacket(pubKey.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
145                 }
146                 else
147                 {
148                     return new SecretSubkeyPacket(pubKey.publicPk, encAlgorithm, s2kUsage, s2k, iv, encData);
149                 }
150             }
151             else
152             {
153                 pOut.write(checksum(null, keyData, keyData.length));
154 
155                 if (isMasterKey)
156                 {
157                     return new SecretKeyPacket(pubKey.publicPk, encAlgorithm, null, null, bOut.toByteArray());
158                 }
159                 else
160                 {
161                     return new SecretSubkeyPacket(pubKey.publicPk, encAlgorithm, null, null, bOut.toByteArray());
162                 }
163             }
164         }
165         catch (PGPException e)
166         {
167             throw e;
168         }
169         catch (Exception e)
170         {
171             throw new PGPException("Exception encrypting key", e);
172         }
173     }
174 
175     /**
176      * Construct a PGPSecretKey using the passed in private/public key pair and binding it to the passed in id
177      * using a generated certification of certificationLevel.The secret key checksum is calculated using the original
178      * non-digest based checksum.
179      *
180      * @param certificationLevel         the type of certification to be added.
181      * @param keyPair                    the public/private keys to use.
182      * @param id                         the id to bind to the key.
183      * @param hashedPcks                 the hashed packets to be added to the certification.
184      * @param unhashedPcks               the unhashed packets to be added to the certification.
185      * @param certificationSignerBuilder the builder for generating the certification.
186      * @param keyEncryptor               an encryptor for the key if required (null otherwise).
187      * @throws PGPException if there is an issue creating the secret key packet or the certification.
188      */
PGPSecretKey( int certificationLevel, PGPKeyPair keyPair, String id, PGPSignatureSubpacketVector hashedPcks, PGPSignatureSubpacketVector unhashedPcks, PGPContentSignerBuilder certificationSignerBuilder, PBESecretKeyEncryptor keyEncryptor)189     public PGPSecretKey(
190         int certificationLevel,
191         PGPKeyPair keyPair,
192         String id,
193         PGPSignatureSubpacketVector hashedPcks,
194         PGPSignatureSubpacketVector unhashedPcks,
195         PGPContentSignerBuilder certificationSignerBuilder,
196         PBESecretKeyEncryptor keyEncryptor)
197         throws PGPException
198     {
199         this(certificationLevel, keyPair, id, null, hashedPcks, unhashedPcks, certificationSignerBuilder, keyEncryptor);
200     }
201 
202     /**
203      * Construct a PGPSecretKey sub-key using the passed in private/public key pair and binding it to the master key pair.
204      * The secret key checksum is calculated using the passed in checksum calculator.
205      *
206      * @param masterKeyPair              the master public/private keys for the new subkey.
207      * @param keyPair                    the public/private keys to use.
208      * @param checksumCalculator         a calculator for the private key checksum
209      * @param certificationSignerBuilder the builder for generating the certification.
210      * @param keyEncryptor               an encryptor for the key if required (null otherwise).
211      * @throws PGPException if there is an issue creating the secret key packet or the certification.
212      */
PGPSecretKey( PGPKeyPair masterKeyPair, PGPKeyPair keyPair, PGPDigestCalculator checksumCalculator, PGPContentSignerBuilder certificationSignerBuilder, PBESecretKeyEncryptor keyEncryptor)213     public PGPSecretKey(
214         PGPKeyPair masterKeyPair,
215         PGPKeyPair keyPair,
216         PGPDigestCalculator checksumCalculator,
217         PGPContentSignerBuilder certificationSignerBuilder,
218         PBESecretKeyEncryptor keyEncryptor)
219         throws PGPException
220     {
221         this(masterKeyPair, keyPair, checksumCalculator, null, null, certificationSignerBuilder, keyEncryptor);
222     }
223 
224     /**
225      * Construct a PGPSecretKey sub-key using the passed in private/public key pair and binding it to the master key pair.
226      * The secret key checksum is calculated using the passed in checksum calculator.
227      *
228      * @param masterKeyPair              the master public/private keys for the new subkey.
229      * @param keyPair                    the public/private keys to use.
230      * @param checksumCalculator         calculator for PGP key checksums.
231      * @param hashedPcks                 the hashed packets to be added to the certification.
232      * @param unhashedPcks               the unhashed packets to be added to the certification.
233      * @param certificationSignerBuilder the builder for generating the certification.
234      * @param keyEncryptor               an encryptor for the key if required (null otherwise).
235      * @throws PGPException if there is an issue creating the secret key packet or the certification.
236      */
PGPSecretKey( PGPKeyPair masterKeyPair, PGPKeyPair keyPair, PGPDigestCalculator checksumCalculator, PGPSignatureSubpacketVector hashedPcks, PGPSignatureSubpacketVector unhashedPcks, PGPContentSignerBuilder certificationSignerBuilder, PBESecretKeyEncryptor keyEncryptor)237     public PGPSecretKey(
238         PGPKeyPair masterKeyPair,
239         PGPKeyPair keyPair,
240         PGPDigestCalculator checksumCalculator,
241         PGPSignatureSubpacketVector hashedPcks,
242         PGPSignatureSubpacketVector unhashedPcks,
243         PGPContentSignerBuilder certificationSignerBuilder,
244         PBESecretKeyEncryptor keyEncryptor)
245         throws PGPException
246     {
247         //
248         // generate the certification
249         //
250         PGPSignatureGenerator sGen = new PGPSignatureGenerator(certificationSignerBuilder);
251 
252         sGen.init(PGPSignature.SUBKEY_BINDING, masterKeyPair.getPrivateKey());
253 
254         // do some basic checking if we are a signing key.
255         if (!keyPair.getPublicKey().isEncryptionKey())
256         {
257             if (hashedPcks == null)
258             {
259                 PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(certificationSignerBuilder);
260 
261                 signatureGenerator.init(PGPSignature.PRIMARYKEY_BINDING, keyPair.getPrivateKey());
262 
263                 PGPSignatureSubpacketGenerator subGen = new PGPSignatureSubpacketGenerator();
264 
265                 try
266                 {
267                     subGen.setEmbeddedSignature(false, signatureGenerator.generateCertification(masterKeyPair.getPublicKey(), keyPair.getPublicKey()));
268 
269                     hashedPcks = subGen.generate();
270                 }
271                 catch (IOException e)
272                 {
273                     throw new PGPException(e.getMessage(), e);
274                 }
275             }
276             else if (!hashedPcks.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE))
277             {
278                 throw new PGPException("signing subkey requires embedded PRIMARYKEY_BINDING signature");
279             }
280         }
281 
282         sGen.setHashedSubpackets(hashedPcks);
283         sGen.setUnhashedSubpackets(unhashedPcks);
284 
285         List subSigs = new ArrayList();
286 
287         subSigs.add(sGen.generateCertification(masterKeyPair.getPublicKey(), keyPair.getPublicKey()));
288 
289         // replace the public key packet structure with a public subkey one.
290         PGPPublicKey pubSubKey = new PGPPublicKey(keyPair.getPublicKey(), null, subSigs);
291 
292         pubSubKey.publicPk = new PublicSubkeyPacket(pubSubKey.getAlgorithm(), pubSubKey.getCreationTime(), pubSubKey.publicPk.getKey());
293 
294         this.pub = pubSubKey;
295         this.secret = buildSecretKeyPacket(false, keyPair.getPrivateKey(), keyPair.getPublicKey(), keyEncryptor, checksumCalculator);
296     }
297 
298     /**
299      * Construct a PGPSecretKey using the passed in private/public key pair and binding it to the passed in id
300      * using a generated certification of certificationLevel.
301      *
302      * @param certificationLevel         the type of certification to be added.
303      * @param keyPair                    the public/private keys to use.
304      * @param id                         the id to bind to the key.
305      * @param checksumCalculator         a calculator for the private key checksum.
306      * @param hashedPcks                 the hashed packets to be added to the certification.
307      * @param unhashedPcks               the unhashed packets to be added to the certification.
308      * @param certificationSignerBuilder the builder for generating the certification.
309      * @param keyEncryptor               an encryptor for the key if required (null otherwise).
310      * @throws PGPException if there is an issue creating the secret key packet or the certification.
311      */
PGPSecretKey( int certificationLevel, PGPKeyPair keyPair, String id, PGPDigestCalculator checksumCalculator, PGPSignatureSubpacketVector hashedPcks, PGPSignatureSubpacketVector unhashedPcks, PGPContentSignerBuilder certificationSignerBuilder, PBESecretKeyEncryptor keyEncryptor)312     public PGPSecretKey(
313         int certificationLevel,
314         PGPKeyPair keyPair,
315         String id,
316         PGPDigestCalculator checksumCalculator,
317         PGPSignatureSubpacketVector hashedPcks,
318         PGPSignatureSubpacketVector unhashedPcks,
319         PGPContentSignerBuilder certificationSignerBuilder,
320         PBESecretKeyEncryptor keyEncryptor)
321         throws PGPException
322     {
323         this(keyPair.getPrivateKey(), certifiedPublicKey(certificationLevel, keyPair, id, hashedPcks, unhashedPcks, certificationSignerBuilder), checksumCalculator, true, keyEncryptor);
324     }
325 
certifiedPublicKey( int certificationLevel, PGPKeyPair keyPair, String id, PGPSignatureSubpacketVector hashedPcks, PGPSignatureSubpacketVector unhashedPcks, PGPContentSignerBuilder certificationSignerBuilder)326     private static PGPPublicKey certifiedPublicKey(
327         int certificationLevel,
328         PGPKeyPair keyPair,
329         String id,
330         PGPSignatureSubpacketVector hashedPcks,
331         PGPSignatureSubpacketVector unhashedPcks,
332         PGPContentSignerBuilder certificationSignerBuilder)
333         throws PGPException
334     {
335         PGPSignatureGenerator sGen;
336 
337         try
338         {
339             sGen = new PGPSignatureGenerator(certificationSignerBuilder);
340         }
341         catch (Exception e)
342         {
343             throw new PGPException("creating signature generator: " + e, e);
344         }
345 
346         //
347         // generate the certification
348         //
349         sGen.init(certificationLevel, keyPair.getPrivateKey());
350 
351         sGen.setHashedSubpackets(hashedPcks);
352         sGen.setUnhashedSubpackets(unhashedPcks);
353 
354         try
355         {
356             PGPSignature certification = sGen.generateCertification(id, keyPair.getPublicKey());
357 
358             return PGPPublicKey.addCertification(keyPair.getPublicKey(), id, certification);
359         }
360         catch (Exception e)
361         {
362             throw new PGPException("exception doing certification: " + e, e);
363         }
364     }
365 
366     /**
367      * Return true if this key has an algorithm type that makes it suitable to use for signing.
368      * <p>
369      * Note: with version 4 keys KeyFlags subpackets should also be considered when present for
370      * determining the preferred use of the key.
371      *
372      * @return true if this key algorithm is suitable for use with signing.
373      */
isSigningKey()374     public boolean isSigningKey()
375     {
376         int algorithm = pub.getAlgorithm();
377 
378         return ((algorithm == PGPPublicKey.RSA_GENERAL) || (algorithm == PGPPublicKey.RSA_SIGN)
379             || (algorithm == PGPPublicKey.DSA) || (algorithm == PGPPublicKey.ECDSA) || (algorithm == PGPPublicKey.EDDSA) || (algorithm == PGPPublicKey.ELGAMAL_GENERAL));
380     }
381 
382     /**
383      * Return true if this is a master key.
384      *
385      * @return true if a master key.
386      */
isMasterKey()387     public boolean isMasterKey()
388     {
389         return pub.isMasterKey();
390     }
391 
392     /**
393      * Detect if the Secret Key's Private Key is empty or not
394      *
395      * @return boolean whether or not the private key is empty
396      */
isPrivateKeyEmpty()397     public boolean isPrivateKeyEmpty()
398     {
399         byte[] secKeyData = secret.getSecretKeyData();
400 
401         return (secKeyData == null || secKeyData.length < 1);
402     }
403 
404     /**
405      * return the algorithm the key is encrypted with.
406      *
407      * @return the algorithm used to encrypt the secret key.
408      */
getKeyEncryptionAlgorithm()409     public int getKeyEncryptionAlgorithm()
410     {
411         return secret.getEncAlgorithm();
412     }
413 
414     /**
415      * Return the keyID of the public key associated with this key.
416      *
417      * @return the keyID associated with this key.
418      */
getKeyID()419     public long getKeyID()
420     {
421         return pub.getKeyID();
422     }
423 
424     /**
425      * Return the S2K usage associated with this key.
426      *
427      * @return the key's S2K usage
428      */
getS2KUsage()429     public int getS2KUsage()
430     {
431         return secret.getS2KUsage();
432     }
433 
434     /**
435      * Return the S2K used to process this key
436      *
437      * @return the key's S2K, null if one is not present.
438      */
getS2K()439     public S2K getS2K()
440     {
441         return secret.getS2K();
442     }
443 
444     /**
445      * Return the public key associated with this key.
446      *
447      * @return the public key for this key.
448      */
getPublicKey()449     public PGPPublicKey getPublicKey()
450     {
451         return pub;
452     }
453 
454     /**
455      * Return any userIDs associated with the key.
456      *
457      * @return an iterator of Strings.
458      */
getUserIDs()459     public Iterator<String> getUserIDs()
460     {
461         return pub.getUserIDs();
462     }
463 
464     /**
465      * Return any user attribute vectors associated with the key.
466      *
467      * @return an iterator of PGPUserAttributeSubpacketVector.
468      */
getUserAttributes()469     public Iterator<PGPUserAttributeSubpacketVector> getUserAttributes()
470     {
471         return pub.getUserAttributes();
472     }
473 
extractKeyData( PBESecretKeyDecryptor decryptorFactory)474     private byte[] extractKeyData(
475         PBESecretKeyDecryptor decryptorFactory)
476         throws PGPException
477     {
478         byte[] encData = secret.getSecretKeyData();
479         byte[] data = null;
480 
481         if (secret.getEncAlgorithm() != SymmetricKeyAlgorithmTags.NULL)
482         {
483             try
484             {
485                 if (secret.getPublicKeyPacket().getVersion() == 4)
486                 {
487                     byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K());
488 
489                     data = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, secret.getIV(), encData, 0, encData.length);
490 
491                     boolean useSHA1 = secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1;
492                     byte[] check = checksum(useSHA1 ? decryptorFactory.getChecksumCalculator(HashAlgorithmTags.SHA1) : null, data, (useSHA1) ? data.length - 20 : data.length - 2);
493 
494                     for (int i = 0; i != check.length; i++)
495                     {
496                         if (check[i] != data[data.length - check.length + i])
497                         {
498                             throw new PGPException("checksum mismatch at " + i + " of " + check.length);
499                         }
500                     }
501                 }
502                 else // version 2 or 3, RSA only.
503                 {
504                     byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K());
505 
506                     data = new byte[encData.length];
507 
508                     byte[] iv = new byte[secret.getIV().length];
509 
510                     System.arraycopy(secret.getIV(), 0, iv, 0, iv.length);
511 
512                     //
513                     // read in the four numbers
514                     //
515                     int pos = 0;
516 
517                     for (int i = 0; i != 4; i++)
518                     {
519                         int encLen = ((((encData[pos] & 0xff) << 8) | (encData[pos + 1] & 0xff)) + 7) / 8;
520 
521                         data[pos] = encData[pos];
522                         data[pos + 1] = encData[pos + 1];
523 
524                         if (encLen > (encData.length - (pos + 2)))
525                         {
526                             throw new PGPException("out of range encLen found in encData");
527                         }
528                         byte[] tmp = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, iv, encData, pos + 2, encLen);
529                         System.arraycopy(tmp, 0, data, pos + 2, tmp.length);
530                         pos += 2 + encLen;
531 
532                         if (i != 3)
533                         {
534                             System.arraycopy(encData, pos - iv.length, iv, 0, iv.length);
535                         }
536                     }
537 
538                     //
539                     // verify and copy checksum
540                     //
541 
542                     data[pos] = encData[pos];
543                     data[pos + 1] = encData[pos + 1];
544 
545                     int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff);
546                     int calcCs = 0;
547                     for (int j = 0; j < data.length - 2; j++)
548                     {
549                         calcCs += data[j] & 0xff;
550                     }
551 
552                     calcCs &= 0xffff;
553                     if (calcCs != cs)
554                     {
555                         throw new PGPException("checksum mismatch: passphrase wrong, expected "
556                             + Integer.toHexString(cs)
557                             + " found " + Integer.toHexString(calcCs));
558                     }
559                 }
560             }
561             catch (PGPException e)
562             {
563                 throw e;
564             }
565             catch (Exception e)
566             {
567                 throw new PGPException("Exception decrypting key", e);
568             }
569         }
570         else
571         {
572             data = encData;
573         }
574 
575         return data;
576     }
577 
578     /**
579      * Extract a PGPPrivate key from the SecretKey's encrypted contents.
580      *
581      * @param decryptorFactory factory to use to generate a decryptor for the passed in secretKey.
582      * @return PGPPrivateKey  the unencrypted private key.
583      * @throws PGPException on failure.
584      */
extractKeyPair( PBESecretKeyDecryptor decryptorFactory)585     public PGPKeyPair extractKeyPair(
586         PBESecretKeyDecryptor decryptorFactory)
587         throws PGPException
588     {
589         return new PGPKeyPair(this.getPublicKey(), this.extractPrivateKey(decryptorFactory));
590     }
591 
592     /**
593      * Extract a PGPPrivate key from the SecretKey's encrypted contents.
594      *
595      * @param decryptorFactory factory to use to generate a decryptor for the passed in secretKey.
596      * @return PGPPrivateKey  the unencrypted private key.
597      * @throws PGPException on failure.
598      */
extractPrivateKey( PBESecretKeyDecryptor decryptorFactory)599     public PGPPrivateKey extractPrivateKey(
600         PBESecretKeyDecryptor decryptorFactory)
601         throws PGPException
602     {
603         if (isPrivateKeyEmpty())
604         {
605             return null;
606         }
607 
608         PublicKeyPacket pubPk = secret.getPublicKeyPacket();
609 
610         try
611         {
612             byte[] data = extractKeyData(decryptorFactory);
613             BCPGInputStream in = new BCPGInputStream(new ByteArrayInputStream(data));
614 
615 
616             switch (pubPk.getAlgorithm())
617             {
618             case PGPPublicKey.RSA_ENCRYPT:
619             case PGPPublicKey.RSA_GENERAL:
620             case PGPPublicKey.RSA_SIGN:
621                 RSASecretBCPGKey rsaPriv = new RSASecretBCPGKey(in);
622 
623                 return new PGPPrivateKey(this.getKeyID(), pubPk, rsaPriv);
624             case PGPPublicKey.DSA:
625                 DSASecretBCPGKey dsaPriv = new DSASecretBCPGKey(in);
626 
627                 return new PGPPrivateKey(this.getKeyID(), pubPk, dsaPriv);
628             case PGPPublicKey.ELGAMAL_ENCRYPT:
629             case PGPPublicKey.ELGAMAL_GENERAL:
630                 ElGamalSecretBCPGKey elPriv = new ElGamalSecretBCPGKey(in);
631 
632                 return new PGPPrivateKey(this.getKeyID(), pubPk, elPriv);
633             case PGPPublicKey.ECDH:
634             case PGPPublicKey.ECDSA:
635                 ECSecretBCPGKey ecPriv = new ECSecretBCPGKey(in);
636 
637                 return new PGPPrivateKey(this.getKeyID(), pubPk, ecPriv);
638             case PGPPublicKey.EDDSA:
639                 EdSecretBCPGKey edPriv = new EdSecretBCPGKey(in);
640 
641                 return new PGPPrivateKey(this.getKeyID(), pubPk, edPriv);
642             default:
643                 throw new PGPException("unknown public key algorithm encountered");
644             }
645         }
646         catch (PGPException e)
647         {
648             throw e;
649         }
650         catch (Exception e)
651         {
652             throw new PGPException("Exception constructing key", e);
653         }
654     }
655 
checksum(PGPDigestCalculator digCalc, byte[] bytes, int length)656     private static byte[] checksum(PGPDigestCalculator digCalc, byte[] bytes, int length)
657         throws PGPException
658     {
659         if (digCalc != null)
660         {
661             OutputStream dOut = digCalc.getOutputStream();
662 
663             try
664             {
665                 dOut.write(bytes, 0, length);
666 
667                 dOut.close();
668             }
669             catch (Exception e)
670             {
671                 throw new PGPException("checksum digest calculation failed: " + e.getMessage(), e);
672             }
673             return digCalc.getDigest();
674         }
675         else
676         {
677             int checksum = 0;
678 
679             for (int i = 0; i != length; i++)
680             {
681                 checksum += bytes[i] & 0xff;
682             }
683 
684             byte[] check = new byte[2];
685 
686             check[0] = (byte)(checksum >> 8);
687             check[1] = (byte)checksum;
688 
689             return check;
690         }
691     }
692 
getEncoded()693     public byte[] getEncoded()
694         throws IOException
695     {
696         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
697 
698         this.encode(bOut);
699 
700         return bOut.toByteArray();
701     }
702 
encode( OutputStream outStream)703     public void encode(
704         OutputStream outStream)
705         throws IOException
706     {
707         BCPGOutputStream out;
708 
709         if (outStream instanceof BCPGOutputStream)
710         {
711             out = (BCPGOutputStream)outStream;
712         }
713         else
714         {
715             out = new BCPGOutputStream(outStream);
716         }
717 
718         out.writePacket(secret);
719         if (pub.trustPk != null)
720         {
721             out.writePacket(pub.trustPk);
722         }
723 
724         if (pub.subSigs == null)        // is not a sub key
725         {
726             for (int i = 0; i != pub.keySigs.size(); i++)
727             {
728                 ((PGPSignature)pub.keySigs.get(i)).encode(out);
729             }
730 
731             for (int i = 0; i != pub.ids.size(); i++)
732             {
733                 if (pub.ids.get(i) instanceof UserIDPacket)
734                 {
735                     UserIDPacket id = (UserIDPacket)pub.ids.get(i);
736 
737                     out.writePacket(id);
738                 }
739                 else
740                 {
741                     PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector)pub.ids.get(i);
742 
743                     out.writePacket(new UserAttributePacket(v.toSubpacketArray()));
744                 }
745 
746                 if (pub.idTrusts.get(i) != null)
747                 {
748                     out.writePacket((ContainedPacket)pub.idTrusts.get(i));
749                 }
750 
751                 List sigs = (ArrayList)pub.idSigs.get(i);
752 
753                 for (int j = 0; j != sigs.size(); j++)
754                 {
755                     ((PGPSignature)sigs.get(j)).encode(out);
756                 }
757             }
758         }
759         else
760         {
761             for (int j = 0; j != pub.subSigs.size(); j++)
762             {
763                 ((PGPSignature)pub.subSigs.get(j)).encode(out);
764             }
765         }
766     }
767 
768     /**
769      * Return a copy of the passed in secret key, encrypted using a new
770      * password and the passed in algorithm.
771      *
772      * @param key             the PGPSecretKey to be copied.
773      * @param oldKeyDecryptor the current decryptor based on the current password for key.
774      * @param newKeyEncryptor a new encryptor based on a new password for encrypting the secret key material.
775      */
copyWithNewPassword( PGPSecretKey key, PBESecretKeyDecryptor oldKeyDecryptor, PBESecretKeyEncryptor newKeyEncryptor)776     public static PGPSecretKey copyWithNewPassword(
777         PGPSecretKey key,
778         PBESecretKeyDecryptor oldKeyDecryptor,
779         PBESecretKeyEncryptor newKeyEncryptor)
780         throws PGPException
781     {
782         if (key.isPrivateKeyEmpty())
783         {
784             throw new PGPException("no private key in this SecretKey - public key present only.");
785         }
786 
787         byte[] rawKeyData = key.extractKeyData(oldKeyDecryptor);
788         int s2kUsage = key.secret.getS2KUsage();
789         byte[] iv = null;
790         S2K s2k = null;
791         byte[] keyData;
792         int newEncAlgorithm = SymmetricKeyAlgorithmTags.NULL;
793 
794         if (newKeyEncryptor == null || newKeyEncryptor.getAlgorithm() == SymmetricKeyAlgorithmTags.NULL)
795         {
796             s2kUsage = SecretKeyPacket.USAGE_NONE;
797             if (key.secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1)   // SHA-1 hash, need to rewrite checksum
798             {
799                 keyData = new byte[rawKeyData.length - 18];
800 
801                 System.arraycopy(rawKeyData, 0, keyData, 0, keyData.length - 2);
802 
803                 byte[] check = checksum(null, keyData, keyData.length - 2);
804 
805                 keyData[keyData.length - 2] = check[0];
806                 keyData[keyData.length - 1] = check[1];
807             }
808             else
809             {
810                 keyData = rawKeyData;
811             }
812         }
813         else
814         {
815             if (s2kUsage == SecretKeyPacket.USAGE_NONE)
816             {
817                 s2kUsage = SecretKeyPacket.USAGE_CHECKSUM;
818             }
819             if (key.secret.getPublicKeyPacket().getVersion() < 4)
820             {
821                 // Version 2 or 3 - RSA Keys only
822 
823                 byte[] encKey = newKeyEncryptor.getKey();
824                 keyData = new byte[rawKeyData.length];
825 
826                 if (newKeyEncryptor.getHashAlgorithm() != HashAlgorithmTags.MD5)
827                 {
828                     throw new PGPException("MD5 Digest Calculator required for version 3 key encryptor.");
829                 }
830 
831                 //
832                 // process 4 numbers
833                 //
834                 int pos = 0;
835                 for (int i = 0; i != 4; i++)
836                 {
837                     int encLen = ((((rawKeyData[pos] & 0xff) << 8) | (rawKeyData[pos + 1] & 0xff)) + 7) / 8;
838 
839                     keyData[pos] = rawKeyData[pos];
840                     keyData[pos + 1] = rawKeyData[pos + 1];
841 
842                     if (encLen > (rawKeyData.length - (pos + 2)))
843                     {
844                         throw new PGPException("out of range encLen found in rawKeyData");
845                     }
846 
847                     byte[] tmp;
848                     if (i == 0)
849                     {
850                         tmp = newKeyEncryptor.encryptKeyData(encKey, rawKeyData, pos + 2, encLen);
851                         iv = newKeyEncryptor.getCipherIV();
852 
853                     }
854                     else
855                     {
856                         byte[] tmpIv = new byte[iv.length];
857 
858                         System.arraycopy(keyData, pos - iv.length, tmpIv, 0, tmpIv.length);
859                         tmp = newKeyEncryptor.encryptKeyData(encKey, tmpIv, rawKeyData, pos + 2, encLen);
860                     }
861 
862                     System.arraycopy(tmp, 0, keyData, pos + 2, tmp.length);
863                     pos += 2 + encLen;
864                 }
865 
866                 //
867                 // copy in checksum.
868                 //
869                 keyData[pos] = rawKeyData[pos];
870                 keyData[pos + 1] = rawKeyData[pos + 1];
871 
872                 s2k = newKeyEncryptor.getS2K();
873                 newEncAlgorithm = newKeyEncryptor.getAlgorithm();
874             }
875             else
876             {
877                 keyData = newKeyEncryptor.encryptKeyData(rawKeyData, 0, rawKeyData.length);
878 
879                 iv = newKeyEncryptor.getCipherIV();
880 
881                 s2k = newKeyEncryptor.getS2K();
882 
883                 newEncAlgorithm = newKeyEncryptor.getAlgorithm();
884             }
885         }
886 
887         SecretKeyPacket secret;
888         if (key.secret instanceof SecretSubkeyPacket)
889         {
890             secret = new SecretSubkeyPacket(key.secret.getPublicKeyPacket(),
891                 newEncAlgorithm, s2kUsage, s2k, iv, keyData);
892         }
893         else
894         {
895             secret = new SecretKeyPacket(key.secret.getPublicKeyPacket(),
896                 newEncAlgorithm, s2kUsage, s2k, iv, keyData);
897         }
898 
899         return new PGPSecretKey(secret, key.pub);
900     }
901 
902     /**
903      * Replace the passed the public key on the passed in secret key.
904      *
905      * @param secretKey secret key to change
906      * @param publicKey new public key.
907      * @return a new secret key.
908      * @throws IllegalArgumentException if keyIDs do not match.
909      */
replacePublicKey(PGPSecretKey secretKey, PGPPublicKey publicKey)910     public static PGPSecretKey replacePublicKey(PGPSecretKey secretKey, PGPPublicKey publicKey)
911     {
912         if (publicKey.getKeyID() != secretKey.getKeyID())
913         {
914             throw new IllegalArgumentException("keyIDs do not match");
915         }
916 
917         return new PGPSecretKey(secretKey.secret, publicKey);
918     }
919 
920     /**
921      * Parse a secret key from one of the GPG S expression keys associating it with the passed in public key.
922      *
923      * @return a secret key object.
924      * @deprecated use org.bouncycastle.gpg.SExprParser - it will also allow you to verify the protection checksum if it is available.
925      */
parseSecretKeyFromSExpr(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, PGPPublicKey pubKey)926     public static PGPSecretKey parseSecretKeyFromSExpr(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, PGPPublicKey pubKey)
927         throws IOException, PGPException
928     {
929         return new SExprParser(null).parseSecretKey(inputStream, keyProtectionRemoverFactory, pubKey);
930     }
931 
932     /**
933      * Parse a secret key from one of the GPG S expression keys.
934      *
935      * @return a secret key object.
936      * @deprecated use org.bouncycastle.gpg.SExprParser - it will also allow you to verify the protection checksum if it is available.
937      */
parseSecretKeyFromSExpr(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, KeyFingerPrintCalculator fingerPrintCalculator)938     public static PGPSecretKey parseSecretKeyFromSExpr(InputStream inputStream, PBEProtectionRemoverFactory keyProtectionRemoverFactory, KeyFingerPrintCalculator fingerPrintCalculator)
939         throws IOException, PGPException
940     {
941         return new SExprParser(null).parseSecretKey(inputStream, keyProtectionRemoverFactory, fingerPrintCalculator);
942     }
943 }
944