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