Skip to content

Commit 839f01d

Browse files
committedOct 21, 2020
8242068: Signed JAR support for RSASSA-PSS and EdDSA
Reviewed-by: valeriep
1 parent e559bd2 commit 839f01d

File tree

24 files changed

+1311
-737
lines changed

24 files changed

+1311
-737
lines changed
 

‎src/java.base/share/classes/sun/security/pkcs/ContentInfo.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ public byte[] getContentBytes() throws IOException {
196196
if (content == null)
197197
return null;
198198

199-
DerInputStream dis = new DerInputStream(content.toByteArray());
200-
return dis.getOctetString();
199+
DerValue v = new DerValue(content.toByteArray());
200+
return v.getOctetString();
201201
}
202202

203203
public String toString() {

‎src/java.base/share/classes/sun/security/pkcs/PKCS7.java

+187-47
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,22 @@
2828
import java.io.*;
2929
import java.math.BigInteger;
3030
import java.net.URI;
31+
import java.security.interfaces.EdECPrivateKey;
32+
import java.security.spec.InvalidParameterSpecException;
33+
import java.security.spec.PSSParameterSpec;
3134
import java.util.*;
3235
import java.security.cert.X509Certificate;
3336
import java.security.cert.CertificateException;
3437
import java.security.cert.X509CRL;
3538
import java.security.cert.CRLException;
3639
import java.security.cert.CertificateFactory;
3740
import java.security.*;
41+
import java.util.function.Function;
3842

43+
import sun.security.provider.SHAKE256;
3944
import sun.security.timestamp.*;
4045
import 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

Comments
 (0)
Please sign in to comment.