1 package org.bouncycastle.cms; 2 3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.util.Collections; 6 import java.util.HashMap; 7 import java.util.Map; 8 9 import org.bouncycastle.asn1.ASN1Encoding; 10 import org.bouncycastle.asn1.ASN1ObjectIdentifier; 11 import org.bouncycastle.asn1.ASN1Set; 12 import org.bouncycastle.asn1.DEROctetString; 13 import org.bouncycastle.asn1.DERSet; 14 import org.bouncycastle.asn1.cms.AttributeTable; 15 import org.bouncycastle.asn1.cms.SignerIdentifier; 16 import org.bouncycastle.asn1.cms.SignerInfo; 17 import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 18 import org.bouncycastle.cert.X509CertificateHolder; 19 import org.bouncycastle.operator.ContentSigner; 20 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; 21 import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder; 22 import org.bouncycastle.operator.DigestCalculator; 23 import org.bouncycastle.operator.DigestCalculatorProvider; 24 import org.bouncycastle.operator.OperatorCreationException; 25 import org.bouncycastle.util.Arrays; 26 import org.bouncycastle.util.io.TeeOutputStream; 27 28 public class SignerInfoGenerator 29 { 30 private final SignerIdentifier signerIdentifier; 31 private final CMSAttributeTableGenerator sAttrGen; 32 private final CMSAttributeTableGenerator unsAttrGen; 33 private final ContentSigner signer; 34 private final DigestCalculator digester; 35 private final DigestAlgorithmIdentifierFinder digAlgFinder = new DefaultDigestAlgorithmIdentifierFinder(); 36 private final CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder; 37 38 private byte[] calculatedDigest = null; 39 private X509CertificateHolder certHolder; 40 SignerInfoGenerator( SignerIdentifier signerIdentifier, ContentSigner signer, DigestCalculatorProvider digesterProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder)41 SignerInfoGenerator( 42 SignerIdentifier signerIdentifier, 43 ContentSigner signer, 44 DigestCalculatorProvider digesterProvider, 45 CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder) 46 throws OperatorCreationException 47 { 48 this(signerIdentifier, signer, digesterProvider, sigEncAlgFinder, false); 49 } 50 SignerInfoGenerator( SignerIdentifier signerIdentifier, ContentSigner signer, DigestCalculatorProvider digesterProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder, boolean isDirectSignature)51 SignerInfoGenerator( 52 SignerIdentifier signerIdentifier, 53 ContentSigner signer, 54 DigestCalculatorProvider digesterProvider, 55 CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder, 56 boolean isDirectSignature) 57 throws OperatorCreationException 58 { 59 this.signerIdentifier = signerIdentifier; 60 this.signer = signer; 61 62 if (digesterProvider != null) 63 { 64 this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier())); 65 } 66 else 67 { 68 this.digester = null; 69 } 70 71 if (isDirectSignature) 72 { 73 this.sAttrGen = null; 74 this.unsAttrGen = null; 75 } 76 else 77 { 78 this.sAttrGen = new DefaultSignedAttributeTableGenerator(); 79 this.unsAttrGen = null; 80 } 81 82 this.sigEncAlgFinder = sigEncAlgFinder; 83 } 84 SignerInfoGenerator( SignerInfoGenerator original, CMSAttributeTableGenerator sAttrGen, CMSAttributeTableGenerator unsAttrGen)85 public SignerInfoGenerator( 86 SignerInfoGenerator original, 87 CMSAttributeTableGenerator sAttrGen, 88 CMSAttributeTableGenerator unsAttrGen) 89 { 90 this.signerIdentifier = original.signerIdentifier; 91 this.signer = original.signer; 92 this.digester = original.digester; 93 this.sigEncAlgFinder = original.sigEncAlgFinder; 94 this.sAttrGen = sAttrGen; 95 this.unsAttrGen = unsAttrGen; 96 } 97 SignerInfoGenerator( SignerIdentifier signerIdentifier, ContentSigner signer, DigestCalculatorProvider digesterProvider, CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder, CMSAttributeTableGenerator sAttrGen, CMSAttributeTableGenerator unsAttrGen)98 SignerInfoGenerator( 99 SignerIdentifier signerIdentifier, 100 ContentSigner signer, 101 DigestCalculatorProvider digesterProvider, 102 CMSSignatureEncryptionAlgorithmFinder sigEncAlgFinder, 103 CMSAttributeTableGenerator sAttrGen, 104 CMSAttributeTableGenerator unsAttrGen) 105 throws OperatorCreationException 106 { 107 this.signerIdentifier = signerIdentifier; 108 this.signer = signer; 109 110 if (digesterProvider != null) 111 { 112 this.digester = digesterProvider.get(digAlgFinder.find(signer.getAlgorithmIdentifier())); 113 } 114 else 115 { 116 this.digester = null; 117 } 118 119 this.sAttrGen = sAttrGen; 120 this.unsAttrGen = unsAttrGen; 121 this.sigEncAlgFinder = sigEncAlgFinder; 122 } 123 getSID()124 public SignerIdentifier getSID() 125 { 126 return signerIdentifier; 127 } 128 getGeneratedVersion()129 public int getGeneratedVersion() 130 { 131 return signerIdentifier.isTagged() ? 3 : 1; 132 } 133 hasAssociatedCertificate()134 public boolean hasAssociatedCertificate() 135 { 136 return certHolder != null; 137 } 138 getAssociatedCertificate()139 public X509CertificateHolder getAssociatedCertificate() 140 { 141 return certHolder; 142 } 143 getDigestAlgorithm()144 public AlgorithmIdentifier getDigestAlgorithm() 145 { 146 if (digester != null) 147 { 148 return digester.getAlgorithmIdentifier(); 149 } 150 151 return digAlgFinder.find(signer.getAlgorithmIdentifier()); 152 } 153 getCalculatingOutputStream()154 public OutputStream getCalculatingOutputStream() 155 { 156 if (digester != null) 157 { 158 if (sAttrGen == null) 159 { 160 return new TeeOutputStream(digester.getOutputStream(), signer.getOutputStream()); 161 } 162 return digester.getOutputStream(); 163 } 164 else 165 { 166 return signer.getOutputStream(); 167 } 168 } 169 generate(ASN1ObjectIdentifier contentType)170 public SignerInfo generate(ASN1ObjectIdentifier contentType) 171 throws CMSException 172 { 173 try 174 { 175 /* RFC 3852 5.4 176 * The result of the message digest calculation process depends on 177 * whether the signedAttrs field is present. When the field is absent, 178 * the result is just the message digest of the content as described 179 * 180 * above. When the field is present, however, the result is the message 181 * digest of the complete DER encoding of the SignedAttrs value 182 * contained in the signedAttrs field. 183 */ 184 ASN1Set signedAttr = null; 185 186 AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier()); 187 188 AlgorithmIdentifier digestAlg = null; 189 190 if (sAttrGen != null) 191 { 192 digestAlg = digester.getAlgorithmIdentifier(); 193 calculatedDigest = digester.getDigest(); 194 Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), digestEncryptionAlgorithm, calculatedDigest); 195 AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters)); 196 197 signedAttr = getAttributeSet(signed); 198 199 // sig must be composed from the DER encoding. 200 OutputStream sOut = signer.getOutputStream(); 201 202 sOut.write(signedAttr.getEncoded(ASN1Encoding.DER)); 203 204 sOut.close(); 205 } 206 else 207 { 208 if (digester != null) 209 { 210 digestAlg = digester.getAlgorithmIdentifier(); 211 calculatedDigest = digester.getDigest(); 212 } 213 else 214 { 215 digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier()); 216 calculatedDigest = null; 217 } 218 } 219 220 byte[] sigBytes = signer.getSignature(); 221 222 ASN1Set unsignedAttr = null; 223 if (unsAttrGen != null) 224 { 225 Map parameters = getBaseParameters(contentType, digestAlg, digestEncryptionAlgorithm, calculatedDigest); 226 parameters.put(CMSAttributeTableGenerator.SIGNATURE, Arrays.clone(sigBytes)); 227 228 AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters)); 229 230 unsignedAttr = getAttributeSet(unsigned); 231 } 232 233 return new SignerInfo(signerIdentifier, digestAlg, 234 signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr); 235 } 236 catch (IOException e) 237 { 238 throw new CMSException("encoding error.", e); 239 } 240 } 241 setAssociatedCertificate(X509CertificateHolder certHolder)242 void setAssociatedCertificate(X509CertificateHolder certHolder) 243 { 244 this.certHolder = certHolder; 245 } 246 getAttributeSet( AttributeTable attr)247 private ASN1Set getAttributeSet( 248 AttributeTable attr) 249 { 250 if (attr != null) 251 { 252 return new DERSet(attr.toASN1EncodableVector()); 253 } 254 255 return null; 256 } 257 getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, AlgorithmIdentifier sigAlgId, byte[] hash)258 private Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, AlgorithmIdentifier sigAlgId, byte[] hash) 259 { 260 Map param = new HashMap(); 261 262 if (contentType != null) 263 { 264 param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType); 265 } 266 267 param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId); 268 param.put(CMSAttributeTableGenerator.SIGNATURE_ALGORITHM_IDENTIFIER, sigAlgId); 269 param.put(CMSAttributeTableGenerator.DIGEST, Arrays.clone(hash)); 270 271 return param; 272 } 273 getCalculatedDigest()274 public byte[] getCalculatedDigest() 275 { 276 if (calculatedDigest != null) 277 { 278 return Arrays.clone(calculatedDigest); 279 } 280 281 return null; 282 } 283 getSignedAttributeTableGenerator()284 public CMSAttributeTableGenerator getSignedAttributeTableGenerator() 285 { 286 return sAttrGen; 287 } 288 getUnsignedAttributeTableGenerator()289 public CMSAttributeTableGenerator getUnsignedAttributeTableGenerator() 290 { 291 return unsAttrGen; 292 } 293 } 294