2828import java .io .*;
2929import java .math .BigInteger ;
3030import java .net .URI ;
31+ import java .security .interfaces .EdECPrivateKey ;
32+ import java .security .spec .InvalidParameterSpecException ;
33+ import java .security .spec .PSSParameterSpec ;
3134import java .util .*;
3235import java .security .cert .X509Certificate ;
3336import java .security .cert .CertificateException ;
3437import java .security .cert .X509CRL ;
3538import java .security .cert .CRLException ;
3639import java .security .cert .CertificateFactory ;
3740import java .security .*;
41+ import java .util .function .Function ;
3842
43+ import sun .security .provider .SHAKE256 ;
3944import sun .security .timestamp .*;
4045import sun .security .util .*;
41- import sun .security .x509 .AlgorithmId ;
42- import sun .security .x509 .X509CertImpl ;
43- import sun .security .x509 .X509CertInfo ;
44- import sun .security .x509 .X509CRLImpl ;
45- import sun .security .x509 .X500Name ;
46+ import sun .security .x509 .*;
4647
4748/**
4849 * PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
@@ -86,16 +87,6 @@ private static class SecureRandomHolder {
8687 }
8788 }
8889
89- /*
90- * Object identifier for the timestamping key purpose.
91- */
92- private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8" ;
93-
94- /*
95- * Object identifier for extendedKeyUsage extension
96- */
97- private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37" ;
98-
9990 /**
10091 * Unmarshals a PKCS7 block from its encoded form, parsing the
10192 * encoded bytes from the InputStream.
@@ -178,9 +169,9 @@ private void parse(DerInputStream derin)
178169 private void parse (DerInputStream derin , boolean oldStyle )
179170 throws IOException
180171 {
181- contentInfo = new ContentInfo (derin , oldStyle );
182- contentType = contentInfo .contentType ;
183- DerValue content = contentInfo .getContent ();
172+ ContentInfo block = new ContentInfo (derin , oldStyle );
173+ contentType = block .contentType ;
174+ DerValue content = block .getContent ();
184175
185176 if (contentType .equals (ContentInfo .SIGNED_DATA_OID )) {
186177 parseSignedData (content );
@@ -189,6 +180,7 @@ private void parse(DerInputStream derin, boolean oldStyle)
189180 parseOldSignedData (content );
190181 } else if (contentType .equals (ContentInfo .NETSCAPE_CERT_SEQUENCE_OID )){
191182 parseNetscapeCertChain (content );
183+ contentInfo = block ; // Maybe useless, just do not let it be null
192184 } else {
193185 throw new ParsingException ("content type " + contentType +
194186 " not supported." );
@@ -773,6 +765,128 @@ public boolean isOldStyle() {
773765 return this .oldStyle ;
774766 }
775767
768+ /**
769+ * Generate a PKCS7 data block.
770+ *
771+ * @param sigalg signature algorithm to be used
772+ * @param sigProvider (optional) provider
773+ * @param privateKey signer's private ky
774+ * @param signerChain signer's certificate chain
775+ * @param content the content to sign
776+ * @param internalsf whether the content should be include in output
777+ * @param directsign if the content is signed directly or thru authattrs
778+ * @param ts (optional) timestamper
779+ * @return the pkcs7 output in an array
780+ * @throws SignatureException if signing failed
781+ * @throws InvalidKeyException if key cannot be used
782+ * @throws IOException should not happen here, all byte array
783+ * @throws NoSuchAlgorithmException if siglag is bad
784+ */
785+ public static byte [] generateNewSignedData (
786+ String sigalg , Provider sigProvider ,
787+ PrivateKey privateKey , X509Certificate [] signerChain ,
788+ byte [] content , boolean internalsf , boolean directsign ,
789+ Function <byte [], PKCS9Attributes > ts )
790+ throws SignatureException , InvalidKeyException , IOException ,
791+ NoSuchAlgorithmException {
792+
793+ Signature signer = SignatureUtil .fromKey (sigalg , privateKey , sigProvider );
794+
795+ AlgorithmId digAlgID = SignatureUtil .getDigestAlgInPkcs7SignerInfo (
796+ signer , sigalg , privateKey , directsign );
797+ AlgorithmId sigAlgID = SignatureUtil .fromSignature (signer , privateKey );
798+
799+ PKCS9Attributes authAttrs = null ;
800+ if (!directsign ) {
801+ // MessageDigest
802+ byte [] md ;
803+ String digAlgName = digAlgID .getName ();
804+ if (digAlgName .equals ("SHAKE256" ) || digAlgName .equals ("SHAKE256-LEN" )) {
805+ // No MessageDigest impl for SHAKE256 yet
806+ var shaker = new SHAKE256 (64 );
807+ shaker .update (content , 0 , content .length );
808+ md = shaker .digest ();
809+ } else {
810+ md = MessageDigest .getInstance (digAlgName )
811+ .digest (content );
812+ }
813+ // CMSAlgorithmProtection (RFC6211)
814+ DerOutputStream derAp = new DerOutputStream ();
815+ DerOutputStream derAlgs = new DerOutputStream ();
816+ digAlgID .derEncode (derAlgs );
817+ DerOutputStream derSigAlg = new DerOutputStream ();
818+ sigAlgID .derEncode (derSigAlg );
819+ derAlgs .writeImplicit ((byte )0xA1 , derSigAlg );
820+ derAp .write (DerValue .tag_Sequence , derAlgs );
821+ authAttrs = new PKCS9Attributes (new PKCS9Attribute []{
822+ new PKCS9Attribute (PKCS9Attribute .CONTENT_TYPE_OID ,
823+ ContentInfo .DATA_OID ),
824+ new PKCS9Attribute (PKCS9Attribute .SIGNING_TIME_OID ,
825+ new Date ()),
826+ new PKCS9Attribute (PKCS9Attribute .CMS_ALGORITHM_PROTECTION_OID ,
827+ derAp .toByteArray ()),
828+ new PKCS9Attribute (PKCS9Attribute .MESSAGE_DIGEST_OID ,
829+ md )
830+ });
831+ signer .update (authAttrs .getDerEncoding ());
832+ } else {
833+ signer .update (content );
834+ }
835+
836+ byte [] signature = signer .sign ();
837+
838+ return constructToken (signature , signerChain ,
839+ internalsf ? content : null ,
840+ authAttrs ,
841+ ts == null ? null : ts .apply (signature ),
842+ digAlgID ,
843+ sigAlgID );
844+ }
845+
846+ /**
847+ * Assemble a PKCS7 token from its components
848+ * @param signature the signature
849+ * @param signerChain the signer's certificate chain
850+ * @param content (optional) encapsulated content
851+ * @param authAttrs (optional) authenticated attributes
852+ * @param unauthAttrs (optional) unauthenticated attributes
853+ * @param digAlgID digest algorithm identifier
854+ * @param encAlgID encryption algorithm identifier
855+ * @return the token in a byte array
856+ * @throws IOException should not happen here, all byte array
857+ */
858+ private static byte [] constructToken (byte [] signature ,
859+ X509Certificate [] signerChain ,
860+ byte [] content ,
861+ PKCS9Attributes authAttrs ,
862+ PKCS9Attributes unauthAttrs ,
863+ AlgorithmId digAlgID ,
864+ AlgorithmId encAlgID )
865+ throws IOException {
866+ // Create the SignerInfo
867+ X500Name issuerName =
868+ X500Name .asX500Name (signerChain [0 ].getIssuerX500Principal ());
869+ BigInteger serialNumber = signerChain [0 ].getSerialNumber ();
870+ SignerInfo signerInfo = new SignerInfo (issuerName , serialNumber ,
871+ digAlgID , authAttrs ,
872+ encAlgID ,
873+ signature , unauthAttrs );
874+
875+ // Create the PKCS #7 signed data message
876+ SignerInfo [] signerInfos = {signerInfo };
877+ AlgorithmId [] algorithms = {signerInfo .getDigestAlgorithmId ()};
878+ // Include or exclude content
879+ ContentInfo contentInfo = (content == null )
880+ ? new ContentInfo (ContentInfo .DATA_OID , null )
881+ : new ContentInfo (content );
882+ PKCS7 pkcs7 = new PKCS7 (algorithms , contentInfo ,
883+ signerChain , signerInfos );
884+ ByteArrayOutputStream p7out = new ByteArrayOutputStream ();
885+ pkcs7 .encodeSignedData (p7out );
886+
887+ return p7out .toByteArray ();
888+ }
889+
776890 /**
777891 * Assembles a PKCS #7 signed data message that optionally includes a
778892 * signature timestamp.
@@ -797,6 +911,7 @@ public boolean isOldStyle() {
797911 * generating the signature timestamp or while generating the signed
798912 * data message.
799913 */
914+ @ Deprecated (since ="16" , forRemoval =true )
800915 public static byte [] generateSignedData (byte [] signature ,
801916 X509Certificate [] signerChain ,
802917 byte [] content ,
@@ -824,34 +939,59 @@ public static byte[] generateSignedData(byte[] signature,
824939 tsToken )});
825940 }
826941
827- // Create the SignerInfo
828- X500Name issuerName =
829- X500Name .asX500Name (signerChain [0 ].getIssuerX500Principal ());
830- BigInteger serialNumber = signerChain [0 ].getSerialNumber ();
831- String encAlg = AlgorithmId .getEncAlgFromSigAlg (signatureAlgorithm );
832- String digAlg = AlgorithmId .getDigAlgFromSigAlg (signatureAlgorithm );
833- if (digAlg == null ) {
834- throw new UnsupportedOperationException ("Unable to determine " +
835- "the digest algorithm from the signature algorithm." );
836- }
837- SignerInfo signerInfo = new SignerInfo (issuerName , serialNumber ,
838- AlgorithmId .get (digAlg ), null ,
839- AlgorithmId .get (encAlg ),
840- signature , unauthAttrs );
942+ return constructToken (signature , signerChain , content ,
943+ null ,
944+ unauthAttrs ,
945+ AlgorithmId .get (SignatureUtil .extractDigestAlgFromDwithE (signatureAlgorithm )),
946+ AlgorithmId .get (signatureAlgorithm ));
947+ }
841948
842- // Create the PKCS #7 signed data message
843- SignerInfo [] signerInfos = { signerInfo };
844- AlgorithmId [] algorithms = { signerInfo . getDigestAlgorithmId ()};
845- // Include or exclude content
846- ContentInfo contentInfo = ( content == null )
847- ? new ContentInfo ( ContentInfo . DATA_OID , null )
848- : new ContentInfo ( content );
849- PKCS7 pkcs7 = new PKCS7 ( algorithms , contentInfo ,
850- signerChain , signerInfos );
851- ByteArrayOutputStream p7out = new ByteArrayOutputStream ();
852- pkcs7 . encodeSignedData ( p7out );
949+ /**
950+ * Examine the certificate for a Subject Information Access extension
951+ * (<a href="http://tools.ietf.org/html/rfc5280">RFC 5280</a>).
952+ * The extension's {@code accessMethod} field should contain the object
953+ * identifier defined for timestamping: 1.3.6.1.5.5.7.48.3 and its
954+ * {@code accessLocation} field should contain an HTTP or HTTPS URL.
955+ *
956+ * @param tsaCertificate (optional) X.509 certificate for the TSA.
957+ * @return An HTTP or HTTPS URI or null if none was found.
958+ */
959+ public static URI getTimestampingURI ( X509Certificate tsaCertificate ) {
853960
854- return p7out .toByteArray ();
961+ if (tsaCertificate == null ) {
962+ return null ;
963+ }
964+ // Parse the extensions
965+ try {
966+ byte [] extensionValue = tsaCertificate .getExtensionValue
967+ (KnownOIDs .SubjectInfoAccess .value ());
968+ if (extensionValue == null ) {
969+ return null ;
970+ }
971+ DerInputStream der = new DerInputStream (extensionValue );
972+ der = new DerInputStream (der .getOctetString ());
973+ DerValue [] derValue = der .getSequence (5 );
974+ AccessDescription description ;
975+ GeneralName location ;
976+ URIName uri ;
977+ for (int i = 0 ; i < derValue .length ; i ++) {
978+ description = new AccessDescription (derValue [i ]);
979+ if (description .getAccessMethod ()
980+ .equals (ObjectIdentifier .of (KnownOIDs .AD_TimeStamping ))) {
981+ location = description .getAccessLocation ();
982+ if (location .getType () == GeneralNameInterface .NAME_URI ) {
983+ uri = (URIName ) location .getName ();
984+ if (uri .getScheme ().equalsIgnoreCase ("http" ) ||
985+ uri .getScheme ().equalsIgnoreCase ("https" )) {
986+ return uri .getURI ();
987+ }
988+ }
989+ }
990+ }
991+ } catch (IOException ioe ) {
992+ // ignore
993+ }
994+ return null ;
855995 }
856996
857997 /**
@@ -873,7 +1013,7 @@ public static byte[] generateSignedData(byte[] signature,
8731013 * @throws CertificateException The exception is thrown if the TSA's
8741014 * certificate is not permitted for timestamping.
8751015 */
876- private static byte [] generateTimestampToken (Timestamper tsa ,
1016+ public static byte [] generateTimestampToken (Timestamper tsa ,
8771017 String tSAPolicyID ,
8781018 String tSADigestAlg ,
8791019 byte [] toBeTimestamped )
@@ -944,13 +1084,13 @@ private static byte[] generateTimestampToken(Timestamper tsa,
9441084 "Certificate not included in timestamp token" );
9451085 } else {
9461086 if (!cert .getCriticalExtensionOIDs ().contains (
947- EXTENDED_KEY_USAGE_OID )) {
1087+ KnownOIDs . extendedKeyUsage . value () )) {
9481088 throw new CertificateException (
9491089 "Certificate is not valid for timestamping" );
9501090 }
9511091 List <String > keyPurposes = cert .getExtendedKeyUsage ();
9521092 if (keyPurposes == null ||
953- !keyPurposes .contains (KP_TIMESTAMPING_OID )) {
1093+ !keyPurposes .contains (KnownOIDs . KP_TimeStamping . value () )) {
9541094 throw new CertificateException (
9551095 "Certificate is not valid for timestamping" );
9561096 }
0 commit comments