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