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.ASN1ObjectIdentifier; 11import org.bouncycastle.asn1.ASN1Set; 12import org.bouncycastle.asn1.DERObjectIdentifier; 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 boolean hasAssociatedCertificate() 125 { 126 return certHolder != null; 127 } 128 129 public X509CertificateHolder getAssociatedCertificate() 130 { 131 return certHolder; 132 } 133 134 public AlgorithmIdentifier getDigestAlgorithm() 135 { 136 if (digester != null) 137 { 138 return digester.getAlgorithmIdentifier(); 139 } 140 141 return digAlgFinder.find(signer.getAlgorithmIdentifier()); 142 } 143 144 public OutputStream getCalculatingOutputStream() 145 { 146 if (digester != null) 147 { 148 if (sAttrGen == null) 149 { 150 return new TeeOutputStream(digester.getOutputStream(), signer.getOutputStream()); 151 } 152 return digester.getOutputStream(); 153 } 154 else 155 { 156 return signer.getOutputStream(); 157 } 158 } 159 160 public SignerInfo generate(ASN1ObjectIdentifier contentType) 161 throws CMSException 162 { 163 try 164 { 165 /* RFC 3852 5.4 166 * The result of the message digest calculation process depends on 167 * whether the signedAttrs field is present. When the field is absent, 168 * the result is just the message digest of the content as described 169 * 170 * above. When the field is present, however, the result is the message 171 * digest of the complete DER encoding of the SignedAttrs value 172 * contained in the signedAttrs field. 173 */ 174 ASN1Set signedAttr = null; 175 176 AlgorithmIdentifier digestAlg = null; 177 178 if (sAttrGen != null) 179 { 180 digestAlg = digester.getAlgorithmIdentifier(); 181 calculatedDigest = digester.getDigest(); 182 Map parameters = getBaseParameters(contentType, digester.getAlgorithmIdentifier(), calculatedDigest); 183 AttributeTable signed = sAttrGen.getAttributes(Collections.unmodifiableMap(parameters)); 184 185 signedAttr = getAttributeSet(signed); 186 187 // sig must be composed from the DER encoding. 188 OutputStream sOut = signer.getOutputStream(); 189 190 sOut.write(signedAttr.getEncoded(ASN1Encoding.DER)); 191 192 sOut.close(); 193 } 194 else 195 { 196 if (digester != null) 197 { 198 digestAlg = digester.getAlgorithmIdentifier(); 199 calculatedDigest = digester.getDigest(); 200 } 201 else 202 { 203 digestAlg = digAlgFinder.find(signer.getAlgorithmIdentifier()); 204 calculatedDigest = null; 205 } 206 } 207 208 byte[] sigBytes = signer.getSignature(); 209 210 ASN1Set unsignedAttr = null; 211 if (unsAttrGen != null) 212 { 213 Map parameters = getBaseParameters(contentType, digestAlg, calculatedDigest); 214 parameters.put(CMSAttributeTableGenerator.SIGNATURE, sigBytes.clone()); 215 216 AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters)); 217 218 unsignedAttr = getAttributeSet(unsigned); 219 } 220 221 AlgorithmIdentifier digestEncryptionAlgorithm = sigEncAlgFinder.findEncryptionAlgorithm(signer.getAlgorithmIdentifier()); 222 223 return new SignerInfo(signerIdentifier, digestAlg, 224 signedAttr, digestEncryptionAlgorithm, new DEROctetString(sigBytes), unsignedAttr); 225 } 226 catch (IOException e) 227 { 228 throw new CMSException("encoding error.", e); 229 } 230 } 231 232 void setAssociatedCertificate(X509CertificateHolder certHolder) 233 { 234 this.certHolder = certHolder; 235 } 236 237 private ASN1Set getAttributeSet( 238 AttributeTable attr) 239 { 240 if (attr != null) 241 { 242 return new DERSet(attr.toASN1EncodableVector()); 243 } 244 245 return null; 246 } 247 248 private Map getBaseParameters(DERObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash) 249 { 250 Map param = new HashMap(); 251 252 if (contentType != null) 253 { 254 param.put(CMSAttributeTableGenerator.CONTENT_TYPE, contentType); 255 } 256 257 param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId); 258 param.put(CMSAttributeTableGenerator.DIGEST, hash.clone()); 259 return param; 260 } 261 262 public byte[] getCalculatedDigest() 263 { 264 if (calculatedDigest != null) 265 { 266 return (byte[])calculatedDigest.clone(); 267 } 268 269 return null; 270 } 271 272 public CMSAttributeTableGenerator getSignedAttributeTableGenerator() 273 { 274 return sAttrGen; 275 } 276 277 public CMSAttributeTableGenerator getUnsignedAttributeTableGenerator() 278 { 279 return unsAttrGen; 280 } 281} 282