private DERObject getSingleValuedSignedAttribute( DERObjectIdentifier attrOID, String printableName) throws CMSException { AttributeTable unsignedAttrTable = this.getUnsignedAttributes(); if (unsignedAttrTable != null && unsignedAttrTable.getAll(attrOID).size() > 0) { throw new CMSException( "The " + printableName + " attribute MUST NOT be an unsigned attribute"); } AttributeTable signedAttrTable = this.getSignedAttributes(); if (signedAttrTable == null) { return null; } ASN1EncodableVector v = signedAttrTable.getAll(attrOID); switch (v.size()) { case 0: return null; case 1: { Attribute t = (Attribute) v.get(0); ASN1Set attrValues = t.getAttrValues(); if (attrValues.size() != 1) { throw new CMSException( "A " + printableName + " attribute MUST have a single attribute value"); } return attrValues.getObjectAt(0).getDERObject(); } default: throw new CMSException( "The SignedAttributes in a signerInfo MUST NOT include multiple instances of the " + printableName + " attribute"); } }
private Date findTimestamp(CMSSignedData cmsSignedData) { Iterator iterator = cmsSignedData.getSignerInfos().getSigners().iterator(); while (iterator.hasNext()) { SignerInformation signerInformation = (SignerInformation) iterator.next(); AttributeTable signedAttrTable = signerInformation.getSignedAttributes(); if (signedAttrTable == null) { continue; } ASN1EncodableVector v = signedAttrTable.getAll(CMSAttributes.signingTime); switch (v.size()) { case 0: continue; case 1: Attribute t = (Attribute) v.get(0); ASN1Set attrValues = t.getAttrValues(); if (attrValues.size() != 1) { continue; } // found it try { return ((ASN1UTCTime) attrValues.getObjectAt(0).getDERObject()).getDate(); } catch (ParseException e) { e.printStackTrace(); } continue; default: continue; } } // no timestamp found return null; }
/** @deprecated */ private boolean doVerify(PublicKey key, Provider sigProvider) throws CMSException, NoSuchAlgorithmException { String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(this.getDigestAlgOID()); String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID()); String signatureName = digestName + "with" + encName; Signature sig = CMSSignedHelper.INSTANCE.getSignatureInstance(signatureName, sigProvider); MessageDigest digest = CMSSignedHelper.INSTANCE.getDigestInstance(digestName, sigProvider); // TODO [BJA-109] Note: PSSParameterSpec requires JDK1.4+ /* try { DERObjectIdentifier sigAlgOID = encryptionAlgorithm.getObjectId(); DEREncodable sigParams = this.encryptionAlgorithm.getParameters(); if (sigAlgOID.equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) { // RFC 4056 // When the id-RSASSA-PSS algorithm identifier is used for a signature, // the AlgorithmIdentifier parameters field MUST contain RSASSA-PSS-params. if (sigParams == null) { throw new CMSException( "RSASSA-PSS signature must specify algorithm parameters"); } AlgorithmParameters params = AlgorithmParameters.getInstance( sigAlgOID.getId(), sig.getProvider().getName()); params.init(sigParams.getDERObject().getEncoded(), "ASN.1"); PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class); sig.setParameter(spec); } else { // TODO Are there other signature algorithms that provide parameters? if (sigParams != null) { throw new CMSException("unrecognised signature parameters provided"); } } } catch (IOException e) { throw new CMSException("error encoding signature parameters.", e); } catch (InvalidAlgorithmParameterException e) { throw new CMSException("error setting signature parameters.", e); } catch (InvalidParameterSpecException e) { throw new CMSException("error processing signature parameters.", e); } */ try { if (digestCalculator != null) { resultDigest = digestCalculator.getDigest(); } else { if (content != null) { content.write(new DigOutputStream(digest)); } else if (signedAttributeSet == null) { // TODO Get rid of this exception and just treat content==null as empty not missing? throw new CMSException("data not encapsulated in signature - use detached constructor."); } resultDigest = digest.digest(); } } catch (IOException e) { throw new CMSException("can't process mime object to create signature.", e); } // RFC 3852 11.1 Check the content-type attribute is correct { DERObject validContentType = getSingleValuedSignedAttribute(CMSAttributes.contentType, "content-type"); if (validContentType == null) { if (!isCounterSignature && signedAttributeSet != null) { throw new CMSException( "The content-type attribute type MUST be present whenever signed attributes are present in signed-data"); } } else { if (isCounterSignature) { throw new CMSException( "[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute"); } if (!(validContentType instanceof DERObjectIdentifier)) { throw new CMSException( "content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'"); } DERObjectIdentifier signedContentType = (DERObjectIdentifier) validContentType; if (!signedContentType.equals(contentType)) { throw new CMSException("content-type attribute value does not match eContentType"); } } } // RFC 3852 11.2 Check the message-digest attribute is correct { DERObject validMessageDigest = getSingleValuedSignedAttribute(CMSAttributes.messageDigest, "message-digest"); if (validMessageDigest == null) { if (signedAttributeSet != null) { throw new CMSException( "the message-digest signed attribute type MUST be present when there are any signed attributes present"); } } else { if (!(validMessageDigest instanceof ASN1OctetString)) { throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'"); } ASN1OctetString signedMessageDigest = (ASN1OctetString) validMessageDigest; if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets())) { throw new CMSSignerDigestMismatchException( "message-digest attribute value does not match calculated value"); } } } // RFC 3852 11.4 Validate countersignature attribute(s) { AttributeTable signedAttrTable = this.getSignedAttributes(); if (signedAttrTable != null && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0) { throw new CMSException("A countersignature attribute MUST NOT be a signed attribute"); } AttributeTable unsignedAttrTable = this.getUnsignedAttributes(); if (unsignedAttrTable != null) { ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature); for (int i = 0; i < csAttrs.size(); ++i) { Attribute csAttr = (Attribute) csAttrs.get(i); if (csAttr.getAttrValues().size() < 1) { throw new CMSException( "A countersignature attribute MUST contain at least one AttributeValue"); } // Note: We don't recursively validate the countersignature value } } } try { sig.initVerify(key); if (signedAttributeSet == null) { if (digestCalculator != null) { // need to decrypt signature and check message bytes return verifyDigest(resultDigest, key, this.getSignature(), sigProvider); } else if (content != null) { // TODO Use raw signature of the hash value instead content.write(new SigOutputStream(sig)); } } else { sig.update(this.getEncodedSignedAttributes()); } return sig.verify(this.getSignature()); } catch (InvalidKeyException e) { throw new CMSException("key not appropriate to signature in message.", e); } catch (IOException e) { throw new CMSException("can't process mime object to create signature.", e); } catch (SignatureException e) { throw new CMSException("invalid signature format in message: " + e.getMessage(), e); } }
private boolean doVerify(SignerInformationVerifier verifier) throws CMSException { String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(this.getDigestAlgOID()); String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID()); String signatureName = digestName + "with" + encName; try { if (digestCalculator != null) { resultDigest = digestCalculator.getDigest(); } else { DigestCalculator calc = verifier.getDigestCalculator(this.getDigestAlgorithmID()); if (content != null) { OutputStream digOut = calc.getOutputStream(); content.write(digOut); digOut.close(); } else if (signedAttributeSet == null) { // TODO Get rid of this exception and just treat content==null as empty not missing? throw new CMSException("data not encapsulated in signature - use detached constructor."); } resultDigest = calc.getDigest(); } } catch (IOException e) { throw new CMSException("can't process mime object to create signature.", e); } catch (NoSuchAlgorithmException e) { throw new CMSException("can't find algorithm: " + e.getMessage(), e); } catch (OperatorCreationException e) { throw new CMSException("can't create digest calculator: " + e.getMessage(), e); } // RFC 3852 11.1 Check the content-type attribute is correct { DERObject validContentType = getSingleValuedSignedAttribute(CMSAttributes.contentType, "content-type"); if (validContentType == null) { if (!isCounterSignature && signedAttributeSet != null) { throw new CMSException( "The content-type attribute type MUST be present whenever signed attributes are present in signed-data"); } } else { if (isCounterSignature) { throw new CMSException( "[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute"); } if (!(validContentType instanceof DERObjectIdentifier)) { throw new CMSException( "content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'"); } DERObjectIdentifier signedContentType = (DERObjectIdentifier) validContentType; if (!signedContentType.equals(contentType)) { throw new CMSException("content-type attribute value does not match eContentType"); } } } // RFC 3852 11.2 Check the message-digest attribute is correct { DERObject validMessageDigest = getSingleValuedSignedAttribute(CMSAttributes.messageDigest, "message-digest"); if (validMessageDigest == null) { if (signedAttributeSet != null) { throw new CMSException( "the message-digest signed attribute type MUST be present when there are any signed attributes present"); } } else { if (!(validMessageDigest instanceof ASN1OctetString)) { throw new CMSException("message-digest attribute value not of ASN.1 type 'OCTET STRING'"); } ASN1OctetString signedMessageDigest = (ASN1OctetString) validMessageDigest; if (!Arrays.constantTimeAreEqual(resultDigest, signedMessageDigest.getOctets())) { throw new CMSSignerDigestMismatchException( "message-digest attribute value does not match calculated value"); } } } // RFC 3852 11.4 Validate countersignature attribute(s) { AttributeTable signedAttrTable = this.getSignedAttributes(); if (signedAttrTable != null && signedAttrTable.getAll(CMSAttributes.counterSignature).size() > 0) { throw new CMSException("A countersignature attribute MUST NOT be a signed attribute"); } AttributeTable unsignedAttrTable = this.getUnsignedAttributes(); if (unsignedAttrTable != null) { ASN1EncodableVector csAttrs = unsignedAttrTable.getAll(CMSAttributes.counterSignature); for (int i = 0; i < csAttrs.size(); ++i) { Attribute csAttr = (Attribute) csAttrs.get(i); if (csAttr.getAttrValues().size() < 1) { throw new CMSException( "A countersignature attribute MUST contain at least one AttributeValue"); } // Note: We don't recursively validate the countersignature value } } } try { ContentVerifier contentVerifier = verifier.getContentVerifier(sigAlgFinder.find(signatureName)); OutputStream sigOut = contentVerifier.getOutputStream(); if (signedAttributeSet == null) { if (digestCalculator != null) { if (contentVerifier instanceof RawContentVerifier) { RawContentVerifier rawVerifier = (RawContentVerifier) contentVerifier; if (encName.equals("RSA")) { DigestInfo digInfo = new DigestInfo(digestAlgorithm, resultDigest); return rawVerifier.verify(digInfo.getDEREncoded(), this.getSignature()); } return rawVerifier.verify(resultDigest, this.getSignature()); } throw new CMSException("verifier unable to process raw signature"); } else if (content != null) { // TODO Use raw signature of the hash value instead content.write(sigOut); } } else { sigOut.write(this.getEncodedSignedAttributes()); } sigOut.close(); return contentVerifier.verify(this.getSignature()); } catch (IOException e) { throw new CMSException("can't process mime object to create signature.", e); } catch (OperatorCreationException e) { throw new CMSException("can't create content verifier: " + e.getMessage(), e); } }
/** * Return a SignerInformationStore containing the counter signatures attached to this signer. If * no counter signatures are present an empty store is returned. */ public SignerInformationStore getCounterSignatures() { // TODO There are several checks implied by the RFC3852 comments that are missing /* The countersignature attribute MUST be an unsigned attribute; it MUST NOT be a signed attribute, an authenticated attribute, an unauthenticated attribute, or an unprotected attribute. */ AttributeTable unsignedAttributeTable = getUnsignedAttributes(); if (unsignedAttributeTable == null) { return new SignerInformationStore(new ArrayList(0)); } List counterSignatures = new ArrayList(); /* The UnsignedAttributes syntax is defined as a SET OF Attributes. The UnsignedAttributes in a signerInfo may include multiple instances of the countersignature attribute. */ ASN1EncodableVector allCSAttrs = unsignedAttributeTable.getAll(CMSAttributes.counterSignature); for (int i = 0; i < allCSAttrs.size(); ++i) { Attribute counterSignatureAttribute = (Attribute) allCSAttrs.get(i); /* A countersignature attribute can have multiple attribute values. The syntax is defined as a SET OF AttributeValue, and there MUST be one or more instances of AttributeValue present. */ ASN1Set values = counterSignatureAttribute.getAttrValues(); if (values.size() < 1) { // TODO Throw an appropriate exception? } for (Enumeration en = values.getObjects(); en.hasMoreElements(); ) { /* Countersignature values have the same meaning as SignerInfo values for ordinary signatures, except that: 1. The signedAttributes field MUST NOT contain a content-type attribute; there is no content type for countersignatures. 2. The signedAttributes field MUST contain a message-digest attribute if it contains any other attributes. 3. The input to the message-digesting process is the contents octets of the DER encoding of the signatureValue field of the SignerInfo value with which the attribute is associated. */ SignerInfo si = SignerInfo.getInstance(en.nextElement()); String digestName = CMSSignedHelper.INSTANCE.getDigestAlgName( si.getDigestAlgorithm().getObjectId().getId()); counterSignatures.add( new SignerInformation( si, null, null, new CounterSignatureDigestCalculator(digestName, null, getSignature()), new DefaultSignatureAlgorithmIdentifierFinder())); } } return new SignerInformationStore(counterSignatures); }