|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.pkcs; |
|
|
|
import java.io.*; |
|
import java.math.BigInteger; |
|
import java.net.URI; |
|
import java.util.*; |
|
import java.security.cert.X509Certificate; |
|
import java.security.cert.CertificateException; |
|
import java.security.cert.X509CRL; |
|
import java.security.cert.CRLException; |
|
import java.security.cert.CertificateFactory; |
|
import java.security.*; |
|
import java.util.function.Function; |
|
|
|
import sun.security.provider.SHAKE256; |
|
import sun.security.timestamp.*; |
|
import sun.security.util.*; |
|
import sun.security.x509.*; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class PKCS7 { |
|
|
|
private ObjectIdentifier contentType; |
|
|
|
|
|
private BigInteger version = null; |
|
private AlgorithmId[] digestAlgorithmIds = null; |
|
private ContentInfo contentInfo = null; |
|
private X509Certificate[] certificates = null; |
|
private X509CRL[] crls = null; |
|
private SignerInfo[] signerInfos = null; |
|
|
|
private boolean oldStyle = false; |
|
|
|
private Principal[] certIssuerNames; |
|
|
|
|
|
|
|
|
|
*/ |
|
private static class SecureRandomHolder { |
|
static final SecureRandom RANDOM; |
|
static { |
|
SecureRandom tmp = null; |
|
try { |
|
tmp = SecureRandom.getInstance("SHA1PRNG"); |
|
} catch (NoSuchAlgorithmException e) { |
|
// should not happen |
|
} |
|
RANDOM = tmp; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public PKCS7(InputStream in) throws ParsingException, IOException { |
|
DataInputStream dis = new DataInputStream(in); |
|
byte[] data = new byte[dis.available()]; |
|
dis.readFully(data); |
|
|
|
parse(new DerInputStream(data)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public PKCS7(DerInputStream derin) throws ParsingException { |
|
parse(derin); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public PKCS7(byte[] bytes) throws ParsingException { |
|
try { |
|
DerInputStream derin = new DerInputStream(bytes); |
|
parse(derin); |
|
} catch (IOException ioe1) { |
|
ParsingException pe = new ParsingException( |
|
"Unable to parse the encoded bytes"); |
|
pe.initCause(ioe1); |
|
throw pe; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void parse(DerInputStream derin) |
|
throws ParsingException |
|
{ |
|
try { |
|
derin.mark(derin.available()); |
|
|
|
parse(derin, false); |
|
} catch (IOException ioe) { |
|
try { |
|
derin.reset(); |
|
|
|
parse(derin, true); |
|
oldStyle = true; |
|
} catch (IOException ioe1) { |
|
ParsingException pe = new ParsingException( |
|
ioe1.getMessage()); |
|
pe.initCause(ioe); |
|
pe.addSuppressed(ioe1); |
|
throw pe; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void parse(DerInputStream derin, boolean oldStyle) |
|
throws IOException |
|
{ |
|
ContentInfo block = new ContentInfo(derin, oldStyle); |
|
contentType = block.contentType; |
|
DerValue content = block.getContent(); |
|
|
|
if (contentType.equals(ContentInfo.SIGNED_DATA_OID)) { |
|
parseSignedData(content); |
|
} else if (contentType.equals(ContentInfo.OLD_SIGNED_DATA_OID)) { |
|
|
|
parseOldSignedData(content); |
|
} else if (contentType.equals(ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){ |
|
parseNetscapeCertChain(content); |
|
contentInfo = block; |
|
} else { |
|
throw new ParsingException("content type " + contentType + |
|
" not supported."); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public PKCS7(AlgorithmId[] digestAlgorithmIds, |
|
ContentInfo contentInfo, |
|
X509Certificate[] certificates, |
|
X509CRL[] crls, |
|
SignerInfo[] signerInfos) { |
|
|
|
version = BigInteger.ONE; |
|
this.digestAlgorithmIds = digestAlgorithmIds; |
|
this.contentInfo = contentInfo; |
|
this.certificates = certificates; |
|
this.crls = crls; |
|
this.signerInfos = signerInfos; |
|
} |
|
|
|
public PKCS7(AlgorithmId[] digestAlgorithmIds, |
|
ContentInfo contentInfo, |
|
X509Certificate[] certificates, |
|
SignerInfo[] signerInfos) { |
|
this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos); |
|
} |
|
|
|
private void parseNetscapeCertChain(DerValue val) |
|
throws ParsingException, IOException { |
|
DerInputStream dis = new DerInputStream(val.toByteArray()); |
|
DerValue[] contents = dis.getSequence(2); |
|
certificates = new X509Certificate[contents.length]; |
|
|
|
CertificateFactory certfac = null; |
|
try { |
|
certfac = CertificateFactory.getInstance("X.509"); |
|
} catch (CertificateException ce) { |
|
// do nothing |
|
} |
|
|
|
for (int i=0; i < contents.length; i++) { |
|
ByteArrayInputStream bais = null; |
|
try { |
|
if (certfac == null) |
|
certificates[i] = new X509CertImpl(contents[i]); |
|
else { |
|
byte[] encoded = contents[i].toByteArray(); |
|
bais = new ByteArrayInputStream(encoded); |
|
certificates[i] = |
|
(X509Certificate)certfac.generateCertificate(bais); |
|
bais.close(); |
|
bais = null; |
|
} |
|
} catch (CertificateException ce) { |
|
ParsingException pe = new ParsingException(ce.getMessage()); |
|
pe.initCause(ce); |
|
throw pe; |
|
} catch (IOException ioe) { |
|
ParsingException pe = new ParsingException(ioe.getMessage()); |
|
pe.initCause(ioe); |
|
throw pe; |
|
} finally { |
|
if (bais != null) |
|
bais.close(); |
|
} |
|
} |
|
} |
|
|
|
private void parseSignedData(DerValue val) |
|
throws ParsingException, IOException { |
|
|
|
DerInputStream dis = val.toDerInputStream(); |
|
|
|
|
|
version = dis.getBigInteger(); |
|
|
|
|
|
DerValue[] digestAlgorithmIdVals = dis.getSet(1); |
|
int len = digestAlgorithmIdVals.length; |
|
digestAlgorithmIds = new AlgorithmId[len]; |
|
try { |
|
for (int i = 0; i < len; i++) { |
|
DerValue oid = digestAlgorithmIdVals[i]; |
|
digestAlgorithmIds[i] = AlgorithmId.parse(oid); |
|
} |
|
|
|
} catch (IOException e) { |
|
ParsingException pe = |
|
new ParsingException("Error parsing digest AlgorithmId IDs: " + |
|
e.getMessage()); |
|
pe.initCause(e); |
|
throw pe; |
|
} |
|
|
|
contentInfo = new ContentInfo(dis); |
|
|
|
CertificateFactory certfac = null; |
|
try { |
|
certfac = CertificateFactory.getInstance("X.509"); |
|
} catch (CertificateException ce) { |
|
// do nothing |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
if ((byte)(dis.peekByte()) == (byte)0xA0) { |
|
DerValue[] certVals = dis.getSet(2, true); |
|
|
|
len = certVals.length; |
|
certificates = new X509Certificate[len]; |
|
int count = 0; |
|
|
|
for (int i = 0; i < len; i++) { |
|
ByteArrayInputStream bais = null; |
|
try { |
|
byte tag = certVals[i].getTag(); |
|
// We only parse the normal certificate. Other types of |
|
|
|
if (tag == DerValue.tag_Sequence) { |
|
if (certfac == null) { |
|
certificates[count] = new X509CertImpl(certVals[i]); |
|
} else { |
|
byte[] encoded = certVals[i].toByteArray(); |
|
bais = new ByteArrayInputStream(encoded); |
|
certificates[count] = |
|
(X509Certificate)certfac.generateCertificate(bais); |
|
bais.close(); |
|
bais = null; |
|
} |
|
count++; |
|
} |
|
} catch (CertificateException ce) { |
|
ParsingException pe = new ParsingException(ce.getMessage()); |
|
pe.initCause(ce); |
|
throw pe; |
|
} catch (IOException ioe) { |
|
ParsingException pe = new ParsingException(ioe.getMessage()); |
|
pe.initCause(ioe); |
|
throw pe; |
|
} finally { |
|
if (bais != null) |
|
bais.close(); |
|
} |
|
} |
|
if (count != len) { |
|
certificates = Arrays.copyOf(certificates, count); |
|
} |
|
} |
|
|
|
|
|
if ((byte)(dis.peekByte()) == (byte)0xA1) { |
|
DerValue[] crlVals = dis.getSet(1, true); |
|
|
|
len = crlVals.length; |
|
crls = new X509CRL[len]; |
|
|
|
for (int i = 0; i < len; i++) { |
|
ByteArrayInputStream bais = null; |
|
try { |
|
if (certfac == null) |
|
crls[i] = new X509CRLImpl(crlVals[i]); |
|
else { |
|
byte[] encoded = crlVals[i].toByteArray(); |
|
bais = new ByteArrayInputStream(encoded); |
|
crls[i] = (X509CRL) certfac.generateCRL(bais); |
|
bais.close(); |
|
bais = null; |
|
} |
|
} catch (CRLException e) { |
|
ParsingException pe = |
|
new ParsingException(e.getMessage()); |
|
pe.initCause(e); |
|
throw pe; |
|
} finally { |
|
if (bais != null) |
|
bais.close(); |
|
} |
|
} |
|
} |
|
|
|
|
|
DerValue[] signerInfoVals = dis.getSet(1); |
|
|
|
len = signerInfoVals.length; |
|
signerInfos = new SignerInfo[len]; |
|
|
|
for (int i = 0; i < len; i++) { |
|
DerInputStream in = signerInfoVals[i].toDerInputStream(); |
|
signerInfos[i] = new SignerInfo(in); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void parseOldSignedData(DerValue val) |
|
throws ParsingException, IOException |
|
{ |
|
DerInputStream dis = val.toDerInputStream(); |
|
|
|
|
|
version = dis.getBigInteger(); |
|
|
|
|
|
DerValue[] digestAlgorithmIdVals = dis.getSet(1); |
|
int len = digestAlgorithmIdVals.length; |
|
|
|
digestAlgorithmIds = new AlgorithmId[len]; |
|
try { |
|
for (int i = 0; i < len; i++) { |
|
DerValue oid = digestAlgorithmIdVals[i]; |
|
digestAlgorithmIds[i] = AlgorithmId.parse(oid); |
|
} |
|
} catch (IOException e) { |
|
throw new ParsingException("Error parsing digest AlgorithmId IDs"); |
|
} |
|
|
|
|
|
contentInfo = new ContentInfo(dis, true); |
|
|
|
|
|
CertificateFactory certfac = null; |
|
try { |
|
certfac = CertificateFactory.getInstance("X.509"); |
|
} catch (CertificateException ce) { |
|
// do nothing |
|
} |
|
DerValue[] certVals = dis.getSet(2); |
|
len = certVals.length; |
|
certificates = new X509Certificate[len]; |
|
|
|
for (int i = 0; i < len; i++) { |
|
ByteArrayInputStream bais = null; |
|
try { |
|
if (certfac == null) |
|
certificates[i] = new X509CertImpl(certVals[i]); |
|
else { |
|
byte[] encoded = certVals[i].toByteArray(); |
|
bais = new ByteArrayInputStream(encoded); |
|
certificates[i] = |
|
(X509Certificate)certfac.generateCertificate(bais); |
|
bais.close(); |
|
bais = null; |
|
} |
|
} catch (CertificateException ce) { |
|
ParsingException pe = new ParsingException(ce.getMessage()); |
|
pe.initCause(ce); |
|
throw pe; |
|
} catch (IOException ioe) { |
|
ParsingException pe = new ParsingException(ioe.getMessage()); |
|
pe.initCause(ioe); |
|
throw pe; |
|
} finally { |
|
if (bais != null) |
|
bais.close(); |
|
} |
|
} |
|
|
|
|
|
dis.getSet(0); |
|
|
|
|
|
DerValue[] signerInfoVals = dis.getSet(1); |
|
len = signerInfoVals.length; |
|
signerInfos = new SignerInfo[len]; |
|
for (int i = 0; i < len; i++) { |
|
DerInputStream in = signerInfoVals[i].toDerInputStream(); |
|
signerInfos[i] = new SignerInfo(in, true); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void encodeSignedData(OutputStream out) throws IOException { |
|
DerOutputStream derout = new DerOutputStream(); |
|
encodeSignedData(derout); |
|
out.write(derout.toByteArray()); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void encodeSignedData(DerOutputStream out) |
|
throws IOException |
|
{ |
|
DerOutputStream signedData = new DerOutputStream(); |
|
|
|
|
|
signedData.putInteger(version); |
|
|
|
|
|
signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds); |
|
|
|
|
|
contentInfo.encode(signedData); |
|
|
|
|
|
if (certificates != null && certificates.length != 0) { |
|
|
|
X509CertImpl[] implCerts = new X509CertImpl[certificates.length]; |
|
for (int i = 0; i < certificates.length; i++) { |
|
if (certificates[i] instanceof X509CertImpl) |
|
implCerts[i] = (X509CertImpl) certificates[i]; |
|
else { |
|
try { |
|
byte[] encoded = certificates[i].getEncoded(); |
|
implCerts[i] = new X509CertImpl(encoded); |
|
} catch (CertificateException ce) { |
|
throw new IOException(ce); |
|
} |
|
} |
|
} |
|
|
|
// Add the certificate set (tagged with [0] IMPLICIT) |
|
|
|
signedData.putOrderedSetOf((byte)0xA0, implCerts); |
|
} |
|
|
|
|
|
if (crls != null && crls.length != 0) { |
|
|
|
Set<X509CRLImpl> implCRLs = new HashSet<>(crls.length); |
|
for (X509CRL crl: crls) { |
|
if (crl instanceof X509CRLImpl) |
|
implCRLs.add((X509CRLImpl) crl); |
|
else { |
|
try { |
|
byte[] encoded = crl.getEncoded(); |
|
implCRLs.add(new X509CRLImpl(encoded)); |
|
} catch (CRLException ce) { |
|
throw new IOException(ce); |
|
} |
|
} |
|
} |
|
|
|
// Add the CRL set (tagged with [1] IMPLICIT) |
|
|
|
signedData.putOrderedSetOf((byte)0xA1, |
|
implCRLs.toArray(new X509CRLImpl[implCRLs.size()])); |
|
} |
|
|
|
|
|
signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos); |
|
|
|
|
|
DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence, |
|
signedData.toByteArray()); |
|
|
|
|
|
ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID, |
|
signedDataSeq); |
|
|
|
|
|
block.encode(out); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SignerInfo verify(SignerInfo info, byte[] bytes) |
|
throws NoSuchAlgorithmException, SignatureException { |
|
return info.verify(this, bytes); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SignerInfo[] verify(byte[] bytes) |
|
throws NoSuchAlgorithmException, SignatureException { |
|
|
|
Vector<SignerInfo> intResult = new Vector<>(); |
|
for (int i = 0; i < signerInfos.length; i++) { |
|
|
|
SignerInfo signerInfo = verify(signerInfos[i], bytes); |
|
if (signerInfo != null) { |
|
intResult.addElement(signerInfo); |
|
} |
|
} |
|
if (!intResult.isEmpty()) { |
|
|
|
SignerInfo[] result = new SignerInfo[intResult.size()]; |
|
intResult.copyInto(result); |
|
return result; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SignerInfo[] verify() |
|
throws NoSuchAlgorithmException, SignatureException { |
|
return verify(null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public BigInteger getVersion() { |
|
return version; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public AlgorithmId[] getDigestAlgorithmIds() { |
|
return digestAlgorithmIds; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public ContentInfo getContentInfo() { |
|
return contentInfo; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509Certificate[] getCertificates() { |
|
if (certificates != null) |
|
return certificates.clone(); |
|
else |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509CRL[] getCRLs() { |
|
if (crls != null) |
|
return crls.clone(); |
|
else |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public SignerInfo[] getSignerInfos() { |
|
return signerInfos; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public X509Certificate getCertificate(BigInteger serial, X500Name issuerName) { |
|
if (certificates != null) { |
|
if (certIssuerNames == null) |
|
populateCertIssuerNames(); |
|
for (int i = 0; i < certificates.length; i++) { |
|
X509Certificate cert = certificates[i]; |
|
BigInteger thisSerial = cert.getSerialNumber(); |
|
if (serial.equals(thisSerial) |
|
&& issuerName.equals(certIssuerNames[i])) |
|
{ |
|
return cert; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("deprecation") |
|
private void populateCertIssuerNames() { |
|
if (certificates == null) |
|
return; |
|
|
|
certIssuerNames = new Principal[certificates.length]; |
|
for (int i = 0; i < certificates.length; i++) { |
|
X509Certificate cert = certificates[i]; |
|
Principal certIssuerName = cert.getIssuerDN(); |
|
if (!(certIssuerName instanceof X500Name)) { |
|
// must extract the original encoded form of DN for |
|
// subsequent name comparison checks (converting to a |
|
// String and back to an encoded DN could cause the |
|
|
|
try { |
|
X509CertInfo tbsCert = |
|
new X509CertInfo(cert.getTBSCertificate()); |
|
certIssuerName = (Principal) |
|
tbsCert.get(X509CertInfo.ISSUER + "." + |
|
X509CertInfo.DN_NAME); |
|
} catch (Exception e) { |
|
// error generating X500Name object from the cert's |
|
// issuer DN, leave name as is. |
|
} |
|
} |
|
certIssuerNames[i] = certIssuerName; |
|
} |
|
} |
|
|
|
|
|
|
|
*/ |
|
public String toString() { |
|
String out = ""; |
|
|
|
out += contentInfo + "\n"; |
|
if (version != null) |
|
out += "PKCS7 :: version: " + Debug.toHexString(version) + "\n"; |
|
if (digestAlgorithmIds != null) { |
|
out += "PKCS7 :: digest AlgorithmIds: \n"; |
|
for (int i = 0; i < digestAlgorithmIds.length; i++) |
|
out += "\t" + digestAlgorithmIds[i] + "\n"; |
|
} |
|
if (certificates != null) { |
|
out += "PKCS7 :: certificates: \n"; |
|
for (int i = 0; i < certificates.length; i++) |
|
out += "\t" + i + ". " + certificates[i] + "\n"; |
|
} |
|
if (crls != null) { |
|
out += "PKCS7 :: crls: \n"; |
|
for (int i = 0; i < crls.length; i++) |
|
out += "\t" + i + ". " + crls[i] + "\n"; |
|
} |
|
if (signerInfos != null) { |
|
out += "PKCS7 :: signer infos: \n"; |
|
for (int i = 0; i < signerInfos.length; i++) |
|
out += ("\t" + i + ". " + signerInfos[i] + "\n"); |
|
} |
|
return out; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean isOldStyle() { |
|
return this.oldStyle; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static byte[] generateNewSignedData( |
|
String sigalg, Provider sigProvider, |
|
PrivateKey privateKey, X509Certificate[] signerChain, |
|
byte[] content, boolean internalsf, boolean directsign, |
|
Function<byte[], PKCS9Attributes> ts) |
|
throws SignatureException, InvalidKeyException, IOException, |
|
NoSuchAlgorithmException { |
|
|
|
Signature signer = SignatureUtil.fromKey(sigalg, privateKey, sigProvider); |
|
|
|
AlgorithmId digAlgID = SignatureUtil.getDigestAlgInPkcs7SignerInfo( |
|
signer, sigalg, privateKey, directsign); |
|
AlgorithmId sigAlgID = SignatureUtil.fromSignature(signer, privateKey); |
|
|
|
PKCS9Attributes authAttrs = null; |
|
if (!directsign) { |
|
|
|
byte[] md; |
|
String digAlgName = digAlgID.getName(); |
|
if (digAlgName.equals("SHAKE256") || digAlgName.equals("SHAKE256-LEN")) { |
|
|
|
var shaker = new SHAKE256(64); |
|
shaker.update(content, 0, content.length); |
|
md = shaker.digest(); |
|
} else { |
|
md = MessageDigest.getInstance(digAlgName) |
|
.digest(content); |
|
} |
|
|
|
DerOutputStream derAp = new DerOutputStream(); |
|
DerOutputStream derAlgs = new DerOutputStream(); |
|
digAlgID.derEncode(derAlgs); |
|
DerOutputStream derSigAlg = new DerOutputStream(); |
|
sigAlgID.derEncode(derSigAlg); |
|
derAlgs.writeImplicit((byte)0xA1, derSigAlg); |
|
derAp.write(DerValue.tag_Sequence, derAlgs); |
|
authAttrs = new PKCS9Attributes(new PKCS9Attribute[]{ |
|
new PKCS9Attribute(PKCS9Attribute.CONTENT_TYPE_OID, |
|
ContentInfo.DATA_OID), |
|
new PKCS9Attribute(PKCS9Attribute.SIGNING_TIME_OID, |
|
new Date()), |
|
new PKCS9Attribute(PKCS9Attribute.CMS_ALGORITHM_PROTECTION_OID, |
|
derAp.toByteArray()), |
|
new PKCS9Attribute(PKCS9Attribute.MESSAGE_DIGEST_OID, |
|
md) |
|
}); |
|
signer.update(authAttrs.getDerEncoding()); |
|
} else { |
|
signer.update(content); |
|
} |
|
|
|
byte[] signature = signer.sign(); |
|
|
|
return constructToken(signature, signerChain, |
|
internalsf ? content : null, |
|
authAttrs, |
|
ts == null ? null : ts.apply(signature), |
|
digAlgID, |
|
sigAlgID); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static byte[] constructToken(byte[] signature, |
|
X509Certificate[] signerChain, |
|
byte[] content, |
|
PKCS9Attributes authAttrs, |
|
PKCS9Attributes unauthAttrs, |
|
AlgorithmId digAlgID, |
|
AlgorithmId encAlgID) |
|
throws IOException { |
|
|
|
X500Name issuerName = |
|
X500Name.asX500Name(signerChain[0].getIssuerX500Principal()); |
|
BigInteger serialNumber = signerChain[0].getSerialNumber(); |
|
SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber, |
|
digAlgID, authAttrs, |
|
encAlgID, |
|
signature, unauthAttrs); |
|
|
|
|
|
SignerInfo[] signerInfos = {signerInfo}; |
|
AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()}; |
|
|
|
ContentInfo contentInfo = (content == null) |
|
? new ContentInfo(ContentInfo.DATA_OID, null) |
|
: new ContentInfo(content); |
|
PKCS7 pkcs7 = new PKCS7(algorithms, contentInfo, |
|
signerChain, signerInfos); |
|
ByteArrayOutputStream p7out = new ByteArrayOutputStream(); |
|
pkcs7.encodeSignedData(p7out); |
|
|
|
return p7out.toByteArray(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@Deprecated(since="16", forRemoval=true) |
|
public static byte[] generateSignedData(byte[] signature, |
|
X509Certificate[] signerChain, |
|
byte[] content, |
|
String signatureAlgorithm, |
|
URI tsaURI, |
|
String tSAPolicyID, |
|
String tSADigestAlg) |
|
throws CertificateException, IOException, NoSuchAlgorithmException |
|
{ |
|
|
|
|
|
PKCS9Attributes unauthAttrs = null; |
|
if (tsaURI != null) { |
|
|
|
HttpTimestamper tsa = new HttpTimestamper(tsaURI); |
|
byte[] tsToken = generateTimestampToken( |
|
tsa, tSAPolicyID, tSADigestAlg, signature); |
|
|
|
// Insert the timestamp token into the PKCS #7 signer info element |
|
|
|
unauthAttrs = |
|
new PKCS9Attributes(new PKCS9Attribute[]{ |
|
new PKCS9Attribute( |
|
PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID, |
|
tsToken)}); |
|
} |
|
|
|
return constructToken(signature, signerChain, content, |
|
null, |
|
unauthAttrs, |
|
AlgorithmId.get(SignatureUtil.extractDigestAlgFromDwithE(signatureAlgorithm)), |
|
AlgorithmId.get(signatureAlgorithm)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static URI getTimestampingURI(X509Certificate tsaCertificate) { |
|
|
|
if (tsaCertificate == null) { |
|
return null; |
|
} |
|
|
|
try { |
|
byte[] extensionValue = tsaCertificate.getExtensionValue |
|
(KnownOIDs.SubjectInfoAccess.value()); |
|
if (extensionValue == null) { |
|
return null; |
|
} |
|
DerInputStream der = new DerInputStream(extensionValue); |
|
der = new DerInputStream(der.getOctetString()); |
|
DerValue[] derValue = der.getSequence(5); |
|
AccessDescription description; |
|
GeneralName location; |
|
URIName uri; |
|
for (int i = 0; i < derValue.length; i++) { |
|
description = new AccessDescription(derValue[i]); |
|
if (description.getAccessMethod() |
|
.equals(ObjectIdentifier.of(KnownOIDs.AD_TimeStamping))) { |
|
location = description.getAccessLocation(); |
|
if (location.getType() == GeneralNameInterface.NAME_URI) { |
|
uri = (URIName) location.getName(); |
|
if (uri.getScheme().equalsIgnoreCase("http") || |
|
uri.getScheme().equalsIgnoreCase("https")) { |
|
return uri.getURI(); |
|
} |
|
} |
|
} |
|
} |
|
} catch (IOException ioe) { |
|
// ignore |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static byte[] generateTimestampToken(Timestamper tsa, |
|
String tSAPolicyID, |
|
String tSADigestAlg, |
|
byte[] toBeTimestamped) |
|
throws IOException, CertificateException |
|
{ |
|
|
|
MessageDigest messageDigest = null; |
|
TSRequest tsQuery = null; |
|
try { |
|
messageDigest = MessageDigest.getInstance(tSADigestAlg); |
|
tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest); |
|
} catch (NoSuchAlgorithmException e) { |
|
throw new IllegalArgumentException(e); |
|
} |
|
|
|
|
|
BigInteger nonce = null; |
|
if (SecureRandomHolder.RANDOM != null) { |
|
nonce = new BigInteger(64, SecureRandomHolder.RANDOM); |
|
tsQuery.setNonce(nonce); |
|
} |
|
tsQuery.requestCertificate(true); |
|
|
|
TSResponse tsReply = tsa.generateTimestamp(tsQuery); |
|
int status = tsReply.getStatusCode(); |
|
|
|
if (status != 0 && status != 1) { |
|
throw new IOException("Error generating timestamp: " + |
|
tsReply.getStatusCodeAsText() + " " + |
|
tsReply.getFailureCodeAsText()); |
|
} |
|
|
|
if (tSAPolicyID != null && |
|
!tSAPolicyID.equals(tsReply.getTimestampToken().getPolicyID())) { |
|
throw new IOException("TSAPolicyID changed in " |
|
+ "timestamp token"); |
|
} |
|
PKCS7 tsToken = tsReply.getToken(); |
|
|
|
TimestampToken tst = tsReply.getTimestampToken(); |
|
try { |
|
if (!tst.getHashAlgorithm().equals(AlgorithmId.get(tSADigestAlg))) { |
|
throw new IOException("Digest algorithm not " + tSADigestAlg + " in " |
|
+ "timestamp token"); |
|
} |
|
} catch (NoSuchAlgorithmException nase) { |
|
throw new IllegalArgumentException(); |
|
} |
|
if (!MessageDigest.isEqual(tst.getHashedMessage(), |
|
tsQuery.getHashedMessage())) { |
|
throw new IOException("Digest octets changed in timestamp token"); |
|
} |
|
|
|
BigInteger replyNonce = tst.getNonce(); |
|
if (replyNonce == null && nonce != null) { |
|
throw new IOException("Nonce missing in timestamp token"); |
|
} |
|
if (replyNonce != null && !replyNonce.equals(nonce)) { |
|
throw new IOException("Nonce changed in timestamp token"); |
|
} |
|
|
|
|
|
for (SignerInfo si: tsToken.getSignerInfos()) { |
|
X509Certificate cert = si.getCertificate(tsToken); |
|
if (cert == null) { |
|
|
|
throw new CertificateException( |
|
"Certificate not included in timestamp token"); |
|
} else { |
|
if (!cert.getCriticalExtensionOIDs().contains( |
|
KnownOIDs.extendedKeyUsage.value())) { |
|
throw new CertificateException( |
|
"Certificate is not valid for timestamping"); |
|
} |
|
List<String> keyPurposes = cert.getExtendedKeyUsage(); |
|
if (keyPurposes == null || |
|
!keyPurposes.contains(KnownOIDs.KP_TimeStamping.value())) { |
|
throw new CertificateException( |
|
"Certificate is not valid for timestamping"); |
|
} |
|
} |
|
} |
|
return tsReply.getEncodedToken(); |
|
} |
|
} |