1 package org.bouncycastle.cms; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.util.ArrayList; 6 import java.util.Enumeration; 7 import java.util.Iterator; 8 import java.util.List; 9 10 import org.bouncycastle.asn1.ASN1Encodable; 11 import org.bouncycastle.asn1.ASN1EncodableVector; 12 import org.bouncycastle.asn1.ASN1Encoding; 13 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 14 import org.bouncycastle.asn1.ASN1OctetString; 15 import org.bouncycastle.asn1.ASN1Primitive; 16 import org.bouncycastle.asn1.ASN1Set; 17 import org.bouncycastle.asn1.DERNull; 18 import org.bouncycastle.asn1.DERSet; 19 import org.bouncycastle.asn1.cms.Attribute; 20 import org.bouncycastle.asn1.cms.AttributeTable; 21 import org.bouncycastle.asn1.cms.CMSAlgorithmProtection; 22 import org.bouncycastle.asn1.cms.CMSAttributes; 23 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; 24 import org.bouncycastle.asn1.cms.SignerIdentifier; 25 import org.bouncycastle.asn1.cms.SignerInfo; 26 import org.bouncycastle.asn1.cms.Time; 27 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 28 import org.bouncycastle.asn1.x509.DigestInfo; 29 import org.bouncycastle.cert.X509CertificateHolder; 30 import org.bouncycastle.operator.ContentVerifier; 31 import org.bouncycastle.operator.DigestCalculator; 32 import org.bouncycastle.operator.OperatorCreationException; 33 import org.bouncycastle.operator.RawContentVerifier; 34 import org.bouncycastle.util.Arrays; 35 import org.bouncycastle.util.io.TeeOutputStream; 36 37 /** 38 * an expanded SignerInfo block from a CMS Signed message 39 */ 40 public class SignerInformation 41 { 42 private final SignerId sid; 43 private final CMSProcessable content; 44 private final byte[] signature; 45 private final ASN1ObjectIdentifier contentType; 46 private final boolean isCounterSignature; 47 48 // Derived 49 private AttributeTable signedAttributeValues; 50 private AttributeTable unsignedAttributeValues; 51 private byte[] resultDigest; 52 53 protected final SignerInfo info; 54 protected final AlgorithmIdentifier digestAlgorithm; 55 protected final AlgorithmIdentifier encryptionAlgorithm; 56 protected final ASN1Set signedAttributeSet; 57 protected final ASN1Set unsignedAttributeSet; 58 SignerInformation( SignerInfo info, ASN1ObjectIdentifier contentType, CMSProcessable content, byte[] resultDigest)59 SignerInformation( 60 SignerInfo info, 61 ASN1ObjectIdentifier contentType, 62 CMSProcessable content, 63 byte[] resultDigest) 64 { 65 this.info = info; 66 this.contentType = contentType; 67 this.isCounterSignature = contentType == null; 68 69 SignerIdentifier s = info.getSID(); 70 71 if (s.isTagged()) 72 { 73 ASN1OctetString octs = ASN1OctetString.getInstance(s.getId()); 74 75 sid = new SignerId(octs.getOctets()); 76 } 77 else 78 { 79 IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(s.getId()); 80 81 sid = new SignerId(iAnds.getName(), iAnds.getSerialNumber().getValue()); 82 } 83 84 this.digestAlgorithm = info.getDigestAlgorithm(); 85 this.signedAttributeSet = info.getAuthenticatedAttributes(); 86 this.unsignedAttributeSet = info.getUnauthenticatedAttributes(); 87 this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm(); 88 this.signature = info.getEncryptedDigest().getOctets(); 89 90 this.content = content; 91 this.resultDigest = resultDigest; 92 } 93 94 /** 95 * Protected constructor. In some cases clients have their own idea about how to encode 96 * the signed attributes and calculate the signature. This constructor is to allow developers 97 * to deal with that by extending off the class and overridng methods like getSignedAttributes(). 98 * 99 * @param baseInfo the SignerInformation to base this one on. 100 */ SignerInformation(SignerInformation baseInfo)101 protected SignerInformation(SignerInformation baseInfo) 102 { 103 this.info = baseInfo.info; 104 this.contentType = baseInfo.contentType; 105 this.isCounterSignature = baseInfo.isCounterSignature(); 106 this.sid = baseInfo.getSID(); 107 this.digestAlgorithm = info.getDigestAlgorithm(); 108 this.signedAttributeSet = info.getAuthenticatedAttributes(); 109 this.unsignedAttributeSet = info.getUnauthenticatedAttributes(); 110 this.encryptionAlgorithm = info.getDigestEncryptionAlgorithm(); 111 this.signature = info.getEncryptedDigest().getOctets(); 112 this.content = baseInfo.content; 113 this.resultDigest = baseInfo.resultDigest; 114 this.signedAttributeValues = baseInfo.signedAttributeValues; 115 this.unsignedAttributeValues = baseInfo.unsignedAttributeValues; 116 } 117 isCounterSignature()118 public boolean isCounterSignature() 119 { 120 return isCounterSignature; 121 } 122 getContentType()123 public ASN1ObjectIdentifier getContentType() 124 { 125 return this.contentType; 126 } 127 encodeObj( ASN1Encodable obj)128 private byte[] encodeObj( 129 ASN1Encodable obj) 130 throws IOException 131 { 132 if (obj != null) 133 { 134 return obj.toASN1Primitive().getEncoded(); 135 } 136 137 return null; 138 } 139 getSID()140 public SignerId getSID() 141 { 142 return sid; 143 } 144 145 /** 146 * return the version number for this objects underlying SignerInfo structure. 147 */ getVersion()148 public int getVersion() 149 { 150 return info.getVersion().getValue().intValue(); 151 } 152 getDigestAlgorithmID()153 public AlgorithmIdentifier getDigestAlgorithmID() 154 { 155 return digestAlgorithm; 156 } 157 158 /** 159 * return the object identifier for the signature. 160 */ getDigestAlgOID()161 public String getDigestAlgOID() 162 { 163 return digestAlgorithm.getAlgorithm().getId(); 164 } 165 166 /** 167 * return the signature parameters, or null if there aren't any. 168 */ getDigestAlgParams()169 public byte[] getDigestAlgParams() 170 { 171 try 172 { 173 return encodeObj(digestAlgorithm.getParameters()); 174 } 175 catch (Exception e) 176 { 177 throw new RuntimeException("exception getting digest parameters " + e); 178 } 179 } 180 181 /** 182 * return the content digest that was calculated during verification. 183 */ getContentDigest()184 public byte[] getContentDigest() 185 { 186 if (resultDigest == null) 187 { 188 throw new IllegalStateException("method can only be called after verify."); 189 } 190 191 return Arrays.clone(resultDigest); 192 } 193 194 /** 195 * return the object identifier for the signature. 196 */ getEncryptionAlgOID()197 public String getEncryptionAlgOID() 198 { 199 return encryptionAlgorithm.getAlgorithm().getId(); 200 } 201 202 /** 203 * return the signature/encryption algorithm parameters, or null if 204 * there aren't any. 205 */ getEncryptionAlgParams()206 public byte[] getEncryptionAlgParams() 207 { 208 try 209 { 210 return encodeObj(encryptionAlgorithm.getParameters()); 211 } 212 catch (Exception e) 213 { 214 throw new RuntimeException("exception getting encryption parameters " + e); 215 } 216 } 217 218 /** 219 * return a table of the signed attributes - indexed by 220 * the OID of the attribute. 221 */ getSignedAttributes()222 public AttributeTable getSignedAttributes() 223 { 224 if (signedAttributeSet != null && signedAttributeValues == null) 225 { 226 signedAttributeValues = new AttributeTable(signedAttributeSet); 227 } 228 229 return signedAttributeValues; 230 } 231 232 /** 233 * return a table of the unsigned attributes indexed by 234 * the OID of the attribute. 235 */ getUnsignedAttributes()236 public AttributeTable getUnsignedAttributes() 237 { 238 if (unsignedAttributeSet != null && unsignedAttributeValues == null) 239 { 240 unsignedAttributeValues = new AttributeTable(unsignedAttributeSet); 241 } 242 243 return unsignedAttributeValues; 244 } 245 246 /** 247 * return the encoded signature 248 */ getSignature()249 public byte[] getSignature() 250 { 251 return Arrays.clone(signature); 252 } 253 254 /** 255 * Return a SignerInformationStore containing the counter signatures attached to this 256 * signer. If no counter signatures are present an empty store is returned. 257 */ getCounterSignatures()258 public SignerInformationStore getCounterSignatures() 259 { 260 // TODO There are several checks implied by the RFC3852 comments that are missing 261 262 /* 263 The countersignature attribute MUST be an unsigned attribute; it MUST 264 NOT be a signed attribute, an authenticated attribute, an 265 unauthenticated attribute, or an unprotected attribute. 266 */ 267 AttributeTable unsignedAttributeTable = getUnsignedAttributes(); 268 if (unsignedAttributeTable == null) 269 { 270 return new SignerInformationStore(new ArrayList(0)); 271 } 272 273 List counterSignatures = new ArrayList(); 274 275 /* 276 The UnsignedAttributes syntax is defined as a SET OF Attributes. The 277 UnsignedAttributes in a signerInfo may include multiple instances of 278 the countersignature attribute. 279 */ 280 ASN1EncodableVector allCSAttrs = unsignedAttributeTable.getAll(CMSAttributes.counterSignature); 281 282 for (int i = 0; i < allCSAttrs.size(); ++i) 283 { 284 Attribute counterSignatureAttribute = (Attribute)allCSAttrs.get(i); 285 286 /* 287 A countersignature attribute can have multiple attribute values. The 288 syntax is defined as a SET OF AttributeValue, and there MUST be one 289 or more instances of AttributeValue present. 290 */ 291 ASN1Set values = counterSignatureAttribute.getAttrValues(); 292 if (values.size() < 1) 293 { 294 // TODO Throw an appropriate exception? 295 } 296 297 for (Enumeration en = values.getObjects(); en.hasMoreElements();) 298 { 299 /* 300 Countersignature values have the same meaning as SignerInfo values 301 for ordinary signatures, except that: 302 303 1. The signedAttributes field MUST NOT contain a content-type 304 attribute; there is no content type for countersignatures. 305 306 2. The signedAttributes field MUST contain a message-digest 307 attribute if it contains any other attributes. 308 309 3. The input to the message-digesting process is the contents 310 octets of the DER encoding of the signatureValue field of the 311 SignerInfo value with which the attribute is associated. 312 */ 313 SignerInfo si = SignerInfo.getInstance(en.nextElement()); 314 315 counterSignatures.add(new SignerInformation(si, null, new CMSProcessableByteArray(getSignature()), null)); 316 } 317 } 318 319 return new SignerInformationStore(counterSignatures); 320 } 321 322 /** 323 * return the DER encoding of the signed attributes. 324 * @throws IOException if an encoding error occurs. 325 */ getEncodedSignedAttributes()326 public byte[] getEncodedSignedAttributes() 327 throws IOException 328 { 329 if (signedAttributeSet != null) 330 { 331 return signedAttributeSet.getEncoded(ASN1Encoding.DER); 332 } 333 334 return null; 335 } 336 doVerify( SignerInformationVerifier verifier)337 private boolean doVerify( 338 SignerInformationVerifier verifier) 339 throws CMSException 340 { 341 String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID()); 342 ContentVerifier contentVerifier; 343 344 try 345 { 346 contentVerifier = verifier.getContentVerifier(encryptionAlgorithm, info.getDigestAlgorithm()); 347 } 348 catch (OperatorCreationException e) 349 { 350 throw new CMSException("can't create content verifier: " + e.getMessage(), e); 351 } 352 353 try 354 { 355 OutputStream sigOut = contentVerifier.getOutputStream(); 356 357 if (resultDigest == null) 358 { 359 DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID()); 360 if (content != null) 361 { 362 OutputStream digOut = calc.getOutputStream(); 363 364 if (signedAttributeSet == null) 365 { 366 if (contentVerifier instanceof RawContentVerifier) 367 { 368 content.write(digOut); 369 } 370 else 371 { 372 OutputStream cOut = new TeeOutputStream(digOut, sigOut); 373 374 content.write(cOut); 375 376 cOut.close(); 377 } 378 } 379 else 380 { 381 content.write(digOut); 382 sigOut.write(this.getEncodedSignedAttributes()); 383 } 384 385 digOut.close(); 386 } 387 else if (signedAttributeSet != null) 388 { 389 sigOut.write(this.getEncodedSignedAttributes()); 390 } 391 else 392 { 393 // TODO Get rid of this exception and just treat content==null as empty not missing? 394 throw new CMSException("data not encapsulated in signature - use detached constructor."); 395 } 396 397 resultDigest = calc.getDigest(); 398 } 399 else 400 { 401 if (signedAttributeSet == null) 402 { 403 if (content != null) 404 { 405 content.write(sigOut); 406 } 407 } 408 else 409 { 410 sigOut.write(this.getEncodedSignedAttributes()); 411 } 412 } 413 414 sigOut.close(); 415 } 416 catch (IOException e) 417 { 418 throw new CMSException("can't process mime object to create signature.", e); 419 } 420 catch (OperatorCreationException e) 421 { 422 throw new CMSException("can't create digest calculator: " + e.getMessage(), e); 423 } 424 425 // RFC 3852 11.1 Check the content-type attribute is correct 426 { 427 ASN1Primitive validContentType = getSingleValuedSignedAttribute( 428 CMSAttributes.contentType, "content-type"); 429 if (validContentType == null) 430 { 431 if (!isCounterSignature && signedAttributeSet != null) 432 { 433 throw new CMSException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data"); 434 } 435 } 436 else 437 { 438 if (isCounterSignature) 439 { 440 throw new CMSException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute"); 441 } 442 443 if (!(validContentType instanceof ASN1ObjectIdentifier)) 444 { 445 throw new CMSException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'"); 446 } 447 448 ASN1ObjectIdentifier signedContentType = (ASN1ObjectIdentifier)validContentType; 449 450 if (!signedContentType.equals(contentType)) 451 { 452 throw new CMSException("content-type attribute value does not match eContentType"); 453 } 454 } 455 } 456 457 AttributeTable signedAttrTable = this.getSignedAttributes(); 458 459 // RFC 6211 Validate Algorithm Identifier protection attribute if present 460 { 461 AttributeTable unsignedAttrTable = this.getUnsignedAttributes(); 462 if (unsignedAttrTable != null && unsignedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect).size() > 0) 463 { 464 throw new CMSException("A cmsAlgorithmProtect attribute MUST be a signed attribute"); 465 } 466 if (signedAttrTable != null) 467 { 468 ASN1EncodableVector protectionAttributes = signedAttrTable.getAll(CMSAttributes.cmsAlgorithmProtect); 469 if (protectionAttributes.size() > 1) 470 { 471 throw new CMSException("Only one instance of a cmsAlgorithmProtect attribute can be present"); 472 } 473 474 if (protectionAttributes.size() > 0) 475 { 476 Attribute attr = Attribute.getInstance(protectionAttributes.get(0)); 477 if (attr.getAttrValues().size() != 1) 478 { 479 throw new CMSException("A cmsAlgorithmProtect attribute MUST contain exactly one value"); 480 } 481 482 CMSAlgorithmProtection algorithmProtection = CMSAlgorithmProtection.getInstance(attr.getAttributeValues()[0]); 483 484 if (!CMSUtils.isEquivalent(algorithmProtection.getDigestAlgorithm(), info.getDigestAlgorithm())) 485 { 486 throw new CMSException("CMS Algorithm Identifier Protection check failed for digestAlgorithm"); 487 } 488 489 if (!CMSUtils.isEquivalent(algorithmProtection.getSignatureAlgorithm(), info.getDigestEncryptionAlgorithm())) 490 { 491 throw new CMSException("CMS Algorithm Identifier Protection check failed for signatureAlgorithm"); 492 } 493 } 494 } 495 } 496 497 // RFC 3852 11.2 Check the message-digest attribute is correct 498 { 499 ASN1Primitive validMessageDigest = getSingleValuedSignedAttribute( 500 CMSAttributes.messageDigest, "message-digest"); 501 if (validMessageDigest == null) 502 { 503 if (signedAttributeSet != null) 504 { 505 throw new CMSException("the message-digest signed attribute type MUST be present when there are any signed attributes present"); 506 } 507 } 508 else 509 { 510 if (!(validMessageDigest instanceof ASN1OctetString)) 511 { 512 throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'"); 513 } 514 515 ASN1OctetString signedMessageDigest = (ASN1OctetString)validMessageDigest; 516 517 if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets())) 518 { 519 throw new CMSSignerDigestMismatchException("message-digest attribute value does not match calculated value"); 520 } 521 } 522 } 523 524 // RFC 3852 11.4 Validate countersignature attribute(s) 525 { 526 if (signedAttrTable != null 527 && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0) 528 { 529 throw new CMSException("A countersignature attribute MUST NOT be a signed attribute"); 530 } 531 532 AttributeTable unsignedAttrTable = this.getUnsignedAttributes(); 533 if (unsignedAttrTable != null) 534 { 535 ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature); 536 for (int i = 0; i < csAttrs.size(); ++i) 537 { 538 Attribute csAttr = Attribute.getInstance(csAttrs.get(i)); 539 if (csAttr.getAttrValues().size() < 1) 540 { 541 throw new CMSException("A countersignature attribute MUST contain at least one AttributeValue"); 542 } 543 544 // Note: We don't recursively validate the countersignature value 545 } 546 } 547 } 548 549 try 550 { 551 if (signedAttributeSet == null && resultDigest != null) 552 { 553 if (contentVerifier instanceof RawContentVerifier) 554 { 555 RawContentVerifier rawVerifier = (RawContentVerifier)contentVerifier; 556 557 if (encName.equals("RSA")) 558 { 559 DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(digestAlgorithm.getAlgorithm(), DERNull.INSTANCE), resultDigest); 560 561 return rawVerifier.verify(digInfo.getEncoded(ASN1Encoding.DER), this.getSignature()); 562 } 563 564 return rawVerifier.verify(resultDigest, this.getSignature()); 565 } 566 } 567 568 return contentVerifier.verify(this.getSignature()); 569 } 570 catch (IOException e) 571 { 572 throw new CMSException("can't process mime object to create signature.", e); 573 } 574 } 575 576 /** 577 * Verify that the given verifier can successfully verify the signature on 578 * this SignerInformation object. 579 * 580 * @param verifier a suitably configured SignerInformationVerifier. 581 * @return true if the signer information is verified, false otherwise. 582 * @throws org.bouncycastle.cms.CMSVerifierCertificateNotValidException if the provider has an associated certificate and the certificate is not valid at the time given as the SignerInfo's signing time. 583 * @throws org.bouncycastle.cms.CMSException if the verifier is unable to create a ContentVerifiers or DigestCalculators. 584 */ verify(SignerInformationVerifier verifier)585 public boolean verify(SignerInformationVerifier verifier) 586 throws CMSException 587 { 588 Time signingTime = getSigningTime(); // has to be validated if present. 589 590 if (verifier.hasAssociatedCertificate()) 591 { 592 if (signingTime != null) 593 { 594 X509CertificateHolder dcv = verifier.getAssociatedCertificate(); 595 596 if (!dcv.isValidOn(signingTime.getDate())) 597 { 598 throw new CMSVerifierCertificateNotValidException("verifier not valid at signingTime"); 599 } 600 } 601 } 602 603 return doVerify(verifier); 604 } 605 606 /** 607 * Return the underlying ASN.1 object defining this SignerInformation object. 608 * 609 * @return a SignerInfo. 610 */ toASN1Structure()611 public SignerInfo toASN1Structure() 612 { 613 return info; 614 } 615 getSingleValuedSignedAttribute( ASN1ObjectIdentifier attrOID, String printableName)616 private ASN1Primitive getSingleValuedSignedAttribute( 617 ASN1ObjectIdentifier attrOID, String printableName) 618 throws CMSException 619 { 620 AttributeTable unsignedAttrTable = this.getUnsignedAttributes(); 621 if (unsignedAttrTable != null 622 && unsignedAttrTable.getAll(attrOID).size() > 0) 623 { 624 throw new CMSException("The " + printableName 625 + " attribute MUST NOT be an unsigned attribute"); 626 } 627 628 AttributeTable signedAttrTable = this.getSignedAttributes(); 629 if (signedAttrTable == null) 630 { 631 return null; 632 } 633 634 ASN1EncodableVector v = signedAttrTable.getAll(attrOID); 635 switch (v.size()) 636 { 637 case 0: 638 return null; 639 case 1: 640 { 641 Attribute t = (Attribute)v.get(0); 642 ASN1Set attrValues = t.getAttrValues(); 643 if (attrValues.size() != 1) 644 { 645 throw new CMSException("A " + printableName 646 + " attribute MUST have a single attribute value"); 647 } 648 649 return attrValues.getObjectAt(0).toASN1Primitive(); 650 } 651 default: 652 throw new CMSException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the " 653 + printableName + " attribute"); 654 } 655 } 656 getSigningTime()657 private Time getSigningTime() throws CMSException 658 { 659 ASN1Primitive validSigningTime = getSingleValuedSignedAttribute( 660 CMSAttributes.signingTime, "signing-time"); 661 662 if (validSigningTime == null) 663 { 664 return null; 665 } 666 667 try 668 { 669 return Time.getInstance(validSigningTime); 670 } 671 catch (IllegalArgumentException e) 672 { 673 throw new CMSException("signing-time attribute value not a valid 'Time' structure"); 674 } 675 } 676 677 /** 678 * Return a signer information object with the passed in unsigned 679 * attributes replacing the ones that are current associated with 680 * the object passed in. 681 * 682 * @param signerInformation the signerInfo to be used as the basis. 683 * @param unsignedAttributes the unsigned attributes to add. 684 * @return a copy of the original SignerInformationObject with the changed attributes. 685 */ replaceUnsignedAttributes( SignerInformation signerInformation, AttributeTable unsignedAttributes)686 public static SignerInformation replaceUnsignedAttributes( 687 SignerInformation signerInformation, 688 AttributeTable unsignedAttributes) 689 { 690 SignerInfo sInfo = signerInformation.info; 691 ASN1Set unsignedAttr = null; 692 693 if (unsignedAttributes != null) 694 { 695 unsignedAttr = new DERSet(unsignedAttributes.toASN1EncodableVector()); 696 } 697 698 return new SignerInformation( 699 new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(), 700 sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), unsignedAttr), 701 signerInformation.contentType, signerInformation.content, null); 702 } 703 704 /** 705 * Return a signer information object with passed in SignerInformationStore representing counter 706 * signatures attached as an unsigned attribute. 707 * 708 * @param signerInformation the signerInfo to be used as the basis. 709 * @param counterSigners signer info objects carrying counter signature. 710 * @return a copy of the original SignerInformationObject with the changed attributes. 711 */ addCounterSigners( SignerInformation signerInformation, SignerInformationStore counterSigners)712 public static SignerInformation addCounterSigners( 713 SignerInformation signerInformation, 714 SignerInformationStore counterSigners) 715 { 716 // TODO Perform checks from RFC 3852 11.4 717 718 SignerInfo sInfo = signerInformation.info; 719 AttributeTable unsignedAttr = signerInformation.getUnsignedAttributes(); 720 ASN1EncodableVector v; 721 722 if (unsignedAttr != null) 723 { 724 v = unsignedAttr.toASN1EncodableVector(); 725 } 726 else 727 { 728 v = new ASN1EncodableVector(); 729 } 730 731 ASN1EncodableVector sigs = new ASN1EncodableVector(); 732 733 for (Iterator it = counterSigners.getSigners().iterator(); it.hasNext();) 734 { 735 sigs.add(((SignerInformation)it.next()).toASN1Structure()); 736 } 737 738 v.add(new Attribute(CMSAttributes.counterSignature, new DERSet(sigs))); 739 740 return new SignerInformation( 741 new SignerInfo(sInfo.getSID(), sInfo.getDigestAlgorithm(), 742 sInfo.getAuthenticatedAttributes(), sInfo.getDigestEncryptionAlgorithm(), sInfo.getEncryptedDigest(), new DERSet(v)), 743 signerInformation.contentType, signerInformation.content, null); 744 } 745 } 746