1package org.bouncycastle.cms; 2 3import java.io.IOException; 4import java.io.OutputStream; 5import java.util.Collections; 6import java.util.HashMap; 7import java.util.Map; 8 9import org.bouncycastle.asn1.ASN1Encoding; 10import org.bouncycastle.asn1.ASN1Integer; 11import org.bouncycastle.asn1.ASN1ObjectIdentifier; 12import org.bouncycastle.asn1.ASN1Set; 13import org.bouncycastle.asn1.DEROctetString; 14import org.bouncycastle.asn1.DERSet; 15import org.bouncycastle.asn1.cms.AttributeTable; 16import org.bouncycastle.asn1.cms.SignerIdentifier; 17import org.bouncycastle.asn1.cms.SignerInfo; 18import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 19import org.bouncycastle.cert.X509CertificateHolder; 20import org.bouncycastle.operator.ContentSigner; 21import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; 22import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder; 23import org.bouncycastle.operator.DigestCalculator; 24import org.bouncycastle.operator.DigestCalculatorProvider; 25import org.bouncycastle.operator.OperatorCreationException; 26import org.bouncycastle.util.io.TeeOutputStream; 27 28public 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 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 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 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 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 124 public SignerIdentifier getSID() 125 { 126 return signerIdentifier; 127 } 128 129 public ASN1Integer getGeneratedVersion() 130 { 131 return new ASN1Integer(signerIdentifier.isTagged() ? 3 : 1); 132 } 133 134 public boolean hasAssociatedCertificate() 135 { 136 return certHolder != null; 137 } 138 139 public X509CertificateHolder getAssociatedCertificate() 140 { 141 return certHolder; 142 } 143 144 public AlgorithmIdentifier getDigestAlgorithm() 145 { 146 if (digester != null) 147 { 148 return digester.getAlgorithmIdentifier(); 149 } 150 151 return digAlgFinder.find(signer.getAlgorithmIdentifier()); 152 } 153 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 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 digestAlg = null; 187 188 if (sAttrGen != null) 189 { 190 digestAlg = digester.getAlgorithmIdentifier(); 191 calculatedDigest = digester.getDigest(); 192 Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), calculatedDigest); 193 AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters)); 194 195 signedAttr = getAttributeSet(signed); 196 197 // sig must be composed from the DER encoding. 198 OutputStream sOut = signer.getOutputStream(); 199 200 sOut.write(signedAttr.getEncoded(ASN1Encoding.DER)); 201 202 sOut.close(); 203 } 204 else 205 { 206 if (digester != null) 207 { 208 digestAlg = digester.getAlgorithmIdentifier(); 209 calculatedDigest = digester.getDigest(); 210 } 211 else 212 { 213 digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier()); 214 calculatedDigest = null; 215 } 216 } 217 218 byte[] sigBytes = signer.getSignature(); 219 220 ASN1Set unsignedAttr = null; 221 if (unsAttrGen != null) 222 { 223 Map parameters = getBaseParameters(contentType, digestAlg, calculatedDigest); 224 parameters.put(CMSAttributeTableGenerator.SIGNATURE, sigBytes.clone()); 225 226 AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters)); 227 228 unsignedAttr = getAttributeSet(unsigned); 229 } 230 231 AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier()); 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 242 void setAssociatedCertificate(X509CertificateHolder certHolder) 243 { 244 this.certHolder = certHolder; 245 } 246 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 258 private Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, 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.DIGEST, hash.clone()); 269 return param; 270 } 271 272 public byte[] getCalculatedDigest() 273 { 274 if (calculatedDigest != null) 275 { 276 return (byte[])calculatedDigest.clone(); 277 } 278 279 return null; 280 } 281 282 public CMSAttributeTableGenerator getSignedAttributeTableGenerator() 283 { 284 return sAttrGen; 285 } 286 287 public CMSAttributeTableGenerator getUnsignedAttributeTableGenerator() 288 { 289 return unsAttrGen; 290 } 291} 292