001: package org.bouncycastle.openpgp;
002:
003: import org.bouncycastle.bcpg.BCPGKey;
004: import org.bouncycastle.bcpg.BCPGOutputStream;
005: import org.bouncycastle.bcpg.ContainedPacket;
006: import org.bouncycastle.bcpg.DSAPublicBCPGKey;
007: import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
008: import org.bouncycastle.bcpg.MPInteger;
009: import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
010: import org.bouncycastle.bcpg.PublicKeyPacket;
011: import org.bouncycastle.bcpg.RSAPublicBCPGKey;
012: import org.bouncycastle.bcpg.TrustPacket;
013: import org.bouncycastle.bcpg.UserAttributePacket;
014: import org.bouncycastle.bcpg.UserIDPacket;
015: import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
016: import org.bouncycastle.jce.spec.ElGamalParameterSpec;
017: import org.bouncycastle.jce.spec.ElGamalPublicKeySpec;
018: import org.bouncycastle.util.Arrays;
019:
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.OutputStream;
023: import java.security.KeyFactory;
024: import java.security.MessageDigest;
025: import java.security.NoSuchAlgorithmException;
026: import java.security.NoSuchProviderException;
027: import java.security.PublicKey;
028: import java.security.interfaces.DSAParams;
029: import java.security.interfaces.DSAPublicKey;
030: import java.security.interfaces.RSAPublicKey;
031: import java.security.spec.DSAPublicKeySpec;
032: import java.security.spec.RSAPublicKeySpec;
033: import java.util.ArrayList;
034: import java.util.Collection;
035: import java.util.Date;
036: import java.util.Iterator;
037: import java.util.List;
038:
039: /**
040: * general class to handle a PGP public key object.
041: */
042: public class PGPPublicKey implements PublicKeyAlgorithmTags {
043: private static final int[] MASTER_KEY_CERTIFICATION_TYPES = new int[] {
044: PGPSignature.POSITIVE_CERTIFICATION,
045: PGPSignature.CASUAL_CERTIFICATION,
046: PGPSignature.NO_CERTIFICATION,
047: PGPSignature.DEFAULT_CERTIFICATION };
048:
049: PublicKeyPacket publicPk;
050: TrustPacket trustPk;
051: List keySigs = new ArrayList();
052: List ids = new ArrayList();
053: List idTrusts = new ArrayList();
054: List idSigs = new ArrayList();
055:
056: List subSigs = null;
057:
058: private long keyID;
059: private byte[] fingerprint;
060: private int keyStrength;
061:
062: private void init() throws IOException {
063: BCPGKey key = publicPk.getKey();
064:
065: if (publicPk.getVersion() <= 3) {
066: RSAPublicBCPGKey rK = (RSAPublicBCPGKey) key;
067:
068: this .keyID = rK.getModulus().longValue();
069:
070: try {
071: MessageDigest digest = MessageDigest.getInstance("MD5");
072:
073: byte[] bytes = new MPInteger(rK.getModulus())
074: .getEncoded();
075: digest.update(bytes, 2, bytes.length - 2);
076:
077: bytes = new MPInteger(rK.getPublicExponent())
078: .getEncoded();
079: digest.update(bytes, 2, bytes.length - 2);
080:
081: this .fingerprint = digest.digest();
082: } catch (NoSuchAlgorithmException e) {
083: throw new IOException("can't find MD5");
084: }
085:
086: this .keyStrength = rK.getModulus().bitLength();
087: } else {
088: byte[] kBytes = publicPk.getEncodedContents();
089:
090: try {
091: MessageDigest digest = MessageDigest
092: .getInstance("SHA1");
093:
094: digest.update((byte) 0x99);
095: digest.update((byte) (kBytes.length >> 8));
096: digest.update((byte) kBytes.length);
097: digest.update(kBytes);
098:
099: this .fingerprint = digest.digest();
100: } catch (NoSuchAlgorithmException e) {
101: throw new IOException("can't find SHA1");
102: }
103:
104: this .keyID = ((long) (fingerprint[fingerprint.length - 8] & 0xff) << 56)
105: | ((long) (fingerprint[fingerprint.length - 7] & 0xff) << 48)
106: | ((long) (fingerprint[fingerprint.length - 6] & 0xff) << 40)
107: | ((long) (fingerprint[fingerprint.length - 5] & 0xff) << 32)
108: | ((long) (fingerprint[fingerprint.length - 4] & 0xff) << 24)
109: | ((long) (fingerprint[fingerprint.length - 3] & 0xff) << 16)
110: | ((long) (fingerprint[fingerprint.length - 2] & 0xff) << 8)
111: | ((fingerprint[fingerprint.length - 1] & 0xff));
112:
113: if (key instanceof RSAPublicBCPGKey) {
114: this .keyStrength = ((RSAPublicBCPGKey) key)
115: .getModulus().bitLength();
116: } else if (key instanceof DSAPublicBCPGKey) {
117: this .keyStrength = ((DSAPublicBCPGKey) key).getP()
118: .bitLength();
119: } else if (key instanceof ElGamalPublicBCPGKey) {
120: this .keyStrength = ((ElGamalPublicBCPGKey) key).getP()
121: .bitLength();
122: }
123: }
124: }
125:
126: /**
127: * Create a PGPPublicKey from the passed in JCA one.
128: * <p>
129: * Note: the time passed in affects the value of the key's keyID, so you probably only want
130: * to do this once for a JCA key, or make sure you keep track of the time you used.
131: *
132: * @param algorithm asymmetric algorithm type representing the public key.
133: * @param pubKey actual public key to associate.
134: * @param time date of creation.
135: * @param provider provider to use for underlying digest calculations.
136: * @throws PGPException on key creation problem.
137: * @throws NoSuchProviderException if the specified provider is required and cannot be found.
138: */
139: public PGPPublicKey(int algorithm, PublicKey pubKey, Date time,
140: String provider) throws PGPException,
141: NoSuchProviderException {
142: BCPGKey bcpgKey;
143:
144: if (pubKey instanceof RSAPublicKey) {
145: RSAPublicKey rK = (RSAPublicKey) pubKey;
146:
147: bcpgKey = new RSAPublicBCPGKey(rK.getModulus(), rK
148: .getPublicExponent());
149: } else if (pubKey instanceof DSAPublicKey) {
150: DSAPublicKey dK = (DSAPublicKey) pubKey;
151: DSAParams dP = dK.getParams();
152:
153: bcpgKey = new DSAPublicBCPGKey(dP.getP(), dP.getQ(), dP
154: .getG(), dK.getY());
155: } else if (pubKey instanceof ElGamalPublicKey) {
156: ElGamalPublicKey eK = (ElGamalPublicKey) pubKey;
157: ElGamalParameterSpec eS = eK.getParameters();
158:
159: bcpgKey = new ElGamalPublicBCPGKey(eS.getP(), eS.getG(), eK
160: .getY());
161: } else {
162: throw new PGPException("unknown key class");
163: }
164:
165: this .publicPk = new PublicKeyPacket(algorithm, time, bcpgKey);
166: this .ids = new ArrayList();
167: this .idSigs = new ArrayList();
168:
169: try {
170: init();
171: } catch (IOException e) {
172: throw new PGPException("exception calculating keyID", e);
173: }
174: }
175:
176: /*
177: * Constructor for a sub-key.
178: */
179: PGPPublicKey(PublicKeyPacket publicPk, TrustPacket trustPk,
180: List sigs) throws IOException {
181: this .publicPk = publicPk;
182: this .trustPk = trustPk;
183: this .subSigs = sigs;
184:
185: init();
186: }
187:
188: PGPPublicKey(PGPPublicKey key, TrustPacket trust, List subSigs) {
189: this .publicPk = key.publicPk;
190: this .trustPk = trust;
191: this .subSigs = subSigs;
192:
193: this .fingerprint = key.fingerprint;
194: this .keyID = key.keyID;
195: this .keyStrength = key.keyStrength;
196: }
197:
198: /**
199: * Copy constructor.
200: * @param pubKey the public key to copy.
201: */
202: PGPPublicKey(PGPPublicKey pubKey) {
203: this .publicPk = pubKey.publicPk;
204:
205: this .keySigs = new ArrayList(pubKey.keySigs);
206: this .ids = new ArrayList(pubKey.ids);
207: this .idTrusts = new ArrayList(pubKey.idTrusts);
208: this .idSigs = new ArrayList(pubKey.idSigs.size());
209: for (int i = 0; i != pubKey.idSigs.size(); i++) {
210: this .idSigs.add(new ArrayList((ArrayList) pubKey.idSigs
211: .get(i)));
212: }
213:
214: if (pubKey.subSigs != null) {
215: this .subSigs = new ArrayList(pubKey.subSigs.size());
216: for (int i = 0; i != pubKey.subSigs.size(); i++) {
217: this .subSigs.add(pubKey.subSigs.get(i));
218: }
219: }
220:
221: this .fingerprint = pubKey.fingerprint;
222: this .keyID = pubKey.keyID;
223: this .keyStrength = pubKey.keyStrength;
224: }
225:
226: PGPPublicKey(PublicKeyPacket publicPk, TrustPacket trustPk,
227: List keySigs, List ids, List idTrusts, List idSigs)
228: throws IOException {
229: this .publicPk = publicPk;
230: this .trustPk = trustPk;
231: this .keySigs = keySigs;
232: this .ids = ids;
233: this .idTrusts = idTrusts;
234: this .idSigs = idSigs;
235:
236: init();
237: }
238:
239: PGPPublicKey(PublicKeyPacket publicPk, List ids, List idSigs)
240: throws IOException {
241: this .publicPk = publicPk;
242: this .ids = ids;
243: this .idSigs = idSigs;
244:
245: init();
246: }
247:
248: /**
249: * @return the version of this key.
250: */
251: public int getVersion() {
252: return publicPk.getVersion();
253: }
254:
255: /**
256: * @return creation time of key.
257: */
258: public Date getCreationTime() {
259: return publicPk.getTime();
260: }
261:
262: /**
263: * @return number of valid days from creation time - zero means no
264: * expiry.
265: */
266: public int getValidDays() {
267: if (publicPk.getVersion() > 3) {
268: return (int) (this .getValidSeconds() / (24 * 60 * 60));
269: } else {
270: return publicPk.getValidDays();
271: }
272: }
273:
274: /**
275: * Return the trust data associated with the public key, if present.
276: * @return a byte array with trust data, null otherwise.
277: */
278: public byte[] getTrustData() {
279: if (trustPk == null) {
280: return null;
281: }
282:
283: return Arrays.clone(trustPk.getLevelAndTrustAmount());
284: }
285:
286: /**
287: * @return number of valid seconds from creation time - zero means no
288: * expiry.
289: */
290: public long getValidSeconds() {
291: if (publicPk.getVersion() > 3) {
292: if (this .isMasterKey()) {
293: for (int i = 0; i != MASTER_KEY_CERTIFICATION_TYPES.length; i++) {
294: long seconds = getExpirationTimeFromSig(true,
295: MASTER_KEY_CERTIFICATION_TYPES[i]);
296:
297: if (seconds >= 0) {
298: return seconds;
299: }
300: }
301: } else {
302: long seconds = getExpirationTimeFromSig(false,
303: PGPSignature.SUBKEY_BINDING);
304:
305: if (seconds >= 0) {
306: return seconds;
307: }
308: }
309:
310: return 0;
311: } else {
312: return (long) publicPk.getValidDays() * 24 * 60 * 60;
313: }
314: }
315:
316: private long getExpirationTimeFromSig(boolean selfSigned,
317: int signatureType) {
318: Iterator signatures = this .getSignaturesOfType(signatureType);
319:
320: if (signatures.hasNext()) {
321: PGPSignature sig = (PGPSignature) signatures.next();
322:
323: if (!selfSigned || sig.getKeyID() == this .getKeyID()) {
324: PGPSignatureSubpacketVector hashed = sig
325: .getHashedSubPackets();
326:
327: if (hashed != null) {
328: return hashed.getKeyExpirationTime();
329: }
330:
331: return 0;
332: }
333: }
334:
335: return -1;
336: }
337:
338: /**
339: * Return the keyID associated with the public key.
340: *
341: * @return long
342: */
343: public long getKeyID() {
344: return keyID;
345: }
346:
347: /**
348: * Return the fingerprint of the key.
349: *
350: * @return key fingerprint.
351: */
352: public byte[] getFingerprint() {
353: byte[] tmp = new byte[fingerprint.length];
354:
355: System.arraycopy(fingerprint, 0, tmp, 0, tmp.length);
356:
357: return tmp;
358: }
359:
360: /**
361: * Return true if this key is marked as suitable for using for encryption.
362: * @return true if this key is marked as suitable for using for encryption.
363: */
364: public boolean isEncryptionKey() {
365: int algorithm = publicPk.getAlgorithm();
366:
367: return ((algorithm == RSA_GENERAL)
368: || (algorithm == RSA_ENCRYPT)
369: || (algorithm == ELGAMAL_ENCRYPT) || (algorithm == ELGAMAL_GENERAL));
370: }
371:
372: /**
373: * Return true if this is a master key.
374: * @return true if a master key.
375: */
376: public boolean isMasterKey() {
377: return (subSigs == null);
378: }
379:
380: /**
381: * Return the algorithm code associated with the public key.
382: *
383: * @return int
384: */
385: public int getAlgorithm() {
386: return publicPk.getAlgorithm();
387: }
388:
389: /**
390: * Return the strength of the key in bits.
391: *
392: * @return bit strenght of key.
393: */
394: public int getBitStrength() {
395: return keyStrength;
396: }
397:
398: /**
399: * Return the public key contained in the object.
400: *
401: * @param provider provider to construct the key for.
402: * @return a JCE/JCA public key.
403: * @throws PGPException if the key algorithm is not recognised.
404: * @throws NoSuchProviderException if the provider cannot be found.
405: */
406: public PublicKey getKey(String provider) throws PGPException,
407: NoSuchProviderException {
408: KeyFactory fact;
409:
410: try {
411: switch (publicPk.getAlgorithm()) {
412: case RSA_ENCRYPT:
413: case RSA_GENERAL:
414: case RSA_SIGN:
415: RSAPublicBCPGKey rsaK = (RSAPublicBCPGKey) publicPk
416: .getKey();
417: RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsaK
418: .getModulus(), rsaK.getPublicExponent());
419:
420: fact = KeyFactory.getInstance("RSA", provider);
421:
422: return fact.generatePublic(rsaSpec);
423: case DSA:
424: DSAPublicBCPGKey dsaK = (DSAPublicBCPGKey) publicPk
425: .getKey();
426: DSAPublicKeySpec dsaSpec = new DSAPublicKeySpec(dsaK
427: .getY(), dsaK.getP(), dsaK.getQ(), dsaK.getG());
428:
429: fact = KeyFactory.getInstance("DSA", provider);
430:
431: return fact.generatePublic(dsaSpec);
432: case ELGAMAL_ENCRYPT:
433: case ELGAMAL_GENERAL:
434: ElGamalPublicBCPGKey elK = (ElGamalPublicBCPGKey) publicPk
435: .getKey();
436: ElGamalPublicKeySpec elSpec = new ElGamalPublicKeySpec(
437: elK.getY(), new ElGamalParameterSpec(
438: elK.getP(), elK.getG()));
439:
440: fact = KeyFactory.getInstance("ElGamal", provider);
441:
442: return fact.generatePublic(elSpec);
443: default:
444: throw new PGPException(
445: "unknown public key algorithm encountered");
446: }
447: } catch (PGPException e) {
448: throw e;
449: } catch (Exception e) {
450: throw new PGPException("exception constructing public key",
451: e);
452: }
453: }
454:
455: /**
456: * Return any userIDs associated with the key.
457: *
458: * @return an iterator of Strings.
459: */
460: public Iterator getUserIDs() {
461: List temp = new ArrayList();
462:
463: for (int i = 0; i != ids.size(); i++) {
464: if (ids.get(i) instanceof String) {
465: temp.add(ids.get(i));
466: }
467: }
468:
469: return temp.iterator();
470: }
471:
472: /**
473: * Return any user attribute vectors associated with the key.
474: *
475: * @return an iterator of PGPUserAttributeSubpacketVector objects.
476: */
477: public Iterator getUserAttributes() {
478: List temp = new ArrayList();
479:
480: for (int i = 0; i != ids.size(); i++) {
481: if (ids.get(i) instanceof PGPUserAttributeSubpacketVector) {
482: temp.add(ids.get(i));
483: }
484: }
485:
486: return temp.iterator();
487: }
488:
489: /**
490: * Return any signatures associated with the passed in id.
491: *
492: * @param id the id to be matched.
493: * @return an iterator of PGPSignature objects.
494: */
495: public Iterator getSignaturesForID(String id) {
496: for (int i = 0; i != ids.size(); i++) {
497: if (id.equals(ids.get(i))) {
498: return ((ArrayList) idSigs.get(i)).iterator();
499: }
500: }
501:
502: return null;
503: }
504:
505: /**
506: * Return an iterator of signatures associated with the passed in user attributes.
507: *
508: * @param userAttributes the vector of user attributes to be matched.
509: * @return an iterator of PGPSignature objects.
510: */
511: public Iterator getSignaturesForUserAttribute(
512: PGPUserAttributeSubpacketVector userAttributes) {
513: for (int i = 0; i != ids.size(); i++) {
514: if (userAttributes.equals(ids.get(i))) {
515: return ((ArrayList) idSigs.get(i)).iterator();
516: }
517: }
518:
519: return null;
520: }
521:
522: /**
523: * Return signatures of the passed in type that are on this key.
524: *
525: * @param signatureType the type of the signature to be returned.
526: * @return an iterator (possibly empty) of signatures of the given type.
527: */
528: public Iterator getSignaturesOfType(int signatureType) {
529: List l = new ArrayList();
530: Iterator it = this .getSignatures();
531:
532: while (it.hasNext()) {
533: PGPSignature sig = (PGPSignature) it.next();
534:
535: if (sig.getSignatureType() == signatureType) {
536: l.add(sig);
537: }
538: }
539:
540: return l.iterator();
541: }
542:
543: /**
544: * Return all signatures/certifications associated with this key.
545: *
546: * @return an iterator (possibly empty) with all signatures/certifications.
547: */
548: public Iterator getSignatures() {
549: if (subSigs == null) {
550: List sigs = new ArrayList();
551:
552: sigs.addAll(keySigs);
553:
554: for (int i = 0; i != idSigs.size(); i++) {
555: sigs.addAll((Collection) idSigs.get(i));
556: }
557:
558: return sigs.iterator();
559: } else {
560: return subSigs.iterator();
561: }
562: }
563:
564: public byte[] getEncoded() throws IOException {
565: ByteArrayOutputStream bOut = new ByteArrayOutputStream();
566:
567: this .encode(bOut);
568:
569: return bOut.toByteArray();
570: }
571:
572: public void encode(OutputStream outStream) throws IOException {
573: BCPGOutputStream out;
574:
575: if (outStream instanceof BCPGOutputStream) {
576: out = (BCPGOutputStream) outStream;
577: } else {
578: out = new BCPGOutputStream(outStream);
579: }
580:
581: out.writePacket(publicPk);
582: if (trustPk != null) {
583: out.writePacket(trustPk);
584: }
585:
586: if (subSigs == null) // not a sub-key
587: {
588: for (int i = 0; i != keySigs.size(); i++) {
589: ((PGPSignature) keySigs.get(i)).encode(out);
590: }
591:
592: for (int i = 0; i != ids.size(); i++) {
593: if (ids.get(i) instanceof String) {
594: String id = (String) ids.get(i);
595:
596: out.writePacket(new UserIDPacket(id));
597: } else {
598: PGPUserAttributeSubpacketVector v = (PGPUserAttributeSubpacketVector) ids
599: .get(i);
600:
601: out.writePacket(new UserAttributePacket(v
602: .toSubpacketArray()));
603: }
604:
605: if (idTrusts.get(i) != null) {
606: out.writePacket((ContainedPacket) idTrusts.get(i));
607: }
608:
609: List sigs = (List) idSigs.get(i);
610: for (int j = 0; j != sigs.size(); j++) {
611: ((PGPSignature) sigs.get(j)).encode(out);
612: }
613: }
614: } else {
615: for (int j = 0; j != subSigs.size(); j++) {
616: ((PGPSignature) subSigs.get(j)).encode(out);
617: }
618: }
619: }
620:
621: /**
622: * Check whether this (sub)key has a revocation signature on it.
623: *
624: * @return boolean indicating whether this (sub)key has been revoked.
625: */
626: public boolean isRevoked() {
627: int ns = 0;
628: boolean revoked = false;
629:
630: if (this .isMasterKey()) // Master key
631: {
632: while (!revoked && (ns < keySigs.size())) {
633: if (((PGPSignature) keySigs.get(ns++))
634: .getSignatureType() == PGPSignature.KEY_REVOCATION) {
635: revoked = true;
636: }
637: }
638: } else // Sub-key
639: {
640: while (!revoked && (ns < subSigs.size())) {
641: if (((PGPSignature) subSigs.get(ns++))
642: .getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
643: revoked = true;
644: }
645: }
646: }
647:
648: return revoked;
649: }
650:
651: /**
652: * Add a certification to the given public key.
653: *
654: * @param key the key the certification is to be added to.
655: * @param id the id the certification is associated with.
656: * @param certification the new certification.
657: * @return the re-certified key.
658: */
659: public static PGPPublicKey addCertification(PGPPublicKey key,
660: String id, PGPSignature certification) {
661: PGPPublicKey returnKey = new PGPPublicKey(key);
662: List sigList = null;
663:
664: for (int i = 0; i != returnKey.ids.size(); i++) {
665: if (id.equals(returnKey.ids.get(i))) {
666: sigList = (List) returnKey.idSigs.get(i);
667: }
668: }
669:
670: if (sigList != null) {
671: sigList.add(certification);
672: } else {
673: sigList = new ArrayList();
674:
675: sigList.add(certification);
676: returnKey.ids.add(id);
677: returnKey.idTrusts.add(null);
678: returnKey.idSigs.add(sigList);
679: }
680:
681: return returnKey;
682: }
683:
684: /**
685: * Remove any certifications associated with a given id on a key.
686: *
687: * @param key the key the certifications are to be removed from.
688: * @param id the id that is to be removed.
689: * @return the re-certified key, null if the id was not found on the key.
690: */
691: public static PGPPublicKey removeCertification(PGPPublicKey key,
692: String id) {
693: PGPPublicKey returnKey = new PGPPublicKey(key);
694: boolean found = false;
695:
696: for (int i = 0; i < returnKey.ids.size(); i++) {
697: if (id.equals(returnKey.ids.get(i))) {
698: found = true;
699: returnKey.ids.remove(i);
700: returnKey.idTrusts.remove(i);
701: returnKey.idSigs.remove(i);
702: }
703: }
704:
705: if (!found) {
706: return null;
707: }
708:
709: return returnKey;
710: }
711:
712: /**
713: * Remove any certifications associated with a given id on a key.
714: *
715: * @param key the key the certifications are to be removed from.
716: * @param id the id that the certfication is to be removed from.
717: * @param certification the certfication to be removed.
718: * @return the re-certified key, null if the certification was not found.
719: */
720: public static PGPPublicKey removeCertification(PGPPublicKey key,
721: String id, PGPSignature certification) {
722: PGPPublicKey returnKey = new PGPPublicKey(key);
723: boolean found = false;
724:
725: for (int i = 0; i < returnKey.ids.size(); i++) {
726: if (id.equals(returnKey.ids.get(i))) {
727: found = ((List) returnKey.idSigs.get(i))
728: .remove(certification);
729: }
730: }
731:
732: if (!found) {
733: return null;
734: }
735:
736: return returnKey;
737: }
738:
739: /**
740: * Add a revocation or some other key certification to a key.
741: *
742: * @param key the key the revocation is to be added to.
743: * @param certification the key signature to be added.
744: * @return the new changed public key object.
745: */
746: public static PGPPublicKey addCertification(PGPPublicKey key,
747: PGPSignature certification) {
748: if (key.isMasterKey()) {
749: if (certification.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
750: throw new IllegalArgumentException(
751: "signature type incorrect for master key revocation.");
752: }
753: } else {
754: if (certification.getSignatureType() == PGPSignature.KEY_REVOCATION) {
755: throw new IllegalArgumentException(
756: "signature type incorrect for sub-key revocation.");
757: }
758: }
759:
760: PGPPublicKey returnKey = new PGPPublicKey(key);
761:
762: if (returnKey.subSigs != null) {
763: returnKey.subSigs.add(certification);
764: } else {
765: returnKey.keySigs.add(certification);
766: }
767:
768: return returnKey;
769: }
770: }
|