001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org> 002// 003// SPDX-License-Identifier: Apache-2.0 004 005package org.pgpainless.signature.subpackets; 006 007import java.util.ArrayList; 008import java.util.Arrays; 009import java.util.Date; 010import java.util.LinkedHashSet; 011import java.util.List; 012import java.util.Set; 013 014import javax.annotation.Nonnull; 015import javax.annotation.Nullable; 016 017import org.bouncycastle.bcpg.sig.Exportable; 018import org.bouncycastle.bcpg.sig.Features; 019import org.bouncycastle.bcpg.sig.IntendedRecipientFingerprint; 020import org.bouncycastle.bcpg.sig.IssuerFingerprint; 021import org.bouncycastle.bcpg.sig.IssuerKeyID; 022import org.bouncycastle.bcpg.sig.KeyExpirationTime; 023import org.bouncycastle.bcpg.sig.KeyFlags; 024import org.bouncycastle.bcpg.sig.NotationData; 025import org.bouncycastle.bcpg.sig.PreferredAlgorithms; 026import org.bouncycastle.bcpg.sig.PrimaryUserID; 027import org.bouncycastle.bcpg.sig.Revocable; 028import org.bouncycastle.bcpg.sig.RevocationKey; 029import org.bouncycastle.bcpg.sig.RevocationReason; 030import org.bouncycastle.bcpg.sig.SignatureCreationTime; 031import org.bouncycastle.bcpg.sig.SignatureExpirationTime; 032import org.bouncycastle.bcpg.sig.SignatureTarget; 033import org.bouncycastle.bcpg.sig.SignerUserID; 034import org.bouncycastle.bcpg.sig.TrustSignature; 035import org.bouncycastle.openpgp.PGPException; 036import org.bouncycastle.openpgp.PGPPublicKey; 037import org.bouncycastle.openpgp.PGPSignature; 038import org.bouncycastle.openpgp.PGPSignatureList; 039import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; 040import org.bouncycastle.util.encoders.Hex; 041import org.pgpainless.algorithm.CompressionAlgorithm; 042import org.pgpainless.algorithm.Feature; 043import org.pgpainless.algorithm.HashAlgorithm; 044import org.pgpainless.algorithm.KeyFlag; 045import org.pgpainless.algorithm.PublicKeyAlgorithm; 046import org.pgpainless.algorithm.SignatureSubpacket; 047import org.pgpainless.algorithm.SymmetricKeyAlgorithm; 048import org.pgpainless.key.OpenPgpFingerprint; 049import org.pgpainless.key.OpenPgpV4Fingerprint; 050import org.pgpainless.key.generation.type.KeyType; 051import org.pgpainless.signature.SignatureUtils; 052 053/** 054 * Utility class to access signature subpackets from signatures. 055 * 056 * Since rfc4880 is not always clear about where a signature subpacket can be located (hashed/unhashed area), 057 * this class makes some educated guesses as to where the subpacket may be found when necessary. 058 */ 059public final class SignatureSubpacketsUtil { 060 061 private SignatureSubpacketsUtil() { 062 063 } 064 065 /** 066 * Return the issuer-fingerprint subpacket of the signature. 067 * Since this packet is self-authenticating, we expect it to be in the unhashed area, 068 * however as it cannot hurt we search for it in the hashed area first. 069 * 070 * @param signature signature 071 * @return issuer fingerprint or null 072 */ 073 public static IssuerFingerprint getIssuerFingerprint(PGPSignature signature) { 074 return hashedOrUnhashed(signature, SignatureSubpacket.issuerFingerprint); 075 } 076 077 /** 078 * Return the {@link IssuerFingerprint} subpacket of the signature into a {@link org.pgpainless.key.OpenPgpFingerprint}. 079 * If no v4 issuer fingerprint is present in the signature, return null. 080 * 081 * @param signature signature 082 * @return v4 fingerprint of the issuer, or null 083 */ 084 public static OpenPgpFingerprint getIssuerFingerprintAsOpenPgpFingerprint(PGPSignature signature) { 085 IssuerFingerprint subpacket = getIssuerFingerprint(signature); 086 if (subpacket == null) { 087 return null; 088 } 089 090 OpenPgpFingerprint fingerprint = null; 091 if (subpacket.getKeyVersion() == 4) { 092 fingerprint = new OpenPgpV4Fingerprint(Hex.encode(subpacket.getFingerprint())); 093 } 094 095 return fingerprint; 096 } 097 098 /** 099 * Return the issuer key-id subpacket of the signature. 100 * Since this packet is self-authenticating, we expect it to be in the unhashed area, 101 * however as it cannot hurt we search for it in the hashed area first. 102 * 103 * @param signature signature 104 * @return issuer key-id or null 105 */ 106 public static IssuerKeyID getIssuerKeyId(PGPSignature signature) { 107 return hashedOrUnhashed(signature, SignatureSubpacket.issuerKeyId); 108 } 109 110 /** 111 * Inspect the given signature's {@link IssuerKeyID} packet to determine the issuer key-id. 112 * If no such packet is present, return null. 113 * 114 * @param signature signature 115 * @return issuer key-id as {@link Long} 116 */ 117 public static Long getIssuerKeyIdAsLong(PGPSignature signature) { 118 IssuerKeyID keyID = getIssuerKeyId(signature); 119 if (keyID == null) { 120 return null; 121 } 122 return keyID.getKeyID(); 123 } 124 125 /** 126 * Return the revocation reason subpacket of the signature. 127 * Since this packet is rather important for revocations, we only search for it in the 128 * hashed area of the signature. 129 * 130 * @param signature signature 131 * @return revocation reason 132 */ 133 public static RevocationReason getRevocationReason(PGPSignature signature) { 134 return hashed(signature, SignatureSubpacket.revocationReason); 135 } 136 137 /** 138 * Return the signature creation time subpacket. 139 * Since this packet is rather important, we only search for it in the hashed area 140 * of the signature. 141 * 142 * @param signature signature 143 * @return signature creation time subpacket 144 */ 145 public static SignatureCreationTime getSignatureCreationTime(PGPSignature signature) { 146 return hashed(signature, SignatureSubpacket.signatureCreationTime); 147 } 148 149 /** 150 * Return the signature expiration time subpacket of the signature. 151 * Since this packet is rather important, we only search for it in the hashed area of the signature. 152 * 153 * @param signature signature 154 * @return signature expiration time 155 */ 156 public static SignatureExpirationTime getSignatureExpirationTime(PGPSignature signature) { 157 return hashed(signature, SignatureSubpacket.signatureExpirationTime); 158 } 159 160 /** 161 * Return the signatures' expiration time as a date. 162 * The expiration date is computed by adding the expiration time to the signature creation date. 163 * If the signature has no expiration time subpacket, or the expiration time is set to '0', this message returns null. 164 * 165 * @param signature signature 166 * @return expiration time as date 167 */ 168 public static Date getSignatureExpirationTimeAsDate(PGPSignature signature) { 169 SignatureExpirationTime subpacket = getSignatureExpirationTime(signature); 170 if (subpacket == null) { 171 return null; 172 } 173 return SignatureUtils.datePlusSeconds(signature.getCreationTime(), subpacket.getTime()); 174 } 175 176 /** 177 * Return the key expiration time subpacket of this signature. 178 * We only look for it in the hashed area of the signature. 179 * 180 * @param signature signature 181 * @return key expiration time 182 */ 183 public static KeyExpirationTime getKeyExpirationTime(PGPSignature signature) { 184 return hashed(signature, SignatureSubpacket.keyExpirationTime); 185 } 186 187 /** 188 * Return the signatures key-expiration time as a date. 189 * The expiration date is computed by adding the signatures' key-expiration time to the signing keys 190 * creation date. 191 * If the signature does not have a key-expiration time subpacket, or its value is '0', this method returns null. 192 * 193 * @param signature self-signature carrying the key-expiration time subpacket 194 * @param signingKey signature creation key 195 * @return key expiration time as date 196 */ 197 public static Date getKeyExpirationTimeAsDate(PGPSignature signature, PGPPublicKey signingKey) { 198 if (signature.getKeyID() != signingKey.getKeyID()) { 199 throw new IllegalArgumentException("Provided key (" + Long.toHexString(signingKey.getKeyID()) + ") did not create the signature (" + Long.toHexString(signature.getKeyID()) + ")"); 200 } 201 KeyExpirationTime subpacket = getKeyExpirationTime(signature); 202 if (subpacket == null) { 203 return null; 204 } 205 206 return SignatureUtils.datePlusSeconds(signingKey.getCreationTime(), subpacket.getTime()); 207 } 208 209 /** 210 * Calculate the duration in seconds until the key expires after creation. 211 * 212 * @param expirationDate new expiration date 213 * @param creationDate key creation time 214 * @return lifetime of the key in seconds 215 */ 216 public static long getKeyLifetimeInSeconds(@Nullable Date expirationDate, @Nonnull Date creationDate) { 217 long secondsToExpire = 0; // 0 means "no expiration" 218 if (expirationDate != null) { 219 if (creationDate.after(expirationDate)) { 220 throw new IllegalArgumentException("Key MUST NOT expire before being created. " + 221 "(creation: " + creationDate + ", expiration: " + expirationDate + ")"); 222 } 223 secondsToExpire = (expirationDate.getTime() - creationDate.getTime()) / 1000; 224 } 225 return secondsToExpire; 226 } 227 228 /** 229 * Return the revocable subpacket of this signature. 230 * We only look for it in the hashed area of the signature. 231 * 232 * @param signature signature 233 * @return revocable subpacket 234 */ 235 public static Revocable getRevocable(PGPSignature signature) { 236 return hashed(signature, SignatureSubpacket.revocable); 237 } 238 239 /** 240 * Return the symmetric algorithm preferences from the signatures hashed area. 241 * 242 * @param signature signature 243 * @return symm. algo. prefs 244 */ 245 public static PreferredAlgorithms getPreferredSymmetricAlgorithms(PGPSignature signature) { 246 return hashed(signature, SignatureSubpacket.preferredSymmetricAlgorithms); 247 } 248 249 /** 250 * Return the preferred {@link SymmetricKeyAlgorithm SymmetricKeyAlgorithms} as present in the signature. 251 * If no preference is given with regard to symmetric encryption algorithms, return an empty set. 252 * 253 * In any case, the resulting set is ordered by occurrence ({@link LinkedHashSet}). 254 * @param signature signature 255 * @return ordered set of symmetric key algorithm preferences 256 */ 257 public static Set<SymmetricKeyAlgorithm> parsePreferredSymmetricKeyAlgorithms(PGPSignature signature) { 258 Set<SymmetricKeyAlgorithm> algorithms = new LinkedHashSet<>(); 259 PreferredAlgorithms preferences = getPreferredSymmetricAlgorithms(signature); 260 if (preferences != null) { 261 for (int code : preferences.getPreferences()) { 262 algorithms.add(SymmetricKeyAlgorithm.fromId(code)); 263 } 264 } 265 return algorithms; 266 } 267 268 /** 269 * Return the hash algorithm preferences from the signatures hashed area. 270 * 271 * @param signature signature 272 * @return hash algo prefs 273 */ 274 public static PreferredAlgorithms getPreferredHashAlgorithms(PGPSignature signature) { 275 return hashed(signature, SignatureSubpacket.preferredHashAlgorithms); 276 } 277 278 /** 279 * Return the preferred {@link HashAlgorithm HashAlgorithms} as present in the signature. 280 * If no preference is given with regard to hash algorithms, return an empty set. 281 * 282 * In any case, the resulting set is ordered by occurrence ({@link LinkedHashSet}). 283 * @param signature signature 284 * @return ordered set of hash algorithm preferences 285 */ 286 public static Set<HashAlgorithm> parsePreferredHashAlgorithms(PGPSignature signature) { 287 Set<HashAlgorithm> algorithms = new LinkedHashSet<>(); 288 PreferredAlgorithms preferences = getPreferredHashAlgorithms(signature); 289 if (preferences != null) { 290 for (int code : preferences.getPreferences()) { 291 algorithms.add(HashAlgorithm.fromId(code)); 292 } 293 } 294 return algorithms; 295 } 296 297 /** 298 * Return the compression algorithm preferences from the signatures hashed area. 299 * 300 * @param signature signature 301 * @return compression algo prefs 302 */ 303 public static PreferredAlgorithms getPreferredCompressionAlgorithms(PGPSignature signature) { 304 return hashed(signature, SignatureSubpacket.preferredCompressionAlgorithms); 305 } 306 307 /** 308 * Return the preferred {@link CompressionAlgorithm CompressionAlgorithms} as present in the signature. 309 * If no preference is given with regard to compression algorithms, return an empty set. 310 * 311 * In any case, the resulting set is ordered by occurrence ({@link LinkedHashSet}). 312 * @param signature signature 313 * @return ordered set of compression algorithm preferences 314 */ 315 public static Set<CompressionAlgorithm> parsePreferredCompressionAlgorithms(PGPSignature signature) { 316 Set<CompressionAlgorithm> algorithms = new LinkedHashSet<>(); 317 PreferredAlgorithms preferences = getPreferredCompressionAlgorithms(signature); 318 if (preferences != null) { 319 for (int code : preferences.getPreferences()) { 320 algorithms.add(CompressionAlgorithm.fromId(code)); 321 } 322 } 323 return algorithms; 324 } 325 326 /** 327 * Return the primary user-id subpacket from the signatures hashed area. 328 * 329 * @param signature signature 330 * @return primary user id 331 */ 332 public static PrimaryUserID getPrimaryUserId(PGPSignature signature) { 333 return hashed(signature, SignatureSubpacket.primaryUserId); 334 } 335 336 /** 337 * Return the key flags subpacket from the signatures hashed area. 338 * 339 * @param signature signature 340 * @return key flags 341 */ 342 public static KeyFlags getKeyFlags(PGPSignature signature) { 343 return hashed(signature, SignatureSubpacket.keyFlags); 344 } 345 346 /** 347 * Return a list of key flags carried by the signature. 348 * If the signature is null, or has no {@link KeyFlags} subpacket, return null. 349 * 350 * @param signature signature 351 * @return list of key flags 352 */ 353 public static List<KeyFlag> parseKeyFlags(@Nullable PGPSignature signature) { 354 if (signature == null) { 355 return null; 356 } 357 KeyFlags keyFlags = getKeyFlags(signature); 358 if (keyFlags == null) { 359 return null; 360 } 361 return KeyFlag.fromBitmask(keyFlags.getFlags()); 362 } 363 364 /** 365 * Return the features subpacket from the signatures hashed area. 366 * 367 * @param signature signature 368 * @return features subpacket 369 */ 370 public static Features getFeatures(PGPSignature signature) { 371 return hashed(signature, SignatureSubpacket.features); 372 } 373 374 /** 375 * Parse out the features subpacket of a signature. 376 * If the signature has no features subpacket, return null. 377 * Otherwise, return the features as a feature set. 378 * 379 * @param signature signature 380 * @return features as set 381 */ 382 public static @Nullable Set<Feature> parseFeatures(PGPSignature signature) { 383 Features features = getFeatures(signature); 384 if (features == null) { 385 return null; 386 } 387 return new LinkedHashSet<>(Feature.fromBitmask(features.getData()[0])); 388 } 389 390 /** 391 * Return the signature target subpacket from the signature. 392 * We search for this subpacket in the hashed and unhashed area (in this order). 393 * 394 * @param signature signature 395 * @return signature target 396 */ 397 public static SignatureTarget getSignatureTarget(PGPSignature signature) { 398 return hashedOrUnhashed(signature, SignatureSubpacket.signatureTarget); 399 } 400 401 /** 402 * Return the notation data subpackets from the signatures hashed area. 403 * 404 * @param signature signature 405 * @return hashed notations 406 */ 407 public static List<NotationData> getHashedNotationData(PGPSignature signature) { 408 NotationData[] notations = signature.getHashedSubPackets().getNotationDataOccurrences(); 409 return Arrays.asList(notations); 410 } 411 412 /** 413 * Return a list of all {@link NotationData} objects from the hashed area of the signature that have a 414 * notation name equal to the given notationName argument. 415 * 416 * @param signature signature 417 * @param notationName notation name 418 * @return list of matching notation data objects 419 */ 420 public static List<NotationData> getHashedNotationData(PGPSignature signature, String notationName) { 421 List<NotationData> allNotations = getHashedNotationData(signature); 422 List<NotationData> withName = new ArrayList<>(); 423 for (NotationData data : allNotations) { 424 if (data.getNotationName().equals(notationName)) { 425 withName.add(data); 426 } 427 } 428 return withName; 429 } 430 431 /** 432 * Return the notation data subpackets from the signatures unhashed area. 433 * 434 * @param signature signture 435 * @return unhashed notations 436 */ 437 public static List<NotationData> getUnhashedNotationData(PGPSignature signature) { 438 NotationData[] notations = signature.getUnhashedSubPackets().getNotationDataOccurrences(); 439 return Arrays.asList(notations); 440 } 441 442 /** 443 * Return a list of all {@link NotationData} objects from the unhashed area of the signature that have a 444 * notation name equal to the given notationName argument. 445 * 446 * @param signature signature 447 * @param notationName notation name 448 * @return list of matching notation data objects 449 */ 450 public static List<NotationData> getUnhashedNotationData(PGPSignature signature, String notationName) { 451 List<NotationData> allNotations = getUnhashedNotationData(signature); 452 List<NotationData> withName = new ArrayList<>(); 453 for (NotationData data : allNotations) { 454 if (data.getNotationName().equals(notationName)) { 455 withName.add(data); 456 } 457 } 458 return withName; 459 } 460 461 /** 462 * Return the revocation key subpacket from the signatures hashed area. 463 * 464 * @param signature signature 465 * @return revocation key 466 */ 467 public static RevocationKey getRevocationKey(PGPSignature signature) { 468 return hashed(signature, SignatureSubpacket.revocationKey); 469 } 470 471 /** 472 * Return the signers user-id from the hashed area of the signature. 473 * TODO: Can this subpacket also be found in the unhashed area? 474 * 475 * @param signature signature 476 * @return signers user-id 477 */ 478 public static SignerUserID getSignerUserID(PGPSignature signature) { 479 return hashed(signature, SignatureSubpacket.signerUserId); 480 } 481 482 /** 483 * Return the intended recipients fingerprint subpackets from the hashed area of this signature. 484 * 485 * @param signature signature 486 * @return intended recipient fingerprint subpackets 487 */ 488 public static List<IntendedRecipientFingerprint> getIntendedRecipientFingerprints(PGPSignature signature) { 489 org.bouncycastle.bcpg.SignatureSubpacket[] subpackets = signature.getHashedSubPackets().getSubpackets(SignatureSubpacket.intendedRecipientFingerprint.getCode()); 490 List<IntendedRecipientFingerprint> intendedRecipients = new ArrayList<>(subpackets.length); 491 for (org.bouncycastle.bcpg.SignatureSubpacket subpacket : subpackets) { 492 intendedRecipients.add((IntendedRecipientFingerprint) subpacket); 493 } 494 return intendedRecipients; 495 } 496 497 /** 498 * Return the embedded signature subpacket from the signatures hashed area. 499 * 500 * @param signature signature 501 * @return embedded signature 502 */ 503 public static PGPSignatureList getEmbeddedSignature(PGPSignature signature) throws PGPException { 504 PGPSignatureList hashed = signature.getHashedSubPackets().getEmbeddedSignatures(); 505 if (!hashed.isEmpty()) { 506 return hashed; 507 } 508 return signature.getUnhashedSubPackets().getEmbeddedSignatures(); 509 } 510 511 /** 512 * Return the signatures exportable certification subpacket from the hashed area. 513 * 514 * @param signature signature 515 * @return exportable certification subpacket 516 */ 517 public static Exportable getExportableCertification(PGPSignature signature) { 518 return hashed(signature, SignatureSubpacket.exportableCertification); 519 } 520 521 /** 522 * Return the trust signature packet from the signatures hashed area. 523 * 524 * @param signature signature 525 * @return trust signature subpacket 526 */ 527 public static TrustSignature getTrustSignature(PGPSignature signature) { 528 return hashed(signature, SignatureSubpacket.trustSignature); 529 } 530 531 /** 532 * Select a list of all signature subpackets of the given type, which are present in the hashed area of 533 * the given signature. 534 * 535 * @param signature signature 536 * @param type subpacket type 537 * @param <P> generic subpacket type 538 * @return list of subpackets from the hashed area 539 */ 540 private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P hashed(PGPSignature signature, SignatureSubpacket type) { 541 return getSignatureSubpacket(signature.getHashedSubPackets(), type); 542 } 543 544 /** 545 * Select a list of all signature subpackets of the given type, which are present in the unhashed area of 546 * the given signature. 547 * 548 * @param signature signature 549 * @param type subpacket type 550 * @param <P> generic subpacket type 551 * @return list of subpackets from the unhashed area 552 */ 553 private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P unhashed(PGPSignature signature, SignatureSubpacket type) { 554 return getSignatureSubpacket(signature.getUnhashedSubPackets(), type); 555 } 556 557 /** 558 * Select a list of all signature subpackets of the given type, which are present in either the hashed 559 * or the unhashed area of the given signature. 560 * 561 * @param signature signature 562 * @param type subpacket type 563 * @param <P> generic subpacket type 564 * @return list of subpackets from the hashed/unhashed area 565 */ 566 private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P hashedOrUnhashed(PGPSignature signature, SignatureSubpacket type) { 567 P hashedSubpacket = hashed(signature, type); 568 return hashedSubpacket != null ? hashedSubpacket : unhashed(signature, type); 569 } 570 571 /** 572 * Return the last occurence of a subpacket type in the given signature subpacket vector. 573 * 574 * @param vector subpacket vector (hashed/unhashed) 575 * @param type subpacket type 576 * @param <P> generic return type of the subpacket 577 * @return last occurrence of the subpacket in the vector 578 */ 579 public static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P getSignatureSubpacket(PGPSignatureSubpacketVector vector, SignatureSubpacket type) { 580 org.bouncycastle.bcpg.SignatureSubpacket[] allPackets = vector.getSubpackets(type.getCode()); 581 if (allPackets.length == 0) { 582 return null; 583 } 584 return (P) allPackets[allPackets.length - 1]; // return last 585 } 586 587 /** 588 * Make sure that the given key type can carry the given key flags. 589 * 590 * @param type key type 591 * @param flags key flags 592 */ 593 public static void assureKeyCanCarryFlags(KeyType type, KeyFlag... flags) { 594 final int mask = KeyFlag.toBitmask(flags); 595 596 if (!type.canCertify() && KeyFlag.hasKeyFlag(mask, KeyFlag.CERTIFY_OTHER)) { 597 throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag CERTIFY_OTHER."); 598 } 599 600 if (!type.canSign() && KeyFlag.hasKeyFlag(mask, KeyFlag.SIGN_DATA)) { 601 throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag SIGN_DATA."); 602 } 603 604 if (!type.canEncryptCommunication() && KeyFlag.hasKeyFlag(mask, KeyFlag.ENCRYPT_COMMS)) { 605 throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag ENCRYPT_COMMS."); 606 } 607 608 if (!type.canEncryptStorage() && KeyFlag.hasKeyFlag(mask, KeyFlag.ENCRYPT_STORAGE)) { 609 throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag ENCRYPT_STORAGE."); 610 } 611 612 if (!type.canAuthenticate() && KeyFlag.hasKeyFlag(mask, KeyFlag.AUTHENTICATION)) { 613 throw new IllegalArgumentException("KeyType " + type.getName() + " cannot carry key flag AUTHENTICATION."); 614 } 615 } 616 617 public static void assureKeyCanCarryFlags(PublicKeyAlgorithm algorithm, KeyFlag... flags) { 618 final int mask = KeyFlag.toBitmask(flags); 619 620 if (!algorithm.isSigningCapable() && KeyFlag.hasKeyFlag(mask, KeyFlag.CERTIFY_OTHER)) { 621 throw new IllegalArgumentException("Algorithm " + algorithm + " cannot be used with key flag CERTIFY_OTHER."); 622 } 623 624 if (!algorithm.isSigningCapable() && KeyFlag.hasKeyFlag(mask, KeyFlag.SIGN_DATA)) { 625 throw new IllegalArgumentException("Algorithm " + algorithm + " cannot be used with key flag SIGN_DATA."); 626 } 627 628 if (!algorithm.isEncryptionCapable() && KeyFlag.hasKeyFlag(mask, KeyFlag.ENCRYPT_COMMS)) { 629 throw new IllegalArgumentException("Algorithm " + algorithm + " cannot be used with key flag ENCRYPT_COMMS."); 630 } 631 632 if (!algorithm.isEncryptionCapable() && KeyFlag.hasKeyFlag(mask, KeyFlag.ENCRYPT_STORAGE)) { 633 throw new IllegalArgumentException("Algorithm " + algorithm + " cannot be used with key flag ENCRYPT_STORAGE."); 634 } 635 636 if (!algorithm.isSigningCapable() && KeyFlag.hasKeyFlag(mask, KeyFlag.AUTHENTICATION)) { 637 throw new IllegalArgumentException("Algorithm " + algorithm + " cannot be used with key flag AUTHENTICATION."); 638 } 639 } 640}