feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.*;
import sun.security.util.*;
/**
* A ContentInfo type, as defined in PKCS#7.
*
* @author Benjamin Renaud
*/
public class ContentInfo {
// pkcs7 pre-defined content types
private static int[] pkcs7 = {1, 2, 840, 113549, 1, 7};
private static int[] data = {1, 2, 840, 113549, 1, 7, 1};
private static int[] sdata = {1, 2, 840, 113549, 1, 7, 2};
private static int[] edata = {1, 2, 840, 113549, 1, 7, 3};
private static int[] sedata = {1, 2, 840, 113549, 1, 7, 4};
private static int[] ddata = {1, 2, 840, 113549, 1, 7, 5};
private static int[] crdata = {1, 2, 840, 113549, 1, 7, 6};
private static int[] nsdata = {2, 16, 840, 1, 113730, 2, 5};
// timestamp token (id-ct-TSTInfo) from RFC 3161
private static int[] tstInfo = {1, 2, 840, 113549, 1, 9, 16, 1, 4};
// this is for backwards-compatibility with JDK 1.1.x
private static final int[] OLD_SDATA = {1, 2, 840, 1113549, 1, 7, 2};
private static final int[] OLD_DATA = {1, 2, 840, 1113549, 1, 7, 1};
public static ObjectIdentifier PKCS7_OID;
public static ObjectIdentifier DATA_OID;
public static ObjectIdentifier SIGNED_DATA_OID;
public static ObjectIdentifier ENVELOPED_DATA_OID;
public static ObjectIdentifier SIGNED_AND_ENVELOPED_DATA_OID;
public static ObjectIdentifier DIGESTED_DATA_OID;
public static ObjectIdentifier ENCRYPTED_DATA_OID;
public static ObjectIdentifier OLD_SIGNED_DATA_OID;
public static ObjectIdentifier OLD_DATA_OID;
public static ObjectIdentifier NETSCAPE_CERT_SEQUENCE_OID;
public static ObjectIdentifier TIMESTAMP_TOKEN_INFO_OID;
static {
PKCS7_OID = ObjectIdentifier.newInternal(pkcs7);
DATA_OID = ObjectIdentifier.newInternal(data);
SIGNED_DATA_OID = ObjectIdentifier.newInternal(sdata);
ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(edata);
SIGNED_AND_ENVELOPED_DATA_OID = ObjectIdentifier.newInternal(sedata);
DIGESTED_DATA_OID = ObjectIdentifier.newInternal(ddata);
ENCRYPTED_DATA_OID = ObjectIdentifier.newInternal(crdata);
OLD_SIGNED_DATA_OID = ObjectIdentifier.newInternal(OLD_SDATA);
OLD_DATA_OID = ObjectIdentifier.newInternal(OLD_DATA);
/**
* The ASN.1 systax for the Netscape Certificate Sequence
* data type is defined
* <a href=http://wp.netscape.com/eng/security/comm4-cert-download.html>
* here.</a>
*/
NETSCAPE_CERT_SEQUENCE_OID = ObjectIdentifier.newInternal(nsdata);
TIMESTAMP_TOKEN_INFO_OID = ObjectIdentifier.newInternal(tstInfo);
}
ObjectIdentifier contentType;
DerValue content; // OPTIONAL
public ContentInfo(ObjectIdentifier contentType, DerValue content) {
this.contentType = contentType;
this.content = content;
}
/**
* Make a contentInfo of type data.
*/
public ContentInfo(byte[] bytes) {
DerValue octetString = new DerValue(DerValue.tag_OctetString, bytes);
this.contentType = DATA_OID;
this.content = octetString;
}
/**
* Parses a PKCS#7 content info.
*/
public ContentInfo(DerInputStream derin)
throws IOException, ParsingException
{
this(derin, false);
}
/**
* Parses a PKCS#7 content info.
*
* <p>This constructor is used only for backwards compatibility with
* PKCS#7 blocks that were generated using JDK1.1.x.
*
* @param derin the ASN.1 encoding of the content info.
* @param oldStyle flag indicating whether or not the given content info
* is encoded according to JDK1.1.x.
*/
public ContentInfo(DerInputStream derin, boolean oldStyle)
throws IOException, ParsingException
{
DerInputStream disType;
DerInputStream disTaggedContent;
DerValue type;
DerValue taggedContent;
DerValue[] typeAndContent;
DerValue[] contents;
typeAndContent = derin.getSequence(2);
if (typeAndContent.length < 1 || typeAndContent.length > 2) {
throw new ParsingException("Invalid length for ContentInfo");
}
// Parse the content type
type = typeAndContent[0];
disType = new DerInputStream(type.toByteArray());
contentType = disType.getOID();
if (oldStyle) {
// JDK1.1.x-style encoding
if (typeAndContent.length > 1) { // content is OPTIONAL
content = typeAndContent[1];
}
} else {
// This is the correct, standards-compliant encoding.
// Parse the content (OPTIONAL field).
// Skip the [0] EXPLICIT tag by pretending that the content is the
// one and only element in an implicitly tagged set
if (typeAndContent.length > 1) { // content is OPTIONAL
taggedContent = typeAndContent[1];
disTaggedContent
= new DerInputStream(taggedContent.toByteArray());
contents = disTaggedContent.getSet(1, true);
if (contents.length != 1) {
throw new ParsingException("ContentInfo encoding error");
}
content = contents[0];
}
}
}
public DerValue getContent() {
return content;
}
public ObjectIdentifier getContentType() {
return contentType;
}
public byte[] getData() throws IOException {
if (contentType.equals((Object)DATA_OID) ||
contentType.equals((Object)OLD_DATA_OID) ||
contentType.equals((Object)TIMESTAMP_TOKEN_INFO_OID)) {
if (content == null)
return null;
else
return content.getOctetString();
}
throw new IOException("content type is not DATA: " + contentType);
}
public void encode(DerOutputStream out) throws IOException {
DerOutputStream contentDerCode;
DerOutputStream seq;
seq = new DerOutputStream();
seq.putOID(contentType);
// content is optional, it could be external
if (content != null) {
DerValue taggedContent = null;
contentDerCode = new DerOutputStream();
content.encode(contentDerCode);
// Add the [0] EXPLICIT tag in front of the content encoding
taggedContent = new DerValue((byte)0xA0,
contentDerCode.toByteArray());
seq.putDerValue(taggedContent);
}
out.write(DerValue.tag_Sequence, seq);
}
/**
* Returns a byte array representation of the data held in
* the content field.
*/
public byte[] getContentBytes() throws IOException {
if (content == null)
return null;
DerInputStream dis = new DerInputStream(content.toByteArray());
return dis.getOctetString();
}
public String toString() {
String out = "";
out += "Content Info Sequence\n\tContent type: " + contentType + "\n";
out += "\tContent: " + content;
return out;
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.*;
import sun.security.x509.*;
import sun.security.util.DerValue;
import sun.security.util.DerOutputStream;
/**
* This class implements the <code>EncryptedPrivateKeyInfo</code> type,
* which is defined in PKCS #8 as follows:
*
* <pre>
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm AlgorithmIdentifier,
* encryptedData OCTET STRING }
* </pre>
*
* @author Jan Luehe
*
*/
public class EncryptedPrivateKeyInfo {
// the "encryptionAlgorithm" field
private AlgorithmId algid;
// the "encryptedData" field
private byte[] encryptedData;
// the ASN.1 encoded contents of this class
private byte[] encoded;
/**
* Constructs (i.e., parses) an <code>EncryptedPrivateKeyInfo</code> from
* its encoding.
*/
public EncryptedPrivateKeyInfo(byte[] encoded)
throws IOException
{
if (encoded == null) {
throw new IllegalArgumentException("encoding must not be null");
}
DerValue val = new DerValue(encoded);
DerValue[] seq = new DerValue[2];
seq[0] = val.data.getDerValue();
seq[1] = val.data.getDerValue();
if (val.data.available() != 0) {
throw new IOException("overrun, bytes = " + val.data.available());
}
this.algid = AlgorithmId.parse(seq[0]);
if (seq[0].data.available() != 0) {
throw new IOException("encryptionAlgorithm field overrun");
}
this.encryptedData = seq[1].getOctetString();
if (seq[1].data.available() != 0)
throw new IOException("encryptedData field overrun");
this.encoded = encoded.clone();
}
/**
* Constructs an <code>EncryptedPrivateKeyInfo</code> from the
* encryption algorithm and the encrypted data.
*/
public EncryptedPrivateKeyInfo(AlgorithmId algid, byte[] encryptedData) {
this.algid = algid;
this.encryptedData = encryptedData.clone();
}
/**
* Returns the encryption algorithm.
*/
public AlgorithmId getAlgorithm() {
return this.algid;
}
/**
* Returns the encrypted data.
*/
public byte[] getEncryptedData() {
return this.encryptedData.clone();
}
/**
* Returns the ASN.1 encoding of this class.
*/
public byte[] getEncoded()
throws IOException
{
if (this.encoded != null) return this.encoded.clone();
DerOutputStream out = new DerOutputStream();
DerOutputStream tmp = new DerOutputStream();
// encode encryption algorithm
algid.encode(tmp);
// encode encrypted data
tmp.putOctetString(encryptedData);
// wrap everything into a SEQUENCE
out.write(DerValue.tag_Sequence, tmp);
this.encoded = out.toByteArray();
return this.encoded.clone();
}
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof EncryptedPrivateKeyInfo))
return false;
try {
byte[] thisEncrInfo = this.getEncoded();
byte[] otherEncrInfo
= ((EncryptedPrivateKeyInfo)other).getEncoded();
if (thisEncrInfo.length != otherEncrInfo.length)
return false;
for (int i = 0; i < thisEncrInfo.length; i++)
if (thisEncrInfo[i] != otherEncrInfo[i])
return false;
return true;
} catch (IOException e) {
return false;
}
}
/**
* Returns a hashcode for this EncryptedPrivateKeyInfo.
*
* @return a hashcode for this EncryptedPrivateKeyInfo.
*/
public int hashCode() {
int retval = 0;
for (int i = 0; i < this.encryptedData.length; i++)
retval += this.encryptedData[i] * i;
return retval;
}
}

View File

@@ -0,0 +1,961 @@
/*
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
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 sun.security.timestamp.*;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
import sun.security.x509.X509CRLImpl;
import sun.security.x509.X500Name;
/**
* PKCS7 as defined in RSA Laboratories PKCS7 Technical Note. Profile
* Supports only {@code SignedData} ContentInfo
* type, where to the type of data signed is plain Data.
* For signedData, {@code crls}, {@code attributes} and
* PKCS#6 Extended Certificates are not supported.
*
* @author Benjamin Renaud
*/
public class PKCS7 {
private ObjectIdentifier contentType;
// the ASN.1 members for a signedData (and other) contentTypes
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; // Is this JDK1.1.x-style?
private Principal[] certIssuerNames;
/*
* Random number generator for creating nonce values
* (Lazy initialization)
*/
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;
}
}
/*
* Object identifier for the timestamping key purpose.
*/
private static final String KP_TIMESTAMPING_OID = "1.3.6.1.5.5.7.3.8";
/*
* Object identifier for extendedKeyUsage extension
*/
private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
/**
* Unmarshals a PKCS7 block from its encoded form, parsing the
* encoded bytes from the InputStream.
*
* @param in an input stream holding at least one PKCS7 block.
* @exception ParsingException on parsing errors.
* @exception IOException on other errors.
*/
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));
}
/**
* Unmarshals a PKCS7 block from its encoded form, parsing the
* encoded bytes from the DerInputStream.
*
* @param derin a DerInputStream holding at least one PKCS7 block.
* @exception ParsingException on parsing errors.
*/
public PKCS7(DerInputStream derin) throws ParsingException {
parse(derin);
}
/**
* Unmarshals a PKCS7 block from its encoded form, parsing the
* encoded bytes.
*
* @param bytes the encoded bytes.
* @exception ParsingException on parsing errors.
*/
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;
}
}
/*
* Parses a PKCS#7 block.
*/
private void parse(DerInputStream derin)
throws ParsingException
{
try {
derin.mark(derin.available());
// try new (i.e., JDK1.2) style
parse(derin, false);
} catch (IOException ioe) {
try {
derin.reset();
// try old (i.e., JDK1.1.x) style
parse(derin, true);
oldStyle = true;
} catch (IOException ioe1) {
ParsingException pe = new ParsingException(
ioe1.getMessage());
pe.initCause(ioe);
pe.addSuppressed(ioe1);
throw pe;
}
}
}
/**
* Parses a PKCS#7 block.
*
* @param derin the ASN.1 encoding of the PKCS#7 block.
* @param oldStyle flag indicating whether or not the given PKCS#7 block
* is encoded according to JDK1.1.x.
*/
private void parse(DerInputStream derin, boolean oldStyle)
throws IOException
{
contentInfo = new ContentInfo(derin, oldStyle);
contentType = contentInfo.contentType;
DerValue content = contentInfo.getContent();
if (content == null) {
throw new ParsingException("content is null");
}
if (contentType.equals((Object)ContentInfo.SIGNED_DATA_OID)) {
parseSignedData(content);
} else if (contentType.equals((Object)ContentInfo.OLD_SIGNED_DATA_OID)) {
// This is for backwards compatibility with JDK 1.1.x
parseOldSignedData(content);
} else if (contentType.equals((Object)
ContentInfo.NETSCAPE_CERT_SEQUENCE_OID)){
parseNetscapeCertChain(content);
} else {
throw new ParsingException("content type " + contentType +
" not supported.");
}
}
/**
* Construct an initialized PKCS7 block.
*
* @param digestAlgorithmIds the message digest algorithm identifiers.
* @param contentInfo the content information.
* @param certificates an array of X.509 certificates.
* @param crls an array of CRLs
* @param signerInfos an array of signer information.
*/
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
version = dis.getBigInteger();
// digestAlgorithmIds
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
contentInfo = new ContentInfo(dis);
CertificateFactory certfac = null;
try {
certfac = CertificateFactory.getInstance("X.509");
} catch (CertificateException ce) {
// do nothing
}
/*
* check if certificates (implicit tag) are provided
* (certificates are OPTIONAL)
*/
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
// CertificateChoices ignored.
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);
}
}
// check if crls (implicit tag) are provided (crls are OPTIONAL)
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();
}
}
}
// signerInfos
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);
}
}
/*
* Parses an old-style SignedData encoding (for backwards
* compatibility with JDK1.1.x).
*/
private void parseOldSignedData(DerValue val)
throws ParsingException, IOException
{
DerInputStream dis = val.toDerInputStream();
// Version
version = dis.getBigInteger();
// digestAlgorithmIds
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
contentInfo = new ContentInfo(dis, true);
// certificates
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();
}
}
// crls are ignored.
dis.getSet(0);
// signerInfos
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);
}
}
/**
* Encodes the signed data to an output stream.
*
* @param out the output stream to write the encoded data to.
* @exception IOException on encoding errors.
*/
public void encodeSignedData(OutputStream out) throws IOException {
DerOutputStream derout = new DerOutputStream();
encodeSignedData(derout);
out.write(derout.toByteArray());
}
/**
* Encodes the signed data to a DerOutputStream.
*
* @param out the DerOutputStream to write the encoded data to.
* @exception IOException on encoding errors.
*/
public void encodeSignedData(DerOutputStream out)
throws IOException
{
DerOutputStream signedData = new DerOutputStream();
// version
signedData.putInteger(version);
// digestAlgorithmIds
signedData.putOrderedSetOf(DerValue.tag_Set, digestAlgorithmIds);
// contentInfo
contentInfo.encode(signedData);
// certificates (optional)
if (certificates != null && certificates.length != 0) {
// cast to X509CertImpl[] since X509CertImpl implements DerEncoder
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)
// to the signed data
signedData.putOrderedSetOf((byte)0xA0, implCerts);
}
// CRLs (optional)
if (crls != null && crls.length != 0) {
// cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder
Set<X509CRLImpl> implCRLs = new HashSet<X509CRLImpl>(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)
// to the signed data
signedData.putOrderedSetOf((byte)0xA1,
implCRLs.toArray(new X509CRLImpl[implCRLs.size()]));
}
// signerInfos
signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
// making it a signed data block
DerValue signedDataSeq = new DerValue(DerValue.tag_Sequence,
signedData.toByteArray());
// making it a content info sequence
ContentInfo block = new ContentInfo(ContentInfo.SIGNED_DATA_OID,
signedDataSeq);
// writing out the contentInfo sequence
block.encode(out);
}
/**
* This verifies a given SignerInfo.
*
* @param info the signer information.
* @param bytes the DER encoded content information.
*
* @exception NoSuchAlgorithmException on unrecognized algorithms.
* @exception SignatureException on signature handling errors.
*/
public SignerInfo verify(SignerInfo info, byte[] bytes)
throws NoSuchAlgorithmException, SignatureException {
return info.verify(this, bytes);
}
/**
* Returns all signerInfos which self-verify.
*
* @param bytes the DER encoded content information.
*
* @exception NoSuchAlgorithmException on unrecognized algorithms.
* @exception SignatureException on signature handling errors.
*/
public SignerInfo[] verify(byte[] bytes)
throws NoSuchAlgorithmException, SignatureException {
Vector<SignerInfo> intResult = new Vector<SignerInfo>();
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;
}
/**
* Returns all signerInfos which self-verify.
*
* @exception NoSuchAlgorithmException on unrecognized algorithms.
* @exception SignatureException on signature handling errors.
*/
public SignerInfo[] verify()
throws NoSuchAlgorithmException, SignatureException {
return verify(null);
}
/**
* Returns the version number of this PKCS7 block.
* @return the version or null if version is not specified
* for the content type.
*/
public BigInteger getVersion() {
return version;
}
/**
* Returns the message digest algorithms specified in this PKCS7 block.
* @return the array of Digest Algorithms or null if none are specified
* for the content type.
*/
public AlgorithmId[] getDigestAlgorithmIds() {
return digestAlgorithmIds;
}
/**
* Returns the content information specified in this PKCS7 block.
*/
public ContentInfo getContentInfo() {
return contentInfo;
}
/**
* Returns the X.509 certificates listed in this PKCS7 block.
* @return a clone of the array of X.509 certificates or null if
* none are specified for the content type.
*/
public X509Certificate[] getCertificates() {
if (certificates != null)
return certificates.clone();
else
return null;
}
/**
* Returns the X.509 crls listed in this PKCS7 block.
* @return a clone of the array of X.509 crls or null if none
* are specified for the content type.
*/
public X509CRL[] getCRLs() {
if (crls != null)
return crls.clone();
else
return null;
}
/**
* Returns the signer's information specified in this PKCS7 block.
* @return the array of Signer Infos or null if none are specified
* for the content type.
*/
public SignerInfo[] getSignerInfos() {
return signerInfos;
}
/**
* Returns the X.509 certificate listed in this PKCS7 block
* which has a matching serial number and Issuer name, or
* null if one is not found.
*
* @param serial the serial number of the certificate to retrieve.
* @param issuerName the Distinguished Name of the Issuer.
*/
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;
}
/**
* Populate array of Issuer DNs from certificates and convert
* each Principal to type X500Name if necessary.
*/
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
// types of String attribute values to be changed)
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;
}
}
/**
* Returns the PKCS7 block in a printable string form.
*/
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;
}
/**
* Returns true if this is a JDK1.1.x-style PKCS#7 block, and false
* otherwise.
*/
public boolean isOldStyle() {
return this.oldStyle;
}
/**
* Assembles a PKCS #7 signed data message that optionally includes a
* signature timestamp.
*
* @param signature the signature bytes
* @param signerChain the signer's X.509 certificate chain
* @param content the content that is signed; specify null to not include
* it in the PKCS7 data
* @param signatureAlgorithm the name of the signature algorithm
* @param tsaURI the URI of the Timestamping Authority; or null if no
* timestamp is requested
* @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
* numerical object identifier; or null if we leave the TSA server
* to choose one. This argument is only used when tsaURI is provided
* @return the bytes of the encoded PKCS #7 signed data message
* @throws NoSuchAlgorithmException The exception is thrown if the signature
* algorithm is unrecognised.
* @throws CertificateException The exception is thrown if an error occurs
* while processing the signer's certificate or the TSA's
* certificate.
* @throws IOException The exception is thrown if an error occurs while
* generating the signature timestamp or while generating the signed
* data message.
*/
public static byte[] generateSignedData(byte[] signature,
X509Certificate[] signerChain,
byte[] content,
String signatureAlgorithm,
URI tsaURI,
String tSAPolicyID,
String tSADigestAlg)
throws CertificateException, IOException, NoSuchAlgorithmException
{
// Generate the timestamp token
PKCS9Attributes unauthAttrs = null;
if (tsaURI != null) {
// Timestamp the signature
HttpTimestamper tsa = new HttpTimestamper(tsaURI);
byte[] tsToken = generateTimestampToken(
tsa, tSAPolicyID, tSADigestAlg, signature);
// Insert the timestamp token into the PKCS #7 signer info element
// (as an unsigned attribute)
unauthAttrs =
new PKCS9Attributes(new PKCS9Attribute[]{
new PKCS9Attribute(
PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID,
tsToken)});
}
// Create the SignerInfo
X500Name issuerName =
X500Name.asX500Name(signerChain[0].getIssuerX500Principal());
BigInteger serialNumber = signerChain[0].getSerialNumber();
String encAlg = AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm);
String digAlg = AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm);
SignerInfo signerInfo = new SignerInfo(issuerName, serialNumber,
AlgorithmId.get(digAlg), null,
AlgorithmId.get(encAlg),
signature, unauthAttrs);
// Create the PKCS #7 signed data message
SignerInfo[] signerInfos = {signerInfo};
AlgorithmId[] algorithms = {signerInfo.getDigestAlgorithmId()};
// Include or exclude content
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();
}
/**
* Requests, processes and validates a timestamp token from a TSA using
* common defaults. Uses the following defaults in the timestamp request:
* SHA-1 for the hash algorithm, a 64-bit nonce, and request certificate
* set to true.
*
* @param tsa the timestamping authority to use
* @param tSAPolicyID the TSAPolicyID of the Timestamping Authority as a
* numerical object identifier; or null if we leave the TSA server
* to choose one
* @param toBeTimestamped the token that is to be timestamped
* @return the encoded timestamp token
* @throws IOException The exception is thrown if an error occurs while
* communicating with the TSA, or a non-null
* TSAPolicyID is specified in the request but it
* does not match the one in the reply
* @throws CertificateException The exception is thrown if the TSA's
* certificate is not permitted for timestamping.
*/
private static byte[] generateTimestampToken(Timestamper tsa,
String tSAPolicyID,
String tSADigestAlg,
byte[] toBeTimestamped)
throws IOException, CertificateException
{
// Generate a timestamp
MessageDigest messageDigest = null;
TSRequest tsQuery = null;
try {
messageDigest = MessageDigest.getInstance(tSADigestAlg);
tsQuery = new TSRequest(tSAPolicyID, toBeTimestamped, messageDigest);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e);
}
// Generate a nonce
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();
// Handle TSP error
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(); // should have been caught before
}
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");
}
// Examine the TSA's certificate (if present)
for (SignerInfo si: tsToken.getSignerInfos()) {
X509Certificate cert = si.getCertificate(tsToken);
if (cert == null) {
// Error, we've already set tsRequestCertificate = true
throw new CertificateException(
"Certificate not included in timestamp token");
} else {
if (!cert.getCriticalExtensionOIDs().contains(
EXTENDED_KEY_USAGE_OID)) {
throw new CertificateException(
"Certificate is not valid for timestamping");
}
List<String> keyPurposes = cert.getExtendedKeyUsage();
if (keyPurposes == null ||
!keyPurposes.contains(KP_TIMESTAMPING_OID)) {
throw new CertificateException(
"Certificate is not valid for timestamping");
}
}
}
return tsReply.getEncodedToken();
}
}

View File

@@ -0,0 +1,442 @@
/*
* Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.*;
import java.util.Properties;
import java.math.*;
import java.security.Key;
import java.security.KeyRep;
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.Security;
import java.security.Provider;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import sun.misc.HexDumpEncoder;
import sun.security.x509.*;
import sun.security.util.*;
/**
* Holds a PKCS#8 key, for example a private key
*
* @author Dave Brownell
* @author Benjamin Renaud
*/
public class PKCS8Key implements PrivateKey {
/** use serialVersionUID from JDK 1.1. for interoperability */
private static final long serialVersionUID = -3836890099307167124L;
/* The algorithm information (name, parameters, etc). */
protected AlgorithmId algid;
/* The key bytes, without the algorithm information */
protected byte[] key;
/* The encoded for the key. */
protected byte[] encodedKey;
/* The version for this key */
public static final BigInteger version = BigInteger.ZERO;
/**
* Default constructor. The key constructed must have its key
* and algorithm initialized before it may be used, for example
* by using <code>decode</code>.
*/
public PKCS8Key() { }
/*
* Build and initialize as a "default" key. All PKCS#8 key
* data is stored and transmitted losslessly, but no knowledge
* about this particular algorithm is available.
*/
private PKCS8Key (AlgorithmId algid, byte key [])
throws InvalidKeyException {
this.algid = algid;
this.key = key;
encode();
}
/*
* Binary backwards compatibility. New uses should call parseKey().
*/
public static PKCS8Key parse (DerValue in) throws IOException {
PrivateKey key;
key = parseKey(in);
if (key instanceof PKCS8Key)
return (PKCS8Key)key;
throw new IOException("Provider did not return PKCS8Key");
}
/**
* Construct PKCS#8 subject public key from a DER value. If
* the runtime environment is configured with a specific class for
* this kind of key, a subclass is returned. Otherwise, a generic
* PKCS8Key object is returned.
*
* <P>This mechanism gurantees that keys (and algorithms) may be
* freely manipulated and transferred, without risk of losing
* information. Also, when a key (or algorithm) needs some special
* handling, that specific need can be accomodated.
*
* @param in the DER-encoded SubjectPublicKeyInfo value
* @exception IOException on data format errors
*/
public static PrivateKey parseKey (DerValue in) throws IOException
{
AlgorithmId algorithm;
PrivateKey privKey;
if (in.tag != DerValue.tag_Sequence)
throw new IOException ("corrupt private key");
BigInteger parsedVersion = in.data.getBigInteger();
if (!version.equals(parsedVersion)) {
throw new IOException("version mismatch: (supported: " +
Debug.toHexString(version) +
", parsed: " +
Debug.toHexString(parsedVersion));
}
algorithm = AlgorithmId.parse (in.data.getDerValue ());
try {
privKey = buildPKCS8Key (algorithm, in.data.getOctetString ());
} catch (InvalidKeyException e) {
throw new IOException("corrupt private key");
}
if (in.data.available () != 0)
throw new IOException ("excess private key");
return privKey;
}
/**
* Parse the key bits. This may be redefined by subclasses to take
* advantage of structure within the key. For example, RSA public
* keys encapsulate two unsigned integers (modulus and exponent) as
* DER values within the <code>key</code> bits; Diffie-Hellman and
* DSS/DSA keys encapsulate a single unsigned integer.
*
* <P>This function is called when creating PKCS#8 SubjectPublicKeyInfo
* values using the PKCS8Key member functions, such as <code>parse</code>
* and <code>decode</code>.
*
* @exception IOException if a parsing error occurs.
* @exception InvalidKeyException if the key encoding is invalid.
*/
protected void parseKeyBits () throws IOException, InvalidKeyException {
encode();
}
/*
* Factory interface, building the kind of key associated with this
* specific algorithm ID or else returning this generic base class.
* See the description above.
*/
static PrivateKey buildPKCS8Key (AlgorithmId algid, byte[] key)
throws IOException, InvalidKeyException
{
/*
* Use the algid and key parameters to produce the ASN.1 encoding
* of the key, which will then be used as the input to the
* key factory.
*/
DerOutputStream pkcs8EncodedKeyStream = new DerOutputStream();
encode(pkcs8EncodedKeyStream, algid, key);
PKCS8EncodedKeySpec pkcs8KeySpec
= new PKCS8EncodedKeySpec(pkcs8EncodedKeyStream.toByteArray());
try {
// Instantiate the key factory of the appropriate algorithm
KeyFactory keyFac = KeyFactory.getInstance(algid.getName());
// Generate the private key
return keyFac.generatePrivate(pkcs8KeySpec);
} catch (NoSuchAlgorithmException e) {
// Return generic PKCS8Key with opaque key data (see below)
} catch (InvalidKeySpecException e) {
// Return generic PKCS8Key with opaque key data (see below)
}
/*
* Try again using JDK1.1-style for backwards compatibility.
*/
String classname = "";
try {
Properties props;
String keytype;
Provider sunProvider;
sunProvider = Security.getProvider("SUN");
if (sunProvider == null)
throw new InstantiationException();
classname = sunProvider.getProperty("PrivateKey.PKCS#8." +
algid.getName());
if (classname == null) {
throw new InstantiationException();
}
Class<?> keyClass = null;
try {
keyClass = Class.forName(classname);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null) {
keyClass = cl.loadClass(classname);
}
}
Object inst = null;
PKCS8Key result;
if (keyClass != null)
inst = keyClass.newInstance();
if (inst instanceof PKCS8Key) {
result = (PKCS8Key) inst;
result.algid = algid;
result.key = key;
result.parseKeyBits();
return result;
}
} catch (ClassNotFoundException e) {
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
// this should not happen.
throw new IOException (classname + " [internal error]");
}
PKCS8Key result = new PKCS8Key();
result.algid = algid;
result.key = key;
return result;
}
/**
* Returns the algorithm to be used with this key.
*/
public String getAlgorithm() {
return algid.getName();
}
/**
* Returns the algorithm ID to be used with this key.
*/
public AlgorithmId getAlgorithmId () { return algid; }
/**
* PKCS#8 sequence on the DER output stream.
*/
public final void encode(DerOutputStream out) throws IOException
{
encode(out, this.algid, this.key);
}
/**
* Returns the DER-encoded form of the key as a byte array.
*/
public synchronized byte[] getEncoded() {
byte[] result = null;
try {
result = encode();
} catch (InvalidKeyException e) {
}
return result;
}
/**
* Returns the format for this key: "PKCS#8"
*/
public String getFormat() {
return "PKCS#8";
}
/**
* Returns the DER-encoded form of the key as a byte array.
*
* @exception InvalidKeyException if an encoding error occurs.
*/
public byte[] encode() throws InvalidKeyException {
if (encodedKey == null) {
try {
DerOutputStream out;
out = new DerOutputStream ();
encode (out);
encodedKey = out.toByteArray();
} catch (IOException e) {
throw new InvalidKeyException ("IOException : " +
e.getMessage());
}
}
return encodedKey.clone();
}
/**
* Initialize an PKCS8Key object from an input stream. The data
* on that input stream must be encoded using DER, obeying the
* PKCS#8 format: a sequence consisting of a version, an algorithm
* ID and a bit string which holds the key. (That bit string is
* often used to encapsulate another DER encoded sequence.)
*
* <P>Subclasses should not normally redefine this method; they should
* instead provide a <code>parseKeyBits</code> method to parse any
* fields inside the <code>key</code> member.
*
* @param in an input stream with a DER-encoded PKCS#8
* SubjectPublicKeyInfo value
*
* @exception InvalidKeyException if a parsing error occurs.
*/
public void decode(InputStream in) throws InvalidKeyException
{
DerValue val;
try {
val = new DerValue (in);
if (val.tag != DerValue.tag_Sequence)
throw new InvalidKeyException ("invalid key format");
BigInteger version = val.data.getBigInteger();
if (!version.equals(PKCS8Key.version)) {
throw new IOException("version mismatch: (supported: " +
Debug.toHexString(PKCS8Key.version) +
", parsed: " +
Debug.toHexString(version));
}
algid = AlgorithmId.parse (val.data.getDerValue ());
key = val.data.getOctetString ();
parseKeyBits ();
if (val.data.available () != 0) {
// OPTIONAL attributes not supported yet
}
} catch (IOException e) {
throw new InvalidKeyException("IOException : " +
e.getMessage());
}
}
public void decode(byte[] encodedKey) throws InvalidKeyException {
decode(new ByteArrayInputStream(encodedKey));
}
protected Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.PRIVATE,
getAlgorithm(),
getFormat(),
getEncoded());
}
/**
* Serialization read ... PKCS#8 keys serialize as
* themselves, and they're parsed when they get read back.
*/
private void readObject (ObjectInputStream stream)
throws IOException {
try {
decode(stream);
} catch (InvalidKeyException e) {
e.printStackTrace();
throw new IOException("deserialized key is invalid: " +
e.getMessage());
}
}
/*
* Produce PKCS#8 encoding from algorithm id and key material.
*/
static void encode(DerOutputStream out, AlgorithmId algid, byte[] key)
throws IOException {
DerOutputStream tmp = new DerOutputStream();
tmp.putInteger(version);
algid.encode(tmp);
tmp.putOctetString(key);
out.write(DerValue.tag_Sequence, tmp);
}
/**
* Compares two private keys. This returns false if the object with which
* to compare is not of type <code>Key</code>.
* Otherwise, the encoding of this key object is compared with the
* encoding of the given key object.
*
* @param object the object with which to compare
* @return <code>true</code> if this key has the same encoding as the
* object argument; <code>false</code> otherwise.
*/
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof Key) {
// this encoding
byte[] b1;
if (encodedKey != null) {
b1 = encodedKey;
} else {
b1 = getEncoded();
}
// that encoding
byte[] b2 = ((Key)object).getEncoded();
// time-constant comparison
return MessageDigest.isEqual(b1, b2);
}
return false;
}
/**
* Calculates a hash code value for this object. Objects
* which are equal will also have the same hashcode.
*/
public int hashCode() {
int retval = 0;
byte[] b1 = getEncoded();
for (int i = 1; i < b1.length; i++) {
retval += b1[i] * i;
}
return(retval);
}
}

View File

@@ -0,0 +1,781 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.IOException;
import java.io.OutputStream;
import java.security.cert.CertificateException;
import java.util.Locale;
import java.util.Date;
import java.util.Hashtable;
import sun.security.x509.CertificateExtensions;
import sun.security.util.*;
import sun.misc.HexDumpEncoder;
/**
* Class supporting any PKCS9 attributes.
* Supports DER decoding/encoding and access to attribute values.
*
* <a name="classTable"><h3>Type/Class Table</h3></a>
* The following table shows the correspondence between
* PKCS9 attribute types and value component classes.
* For types not listed here, its name is the OID
* in string form, its value is a (single-valued)
* byte array that is the SET's encoding.
*
* <P>
* <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
*
* <TR>
* <TH>Object Identifier</TH>
* <TH>Attribute Name</TH>
* <TH>Type</TH>
* <TH>Value Class</TH>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.1</TD>
* <TD>EmailAddress</TD>
* <TD>Multi-valued</TD>
* <TD><code>String[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.2</TD>
* <TD>UnstructuredName</TD>
* <TD>Multi-valued</TD>
* <TD><code>String[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.3</TD>
* <TD>ContentType</TD>
* <TD>Single-valued</TD>
* <TD><code>ObjectIdentifier</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.4</TD>
* <TD>MessageDigest</TD>
* <TD>Single-valued</TD>
* <TD><code>byte[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.5</TD>
* <TD>SigningTime</TD>
* <TD>Single-valued</TD>
* <TD><code>Date</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.6</TD>
* <TD>Countersignature</TD>
* <TD>Multi-valued</TD>
* <TD><code>SignerInfo[]</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.7</TD>
* <TD>ChallengePassword</TD>
* <TD>Single-valued</TD>
* <TD><code>String</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.8</TD>
* <TD>UnstructuredAddress</TD>
* <TD>Single-valued</TD>
* <TD><code>String</code></TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.9</TD>
* <TD>ExtendedCertificateAttributes</TD>
* <TD>Multi-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.10</TD>
* <TD>IssuerAndSerialNumber</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.{11,12}</TD>
* <TD>RSA DSI proprietary</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.13</TD>
* <TD>S/MIME unused assignment</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.14</TD>
* <TD>ExtensionRequest</TD>
* <TD>Single-valued</TD>
* <TD>CertificateExtensions</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.15</TD>
* <TD>SMIMECapability</TD>
* <TD>Single-valued</TD>
* <TD>(not supported)</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.16.2.12</TD>
* <TD>SigningCertificate</TD>
* <TD>Single-valued</TD>
* <TD>SigningCertificateInfo</TD>
* </TR>
*
* <TR>
* <TD>1.2.840.113549.1.9.16.2.14</TD>
* <TD>SignatureTimestampToken</TD>
* <TD>Single-valued</TD>
* <TD>byte[]</TD>
* </TR>
*
* </TABLE>
*
* @author Douglas Hoover
*/
public class PKCS9Attribute implements DerEncoder {
/* Are we debugging ? */
private static final Debug debug = Debug.getInstance("jar");
/**
* Array of attribute OIDs defined in PKCS9, by number.
*/
static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[18];
private final static Class<?> BYTE_ARRAY_CLASS;
static {
// set unused PKCS9_OIDS entries to null
// rest are initialized with public constants
PKCS9_OIDS[0] = PKCS9_OIDS[11] = PKCS9_OIDS[12] = PKCS9_OIDS[13] =
PKCS9_OIDS[15] = null;
try {
BYTE_ARRAY_CLASS = Class.forName("[B");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e.toString());
}
}
public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1] =
ObjectIdentifier.of(KnownOIDs.EmailAddress);
public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2] =
ObjectIdentifier.of(KnownOIDs.UnstructuredName);
public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3] =
ObjectIdentifier.of(KnownOIDs.ContentType);
public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4] =
ObjectIdentifier.of(KnownOIDs.MessageDigest);
public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5] =
ObjectIdentifier.of(KnownOIDs.SigningTime);
public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6] =
ObjectIdentifier.of(KnownOIDs.CounterSignature);
public static final ObjectIdentifier CHALLENGE_PASSWORD_OID =
PKCS9_OIDS[7] = ObjectIdentifier.of(KnownOIDs.ChallengePassword);
public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID =
PKCS9_OIDS[8] = ObjectIdentifier.of(KnownOIDs.UnstructuredAddress);
public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID =
PKCS9_OIDS[9] =
ObjectIdentifier.of(KnownOIDs.ExtendedCertificateAttributes);
public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID =
PKCS9_OIDS[10] =
ObjectIdentifier.of(KnownOIDs.IssuerAndSerialNumber);
// [11], [12] are RSA DSI proprietary
// [13] ==> signingDescription, S/MIME, not used anymore
public static final ObjectIdentifier EXTENSION_REQUEST_OID =
PKCS9_OIDS[14] = ObjectIdentifier.of(KnownOIDs.ExtensionRequest);
public static final ObjectIdentifier SIGNING_CERTIFICATE_OID =
PKCS9_OIDS[16] = ObjectIdentifier.of(KnownOIDs.SigningCertificate);
public static final ObjectIdentifier SIGNATURE_TIMESTAMP_TOKEN_OID =
PKCS9_OIDS[17] =
ObjectIdentifier.of(KnownOIDs.SignatureTimestampToken);
/**
* Acceptable ASN.1 tags for DER encodings of values of PKCS9
* attributes, by index in <code>PKCS9_OIDS</code>.
* Sets of acceptable tags are represented as arrays.
*/
private static final Byte[][] PKCS9_VALUE_TAGS = {
null,
{new Byte(DerValue.tag_IA5String)}, // EMailAddress
{new Byte(DerValue.tag_IA5String), // UnstructuredName
new Byte(DerValue.tag_PrintableString)},
{new Byte(DerValue.tag_ObjectId)}, // ContentType
{new Byte(DerValue.tag_OctetString)}, // MessageDigest
{new Byte(DerValue.tag_UtcTime)}, // SigningTime
{new Byte(DerValue.tag_Sequence)}, // Countersignature
{new Byte(DerValue.tag_PrintableString),
new Byte(DerValue.tag_T61String)}, // ChallengePassword
{new Byte(DerValue.tag_PrintableString),
new Byte(DerValue.tag_T61String)}, // UnstructuredAddress
{new Byte(DerValue.tag_SetOf)}, // ExtendedCertificateAttributes
{new Byte(DerValue.tag_Sequence)}, // issuerAndSerialNumber
null,
null,
null,
{new Byte(DerValue.tag_Sequence)}, // extensionRequest
{new Byte(DerValue.tag_Sequence)}, // SMIMECapability
{new Byte(DerValue.tag_Sequence)}, // SigningCertificate
{new Byte(DerValue.tag_Sequence)} // SignatureTimestampToken
};
private static final Class<?>[] VALUE_CLASSES = new Class<?>[18];
static {
try {
Class<?> str = Class.forName("[Ljava.lang.String;");
VALUE_CLASSES[0] = null; // not used
VALUE_CLASSES[1] = str; // EMailAddress
VALUE_CLASSES[2] = str; // UnstructuredName
VALUE_CLASSES[3] = // ContentType
Class.forName("sun.security.util.ObjectIdentifier");
VALUE_CLASSES[4] = BYTE_ARRAY_CLASS; // MessageDigest (byte[])
VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime
VALUE_CLASSES[6] = // Countersignature
Class.forName("[Lsun.security.pkcs.SignerInfo;");
VALUE_CLASSES[7] = // ChallengePassword
Class.forName("java.lang.String");
VALUE_CLASSES[8] = str; // UnstructuredAddress
VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes
VALUE_CLASSES[10] = null; // IssuerAndSerialNumber
VALUE_CLASSES[11] = null; // not used
VALUE_CLASSES[12] = null; // not used
VALUE_CLASSES[13] = null; // not used
VALUE_CLASSES[14] = // ExtensionRequest
Class.forName("sun.security.x509.CertificateExtensions");
VALUE_CLASSES[15] = null; // not supported yet
VALUE_CLASSES[16] = null; // not supported yet
VALUE_CLASSES[17] = BYTE_ARRAY_CLASS; // SignatureTimestampToken
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e.toString());
}
}
/**
* Array indicating which PKCS9 attributes are single-valued,
* by index in <code>PKCS9_OIDS</code>.
*/
private static final boolean[] SINGLE_VALUED = {
false,
false, // EMailAddress
false, // UnstructuredName
true, // ContentType
true, // MessageDigest
true, // SigningTime
false, // Countersignature
true, // ChallengePassword
false, // UnstructuredAddress
false, // ExtendedCertificateAttributes
true, // IssuerAndSerialNumber - not supported yet
false, // not used
false, // not used
false, // not used
true, // ExtensionRequest
true, // SMIMECapability - not supported yet
true, // SigningCertificate
true // SignatureTimestampToken
};
/**
* The OID of this attribute.
*/
private ObjectIdentifier oid;
/**
* The index of the OID of this attribute in <code>PKCS9_OIDS</code>,
* or -1 if it's unknown.
*/
private int index;
/**
* Value set of this attribute. Its class is given by
* <code>VALUE_CLASSES[index]</code>. The SET itself
* as byte[] if unknown.
*/
private Object value;
/**
* Construct an attribute object from the attribute's OID and
* value. If the attribute is single-valued, provide only one
* value. If the attribute is multi-valued, provide an array
* containing all the values.
* Arrays of length zero are accepted, though probably useless.
*
* <P> The
* <a href=#classTable>table</a> gives the class that <code>value</code>
* must have for a given attribute.
*
* @exception IllegalArgumentException
* if the <code>value</code> has the wrong type.
*/
public PKCS9Attribute(ObjectIdentifier oid, Object value)
throws IllegalArgumentException {
init(oid, value);
}
private void init(ObjectIdentifier oid, Object value)
throws IllegalArgumentException {
this.oid = oid;
index = indexOf(oid, PKCS9_OIDS, 1);
Class<?> clazz = index == -1 ? BYTE_ARRAY_CLASS: VALUE_CLASSES[index];
if (!clazz.isInstance(value)) {
throw new IllegalArgumentException(
"Wrong value class " +
" for attribute " + oid +
" constructing PKCS9Attribute; was " +
value.getClass().toString() + ", should be " +
clazz.toString());
}
this.value = value;
}
/**
* Construct a PKCS9Attribute from its encoding on an input
* stream.
*
* @param val the DerValue representing the DER encoding of the attribute.
* @exception IOException on parsing error.
*/
public PKCS9Attribute(DerValue derVal) throws IOException {
DerInputStream derIn = new DerInputStream(derVal.toByteArray());
DerValue[] val = derIn.getSequence(2);
if (derIn.available() != 0)
throw new IOException("Excess data parsing PKCS9Attribute");
if (val.length != 2)
throw new IOException("PKCS9Attribute doesn't have two components");
// get the oid
oid = val[0].getOID();
byte[] content = val[1].toByteArray();
DerValue[] elems = new DerInputStream(content).getSet(1);
index = indexOf(oid, PKCS9_OIDS, 1);
if (index == -1) {
if (debug != null) {
debug.println("Unsupported signer attribute: " + oid);
}
value = content;
return;
}
// check single valued have only one value
if (SINGLE_VALUED[index] && elems.length > 1)
throwSingleValuedException();
// check for illegal element tags
Byte tag;
for (int i=0; i < elems.length; i++) {
tag = new Byte(elems[i].tag);
if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1)
throwTagException(tag);
}
switch (index) {
case 1: // email address
case 2: // unstructured name
case 8: // unstructured address
{ // open scope
String[] values = new String[elems.length];
for (int i=0; i < elems.length; i++)
values[i] = elems[i].getAsString();
value = values;
} // close scope
break;
case 3: // content type
value = elems[0].getOID();
break;
case 4: // message digest
value = elems[0].getOctetString();
break;
case 5: // signing time
value = (new DerInputStream(elems[0].toByteArray())).getUTCTime();
break;
case 6: // countersignature
{ // open scope
SignerInfo[] values = new SignerInfo[elems.length];
for (int i=0; i < elems.length; i++)
values[i] =
new SignerInfo(elems[i].toDerInputStream());
value = values;
} // close scope
break;
case 7: // challenge password
value = elems[0].getAsString();
break;
case 9: // extended-certificate attribute -- not supported
throw new IOException("PKCS9 extended-certificate " +
"attribute not supported.");
// break unnecessary
case 10: // issuerAndserialNumber attribute -- not supported
throw new IOException("PKCS9 IssuerAndSerialNumber" +
"attribute not supported.");
// break unnecessary
case 11: // RSA DSI proprietary
case 12: // RSA DSI proprietary
throw new IOException("PKCS9 RSA DSI attributes" +
"11 and 12, not supported.");
// break unnecessary
case 13: // S/MIME unused attribute
throw new IOException("PKCS9 attribute #13 not supported.");
// break unnecessary
case 14: // ExtensionRequest
value = new CertificateExtensions(
new DerInputStream(elems[0].toByteArray()));
break;
case 15: // SMIME-capability attribute -- not supported
throw new IOException("PKCS9 SMIMECapability " +
"attribute not supported.");
// break unnecessary
case 16: // SigningCertificate attribute
value = new SigningCertificateInfo(elems[0].toByteArray());
break;
case 17: // SignatureTimestampToken attribute
value = elems[0].toByteArray();
break;
default: // can't happen
}
}
/**
* Write the DER encoding of this attribute to an output stream.
*
* <P> N.B.: This method always encodes values of
* ChallengePassword and UnstructuredAddress attributes as ASN.1
* <code>PrintableString</code>s, without checking whether they
* should be encoded as <code>T61String</code>s.
*/
public void derEncode(OutputStream out) throws IOException {
DerOutputStream temp = new DerOutputStream();
temp.putOID(oid);
switch (index) {
case -1: // Unknown
temp.write((byte[])value);
break;
case 1: // email address
case 2: // unstructured name
{ // open scope
String[] values = (String[]) value;
DerOutputStream[] temps = new
DerOutputStream[values.length];
for (int i=0; i < values.length; i++) {
temps[i] = new DerOutputStream();
temps[i].putIA5String( values[i]);
}
temp.putOrderedSetOf(DerValue.tag_Set, temps);
} // close scope
break;
case 3: // content type
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putOID((ObjectIdentifier) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 4: // message digest
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putOctetString((byte[]) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 5: // signing time
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putUTCTime((Date) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 6: // countersignature
temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value);
break;
case 7: // challenge password
{
DerOutputStream temp2 = new DerOutputStream();
temp2.putPrintableString((String) value);
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 8: // unstructured address
{ // open scope
String[] values = (String[]) value;
DerOutputStream[] temps = new
DerOutputStream[values.length];
for (int i=0; i < values.length; i++) {
temps[i] = new DerOutputStream();
temps[i].putPrintableString(values[i]);
}
temp.putOrderedSetOf(DerValue.tag_Set, temps);
} // close scope
break;
case 9: // extended-certificate attribute -- not supported
throw new IOException("PKCS9 extended-certificate " +
"attribute not supported.");
// break unnecessary
case 10: // issuerAndserialNumber attribute -- not supported
throw new IOException("PKCS9 IssuerAndSerialNumber" +
"attribute not supported.");
// break unnecessary
case 11: // RSA DSI proprietary
case 12: // RSA DSI proprietary
throw new IOException("PKCS9 RSA DSI attributes" +
"11 and 12, not supported.");
// break unnecessary
case 13: // S/MIME unused attribute
throw new IOException("PKCS9 attribute #13 not supported.");
// break unnecessary
case 14: // ExtensionRequest
{
DerOutputStream temp2 = new DerOutputStream();
CertificateExtensions exts = (CertificateExtensions)value;
try {
exts.encode(temp2, true);
} catch (CertificateException ex) {
throw new IOException(ex.toString());
}
temp.write(DerValue.tag_Set, temp2.toByteArray());
}
break;
case 15: // SMIMECapability
throw new IOException("PKCS9 attribute #15 not supported.");
// break unnecessary
case 16: // SigningCertificate
throw new IOException(
"PKCS9 SigningCertificate attribute not supported.");
// break unnecessary
case 17: // SignatureTimestampToken
temp.write(DerValue.tag_Set, (byte[])value);
break;
default: // can't happen
}
DerOutputStream derOut = new DerOutputStream();
derOut.write(DerValue.tag_Sequence, temp.toByteArray());
out.write(derOut.toByteArray());
}
/**
* Returns if the attribute is known. Unknown attributes can be created
* from DER encoding with unknown OIDs.
*/
public boolean isKnown() {
return index != -1;
}
/**
* Get the value of this attribute. If the attribute is
* single-valued, return just the one value. If the attribute is
* multi-valued, return an array containing all the values.
* It is possible for this array to be of length 0.
*
* <P> The
* <a href=#classTable>table</a> gives the class of the value returned,
* depending on the type of this attribute.
*/
public Object getValue() {
return value;
}
/**
* Show whether this attribute is single-valued.
*/
public boolean isSingleValued() {
return index == -1 || SINGLE_VALUED[index];
}
/**
* Return the OID of this attribute.
*/
public ObjectIdentifier getOID() {
return oid;
}
/**
* Return the name of this attribute.
*/
public String getName() {
String n = oid.toString();
KnownOIDs os = KnownOIDs.findMatch(n);
return (os == null? n : os.stdName());
}
/**
* Return the OID for a given attribute name or null if we don't recognize
* the name.
*/
public static ObjectIdentifier getOID(String name) {
KnownOIDs o = KnownOIDs.findMatch(name);
if (o != null) {
return ObjectIdentifier.of(o);
} else {
return null;
}
}
/**
* Return the attribute name for a given OID or null if we don't recognize
* the oid.
*/
public static String getName(ObjectIdentifier oid) {
return KnownOIDs.findMatch(oid.toString()).stdName();
}
/**
* Returns a string representation of this attribute.
*/
public String toString() {
StringBuffer buf = new StringBuffer(100);
buf.append("[");
if (index == -1) {
buf.append(oid.toString());
} else {
buf.append(getName(oid));
}
buf.append(": ");
if (index == -1 || SINGLE_VALUED[index]) {
if (value instanceof byte[]) { // special case for octet string
HexDumpEncoder hexDump = new HexDumpEncoder();
buf.append(hexDump.encodeBuffer((byte[]) value));
} else {
buf.append(value.toString());
}
buf.append("]");
return buf.toString();
} else { // multi-valued
boolean first = true;
Object[] values = (Object[]) value;
for (int j=0; j < values.length; j++) {
if (first)
first = false;
else
buf.append(", ");
buf.append(values[j].toString());
}
return buf.toString();
}
}
/**
* Beginning the search at <code>start</code>, find the first
* index <code>i</code> such that <code>a[i] = obj</code>.
*
* @return the index, if found, and -1 otherwise.
*/
static int indexOf(Object obj, Object[] a, int start) {
for (int i=start; i < a.length; i++) {
if (obj.equals(a[i])) return i;
}
return -1;
}
/**
* Throw an exception when there are multiple values for
* a single-valued attribute.
*/
private void throwSingleValuedException() throws IOException {
throw new IOException("Single-value attribute " +
oid + " (" + getName() + ")" +
" has multiple values.");
}
/**
* Throw an exception when the tag on a value encoding is
* wrong for the attribute whose value it is. This method
* will only be called for known tags.
*/
private void throwTagException(Byte tag)
throws IOException {
Byte[] expectedTags = PKCS9_VALUE_TAGS[index];
StringBuffer msg = new StringBuffer(100);
msg.append("Value of attribute ");
msg.append(oid.toString());
msg.append(" (");
msg.append(getName());
msg.append(") has wrong tag: ");
msg.append(tag.toString());
msg.append(". Expected tags: ");
msg.append(expectedTags[0].toString());
for (int i = 1; i < expectedTags.length; i++) {
msg.append(", ");
msg.append(expectedTags[i].toString());
}
msg.append(".");
throw new IOException(msg.toString());
}
}

View File

@@ -0,0 +1,369 @@
/*
* Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;
import sun.security.util.DerEncoder;
import sun.security.util.DerValue;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.util.ObjectIdentifier;
/**
* A set of attributes of class PKCS9Attribute.
*
* @author Douglas Hoover
*/
public class PKCS9Attributes {
/**
* Attributes in this set indexed by OID.
*/
private final Hashtable<ObjectIdentifier, PKCS9Attribute> attributes =
new Hashtable<ObjectIdentifier, PKCS9Attribute>(3);
/**
* The keys of this hashtable are the OIDs of permitted attributes.
*/
private final Hashtable<ObjectIdentifier, ObjectIdentifier> permittedAttributes;
/**
* The DER encoding of this attribute set. The tag byte must be
* DerValue.tag_SetOf.
*/
private final byte[] derEncoding;
/*
* Contols how attributes, which are not recognized by the PKCS9Attribute
* class, are handled during parsing.
*/
private boolean ignoreUnsupportedAttributes = false;
/**
* Construct a set of PKCS9 Attributes from its
* DER encoding on a DerInputStream, accepting only attributes
* with OIDs on the given
* list. If the array is null, accept all attributes supported by
* class PKCS9Attribute.
*
* @param permittedAttributes
* Array of attribute OIDs that will be accepted.
* @param in
* the contents of the DER encoding of the attribute set.
*
* @exception IOException
* on i/o error, encoding syntax error, unacceptable or
* unsupported attribute, or duplicate attribute.
*
* @see PKCS9Attribute
*/
public PKCS9Attributes(ObjectIdentifier[] permittedAttributes,
DerInputStream in) throws IOException {
if (permittedAttributes != null) {
this.permittedAttributes =
new Hashtable<ObjectIdentifier, ObjectIdentifier>(
permittedAttributes.length);
for (int i = 0; i < permittedAttributes.length; i++)
this.permittedAttributes.put(permittedAttributes[i],
permittedAttributes[i]);
} else {
this.permittedAttributes = null;
}
// derEncoding initialized in <code>decode()</code>
derEncoding = decode(in);
}
/**
* Construct a set of PKCS9 Attributes from the contents of its
* DER encoding on a DerInputStream. Accept all attributes
* supported by class PKCS9Attribute and reject any unsupported
* attributes.
*
* @param in the contents of the DER encoding of the attribute set.
* @exception IOException
* on i/o error, encoding syntax error, or unsupported or
* duplicate attribute.
*
* @see PKCS9Attribute
*/
public PKCS9Attributes(DerInputStream in) throws IOException {
this(in, false);
}
/**
* Construct a set of PKCS9 Attributes from the contents of its
* DER encoding on a DerInputStream. Accept all attributes
* supported by class PKCS9Attribute and ignore any unsupported
* attributes, if directed.
*
* @param in the contents of the DER encoding of the attribute set.
* @param ignoreUnsupportedAttributes If true then any attributes
* not supported by the PKCS9Attribute class are ignored. Otherwise
* unsupported attributes cause an exception to be thrown.
* @exception IOException
* on i/o error, encoding syntax error, or unsupported or
* duplicate attribute.
*
* @see PKCS9Attribute
*/
public PKCS9Attributes(DerInputStream in,
boolean ignoreUnsupportedAttributes) throws IOException {
this.ignoreUnsupportedAttributes = ignoreUnsupportedAttributes;
// derEncoding initialized in <code>decode()</code>
derEncoding = decode(in);
permittedAttributes = null;
}
/**
* Construct a set of PKCS9 Attributes from the given array of
* PKCS9 attributes.
* DER encoding on a DerInputStream. All attributes in
* <code>attribs</code> must be
* supported by class PKCS9Attribute.
*
* @exception IOException
* on i/o error, encoding syntax error, or unsupported or
* duplicate attribute.
*
* @see PKCS9Attribute
*/
public PKCS9Attributes(PKCS9Attribute[] attribs)
throws IllegalArgumentException, IOException {
ObjectIdentifier oid;
for (int i=0; i < attribs.length; i++) {
oid = attribs[i].getOID();
if (attributes.containsKey(oid))
throw new IllegalArgumentException(
"PKCSAttribute " + attribs[i].getOID() +
" duplicated while constructing " +
"PKCS9Attributes.");
attributes.put(oid, attribs[i]);
}
derEncoding = generateDerEncoding();
permittedAttributes = null;
}
/**
* Decode this set of PKCS9 attributes from the contents of its
* DER encoding. Ignores unsupported attributes when directed.
*
* @param in
* the contents of the DER encoding of the attribute set.
*
* @exception IOException
* on i/o error, encoding syntax error, unacceptable or
* unsupported attribute, or duplicate attribute.
*/
private byte[] decode(DerInputStream in) throws IOException {
DerValue val = in.getDerValue();
// save the DER encoding with its proper tag byte.
byte[] derEncoding = val.toByteArray();
derEncoding[0] = DerValue.tag_SetOf;
DerInputStream derIn = new DerInputStream(derEncoding);
DerValue[] derVals = derIn.getSet(3,true);
PKCS9Attribute attrib;
ObjectIdentifier oid;
boolean reuseEncoding = true;
for (int i=0; i < derVals.length; i++) {
try {
attrib = new PKCS9Attribute(derVals[i]);
} catch (ParsingException e) {
if (ignoreUnsupportedAttributes) {
reuseEncoding = false; // cannot reuse supplied DER encoding
continue; // skip
} else {
throw e;
}
}
oid = attrib.getOID();
if (attributes.get(oid) != null)
throw new IOException("Duplicate PKCS9 attribute: " + oid);
if (permittedAttributes != null &&
!permittedAttributes.containsKey(oid))
throw new IOException("Attribute " + oid +
" not permitted in this attribute set");
attributes.put(oid, attrib);
}
return reuseEncoding ? derEncoding : generateDerEncoding();
}
/**
* Put the DER encoding of this PKCS9 attribute set on an
* DerOutputStream, tagged with the given implicit tag.
*
* @param tag the implicit tag to use in the DER encoding.
* @param out the output stream on which to put the DER encoding.
*
* @exception IOException on output error.
*/
public void encode(byte tag, OutputStream out) throws IOException {
out.write(tag);
out.write(derEncoding, 1, derEncoding.length -1);
}
private byte[] generateDerEncoding() throws IOException {
DerOutputStream out = new DerOutputStream();
Object[] attribVals = attributes.values().toArray();
out.putOrderedSetOf(DerValue.tag_SetOf,
castToDerEncoder(attribVals));
return out.toByteArray();
}
/**
* Return the DER encoding of this attribute set, tagged with
* DerValue.tag_SetOf.
*/
public byte[] getDerEncoding() throws IOException {
return derEncoding.clone();
}
/**
* Get an attribute from this set.
*/
public PKCS9Attribute getAttribute(ObjectIdentifier oid) {
return attributes.get(oid);
}
/**
* Get an attribute from this set.
*/
public PKCS9Attribute getAttribute(String name) {
return attributes.get(PKCS9Attribute.getOID(name));
}
/**
* Get an array of all attributes in this set, in order of OID.
*/
public PKCS9Attribute[] getAttributes() {
PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()];
int j = 0;
for (int i=1; i < PKCS9Attribute.PKCS9_OIDS.length &&
j < attribs.length; i++) {
if (PKCS9Attribute.PKCS9_OIDS[i] == null) {
continue;
}
attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
if (attribs[j] != null)
j++;
}
return attribs;
}
/**
* Get an attribute value by OID.
*/
public Object getAttributeValue(ObjectIdentifier oid)
throws IOException {
try {
Object value = getAttribute(oid).getValue();
return value;
} catch (NullPointerException ex) {
throw new IOException("No value found for attribute " + oid);
}
}
/**
* Get an attribute value by type name.
*/
public Object getAttributeValue(String name) throws IOException {
ObjectIdentifier oid = PKCS9Attribute.getOID(name);
if (oid == null)
throw new IOException("Attribute name " + name +
" not recognized or not supported.");
return getAttributeValue(oid);
}
/**
* Returns the PKCS9 block in a printable string form.
*/
public String toString() {
StringBuffer buf = new StringBuffer(200);
buf.append("PKCS9 Attributes: [\n\t");
PKCS9Attribute value;
boolean first = true;
for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) {
if (PKCS9Attribute.PKCS9_OIDS[i] == null) {
continue;
}
value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]);
if (value == null) continue;
// we have a value; print it
if (first)
first = false;
else
buf.append(";\n\t");
buf.append(value);
}
buf.append("\n\t] (end PKCS9 Attributes)");
return buf.toString();
}
/**
* Cast an object array whose components are
* <code>DerEncoder</code>s to <code>DerEncoder[]</code>.
*/
static DerEncoder[] castToDerEncoder(Object[] objs) {
DerEncoder[] encoders = new DerEncoder[objs.length];
for (int i=0; i < encoders.length; i++)
encoders[i] = (DerEncoder) objs[i];
return encoders;
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 1996, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Generic PKCS Parsing exception.
*
* @author Benjamin Renaud
*/
package sun.security.pkcs;
import java.io.IOException;
public class ParsingException extends IOException {
private static final long serialVersionUID = -6316569918966181883L;
public ParsingException() {
super();
}
public ParsingException(String s) {
super(s);
}
}

View File

@@ -0,0 +1,663 @@
/*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.OutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.security.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import sun.misc.HexDumpEncoder;
import sun.security.timestamp.TimestampToken;
import sun.security.util.Debug;
import sun.security.util.DerEncoder;
import sun.security.util.DerInputStream;
import sun.security.util.DerOutputStream;
import sun.security.util.DerValue;
import sun.security.util.DisabledAlgorithmConstraints;
import sun.security.util.JarConstraintsParameters;
import sun.security.util.KeyUtil;
import sun.security.util.ObjectIdentifier;
import sun.security.util.SignatureUtil;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X500Name;
import sun.security.x509.KeyUsageExtension;
/**
* A SignerInfo, as defined in PKCS#7's signedData type.
*
* @author Benjamin Renaud
*/
public class SignerInfo implements DerEncoder {
private static final DisabledAlgorithmConstraints JAR_DISABLED_CHECK =
DisabledAlgorithmConstraints.jarConstraints();
BigInteger version;
X500Name issuerName;
BigInteger certificateSerialNumber;
AlgorithmId digestAlgorithmId;
AlgorithmId digestEncryptionAlgorithmId;
byte[] encryptedDigest;
Timestamp timestamp;
private boolean hasTimestamp = true;
private static final Debug debug = Debug.getInstance("jar");
PKCS9Attributes authenticatedAttributes;
PKCS9Attributes unauthenticatedAttributes;
/**
* A map containing the algorithms in this SignerInfo. This is used to
* avoid checking algorithms to see if they are disabled more than once.
* The key is the AlgorithmId of the algorithm, and the value is a record
* containing the name of the field or attribute and whether the key
* should also be checked (ex: if it is a signature algorithm).
*/
private class AlgorithmInfo {
String field;
boolean checkKey;
private AlgorithmInfo(String f, boolean cK) {
field = f;
checkKey = cK;
}
String field() { return field; }
boolean checkKey() { return checkKey; }
}
private Map<AlgorithmId, AlgorithmInfo> algorithms = new HashMap<>();
public SignerInfo(X500Name issuerName,
BigInteger serial,
AlgorithmId digestAlgorithmId,
AlgorithmId digestEncryptionAlgorithmId,
byte[] encryptedDigest) {
this.version = BigInteger.ONE;
this.issuerName = issuerName;
this.certificateSerialNumber = serial;
this.digestAlgorithmId = digestAlgorithmId;
this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
this.encryptedDigest = encryptedDigest;
}
public SignerInfo(X500Name issuerName,
BigInteger serial,
AlgorithmId digestAlgorithmId,
PKCS9Attributes authenticatedAttributes,
AlgorithmId digestEncryptionAlgorithmId,
byte[] encryptedDigest,
PKCS9Attributes unauthenticatedAttributes) {
this.version = BigInteger.ONE;
this.issuerName = issuerName;
this.certificateSerialNumber = serial;
this.digestAlgorithmId = digestAlgorithmId;
this.authenticatedAttributes = authenticatedAttributes;
this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
this.encryptedDigest = encryptedDigest;
this.unauthenticatedAttributes = unauthenticatedAttributes;
}
/**
* Parses a PKCS#7 signer info.
*/
public SignerInfo(DerInputStream derin)
throws IOException, ParsingException
{
this(derin, false);
}
/**
* Parses a PKCS#7 signer info.
*
* <p>This constructor is used only for backwards compatibility with
* PKCS#7 blocks that were generated using JDK1.1.x.
*
* @param derin the ASN.1 encoding of the signer info.
* @param oldStyle flag indicating whether or not the given signer info
* is encoded according to JDK1.1.x.
*/
public SignerInfo(DerInputStream derin, boolean oldStyle)
throws IOException, ParsingException
{
// version
version = derin.getBigInteger();
// issuerAndSerialNumber
DerValue[] issuerAndSerialNumber = derin.getSequence(2);
if (issuerAndSerialNumber.length != 2) {
throw new ParsingException("Invalid length for IssuerAndSerialNumber");
}
byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray();
issuerName = new X500Name(new DerValue(DerValue.tag_Sequence,
issuerBytes));
certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger();
// digestAlgorithmId
DerValue tmp = derin.getDerValue();
digestAlgorithmId = AlgorithmId.parse(tmp);
// authenticatedAttributes
if (oldStyle) {
// In JDK1.1.x, the authenticatedAttributes are always present,
// encoded as an empty Set (Set of length zero)
derin.getSet(0);
} else {
// check if set of auth attributes (implicit tag) is provided
// (auth attributes are OPTIONAL)
if ((byte)(derin.peekByte()) == (byte)0xA0) {
authenticatedAttributes = new PKCS9Attributes(derin);
}
}
// digestEncryptionAlgorithmId - little RSA naming scheme -
// signature == encryption...
tmp = derin.getDerValue();
digestEncryptionAlgorithmId = AlgorithmId.parse(tmp);
// encryptedDigest
encryptedDigest = derin.getOctetString();
// unauthenticatedAttributes
if (oldStyle) {
// In JDK1.1.x, the unauthenticatedAttributes are always present,
// encoded as an empty Set (Set of length zero)
derin.getSet(0);
} else {
// check if set of unauth attributes (implicit tag) is provided
// (unauth attributes are OPTIONAL)
if (derin.available() != 0
&& (byte)(derin.peekByte()) == (byte)0xA1) {
unauthenticatedAttributes =
new PKCS9Attributes(derin, true);// ignore unsupported attrs
}
}
// all done
if (derin.available() != 0) {
throw new ParsingException("extra data at the end");
}
}
public void encode(DerOutputStream out) throws IOException {
derEncode(out);
}
/**
* DER encode this object onto an output stream.
* Implements the {@code DerEncoder} interface.
*
* @param out
* the output stream on which to write the DER encoding.
*
* @exception IOException on encoding error.
*/
public void derEncode(OutputStream out) throws IOException {
DerOutputStream seq = new DerOutputStream();
seq.putInteger(version);
DerOutputStream issuerAndSerialNumber = new DerOutputStream();
issuerName.encode(issuerAndSerialNumber);
issuerAndSerialNumber.putInteger(certificateSerialNumber);
seq.write(DerValue.tag_Sequence, issuerAndSerialNumber);
digestAlgorithmId.encode(seq);
// encode authenticated attributes if there are any
if (authenticatedAttributes != null)
authenticatedAttributes.encode((byte)0xA0, seq);
digestEncryptionAlgorithmId.encode(seq);
seq.putOctetString(encryptedDigest);
// encode unauthenticated attributes if there are any
if (unauthenticatedAttributes != null)
unauthenticatedAttributes.encode((byte)0xA1, seq);
DerOutputStream tmp = new DerOutputStream();
tmp.write(DerValue.tag_Sequence, seq);
out.write(tmp.toByteArray());
}
/*
* Returns the (user) certificate pertaining to this SignerInfo.
*/
public X509Certificate getCertificate(PKCS7 block)
throws IOException
{
return block.getCertificate(certificateSerialNumber, issuerName);
}
/*
* Returns the certificate chain pertaining to this SignerInfo.
*/
public ArrayList<X509Certificate> getCertificateChain(PKCS7 block)
throws IOException
{
X509Certificate userCert;
userCert = block.getCertificate(certificateSerialNumber, issuerName);
if (userCert == null)
return null;
ArrayList<X509Certificate> certList = new ArrayList<>();
certList.add(userCert);
X509Certificate[] pkcsCerts = block.getCertificates();
if (pkcsCerts == null
|| userCert.getSubjectDN().equals(userCert.getIssuerDN())) {
return certList;
}
Principal issuer = userCert.getIssuerDN();
int start = 0;
while (true) {
boolean match = false;
int i = start;
while (i < pkcsCerts.length) {
if (issuer.equals(pkcsCerts[i].getSubjectDN())) {
// next cert in chain found
certList.add(pkcsCerts[i]);
// if selected cert is self-signed, we're done
// constructing the chain
if (pkcsCerts[i].getSubjectDN().equals(
pkcsCerts[i].getIssuerDN())) {
start = pkcsCerts.length;
} else {
issuer = pkcsCerts[i].getIssuerDN();
X509Certificate tmpCert = pkcsCerts[start];
pkcsCerts[start] = pkcsCerts[i];
pkcsCerts[i] = tmpCert;
start++;
}
match = true;
break;
} else {
i++;
}
}
if (!match)
break;
}
return certList;
}
/* Returns null if verify fails, this signerInfo if
verify succeeds. */
SignerInfo verify(PKCS7 block, byte[] data)
throws NoSuchAlgorithmException, SignatureException {
try {
Timestamp timestamp = getTimestamp();
ContentInfo content = block.getContentInfo();
if (data == null) {
data = content.getContentBytes();
}
String digestAlgName = digestAlgorithmId.getName();
algorithms.put(digestAlgorithmId,
new AlgorithmInfo("SignerInfo digestAlgorithm field", false));
byte[] dataSigned;
// if there are authenticate attributes, get the message
// digest and compare it with the digest of data
if (authenticatedAttributes == null) {
dataSigned = data;
} else {
// first, check content type
ObjectIdentifier contentType = (ObjectIdentifier)
authenticatedAttributes.getAttributeValue(
PKCS9Attribute.CONTENT_TYPE_OID);
if (contentType == null ||
!contentType.equals((Object)content.contentType))
return null; // contentType does not match, bad SignerInfo
// now, check message digest
byte[] messageDigest = (byte[])
authenticatedAttributes.getAttributeValue(
PKCS9Attribute.MESSAGE_DIGEST_OID);
if (messageDigest == null) // fail if there is no message digest
return null;
MessageDigest md = MessageDigest.getInstance(digestAlgName);
byte[] computedMessageDigest = md.digest(data);
if (!MessageDigest.isEqual(messageDigest, computedMessageDigest)) {
return null;
}
// message digest attribute matched
// digest of original data
// the data actually signed is the DER encoding of
// the authenticated attributes (tagged with
// the "SET OF" tag, not 0xA0).
dataSigned = authenticatedAttributes.getDerEncoding();
}
// put together digest algorithm and encryption algorithm
// to form signing algorithm
String encryptionAlgName =
getDigestEncryptionAlgorithmId().getName();
// Workaround: sometimes the encryptionAlgname is actually
// a signature name
String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgName);
if (tmp != null) encryptionAlgName = tmp;
String sigAlgName = AlgorithmId.makeSigAlg(
digestAlgName, encryptionAlgName);
try {
ObjectIdentifier oid = AlgorithmId.get(sigAlgName).getOID();
AlgorithmId sigAlgId =
new AlgorithmId(oid,
digestEncryptionAlgorithmId.getParameters());
algorithms.put(sigAlgId,
new AlgorithmInfo(
"SignerInfo digestEncryptionAlgorithm field", true));
} catch (NoSuchAlgorithmException ignore) {
}
X509Certificate cert = getCertificate(block);
if (cert == null) {
return null;
}
PublicKey key = cert.getPublicKey();
if (cert.hasUnsupportedCriticalExtension()) {
throw new SignatureException("Certificate has unsupported "
+ "critical extension(s)");
}
// Make sure that if the usage of the key in the certificate is
// restricted, it can be used for digital signatures.
// XXX We may want to check for additional extensions in the
// future.
boolean[] keyUsageBits = cert.getKeyUsage();
if (keyUsageBits != null) {
KeyUsageExtension keyUsage;
try {
// We don't care whether or not this extension was marked
// critical in the certificate.
// We're interested only in its value (i.e., the bits set)
// and treat the extension as critical.
keyUsage = new KeyUsageExtension(keyUsageBits);
} catch (IOException ioe) {
throw new SignatureException("Failed to parse keyUsage "
+ "extension");
}
boolean digSigAllowed = keyUsage.get(
KeyUsageExtension.DIGITAL_SIGNATURE).booleanValue();
boolean nonRepuAllowed = keyUsage.get(
KeyUsageExtension.NON_REPUDIATION).booleanValue();
if (!digSigAllowed && !nonRepuAllowed) {
throw new SignatureException("Key usage restricted: "
+ "cannot be used for "
+ "digital signatures");
}
}
Signature sig = Signature.getInstance(sigAlgName);
AlgorithmParameters ap =
digestEncryptionAlgorithmId.getParameters();
try {
SignatureUtil.initVerifyWithParam(sig, key,
SignatureUtil.getParamSpec(sigAlgName, ap));
} catch (ProviderException | InvalidAlgorithmParameterException |
InvalidKeyException e) {
throw new SignatureException(e.getMessage(), e);
}
sig.update(dataSigned);
if (sig.verify(encryptedDigest)) {
return this;
}
} catch (IOException | CertificateException e) {
throw new SignatureException("Error verifying signature", e);
}
return null;
}
/* Verify the content of the pkcs7 block. */
SignerInfo verify(PKCS7 block)
throws NoSuchAlgorithmException, SignatureException {
return verify(block, null);
}
public BigInteger getVersion() {
return version;
}
public X500Name getIssuerName() {
return issuerName;
}
public BigInteger getCertificateSerialNumber() {
return certificateSerialNumber;
}
public AlgorithmId getDigestAlgorithmId() {
return digestAlgorithmId;
}
public PKCS9Attributes getAuthenticatedAttributes() {
return authenticatedAttributes;
}
public AlgorithmId getDigestEncryptionAlgorithmId() {
return digestEncryptionAlgorithmId;
}
public byte[] getEncryptedDigest() {
return encryptedDigest;
}
public PKCS9Attributes getUnauthenticatedAttributes() {
return unauthenticatedAttributes;
}
/**
* Returns the timestamp PKCS7 data unverified.
* @return a PKCS7 object
*/
public PKCS7 getTsToken() throws IOException {
if (unauthenticatedAttributes == null) {
return null;
}
PKCS9Attribute tsTokenAttr =
unauthenticatedAttributes.getAttribute(
PKCS9Attribute.SIGNATURE_TIMESTAMP_TOKEN_OID);
if (tsTokenAttr == null) {
return null;
}
return new PKCS7((byte[])tsTokenAttr.getValue());
}
/*
* Extracts a timestamp from a PKCS7 SignerInfo.
*
* Examines the signer's unsigned attributes for a
* {@code signatureTimestampToken} attribute. If present,
* then it is parsed to extract the date and time at which the
* timestamp was generated.
*
* @param info A signer information element of a PKCS 7 block.
*
* @return A timestamp token or null if none is present.
* @throws IOException if an error is encountered while parsing the
* PKCS7 data.
* @throws NoSuchAlgorithmException if an error is encountered while
* verifying the PKCS7 object.
* @throws SignatureException if an error is encountered while
* verifying the PKCS7 object.
* @throws CertificateException if an error is encountered while generating
* the TSA's certpath.
*/
public Timestamp getTimestamp()
throws IOException, NoSuchAlgorithmException, SignatureException,
CertificateException
{
if (timestamp != null || !hasTimestamp)
return timestamp;
PKCS7 tsToken = getTsToken();
if (tsToken == null) {
hasTimestamp = false;
return null;
}
// Extract the content (an encoded timestamp token info)
byte[] encTsTokenInfo = tsToken.getContentInfo().getData();
// Extract the signer (the Timestamping Authority)
// while verifying the content
SignerInfo[] tsa = tsToken.verify(encTsTokenInfo);
if (tsa == null || tsa.length == 0) {
throw new SignatureException("Unable to verify timestamp");
}
// Expect only one signer
ArrayList<X509Certificate> chain = tsa[0].getCertificateChain(tsToken);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
CertPath tsaChain = cf.generateCertPath(chain);
// Create a timestamp token info object
TimestampToken tsTokenInfo = new TimestampToken(encTsTokenInfo);
// Check that the signature timestamp applies to this signature
verifyTimestamp(tsTokenInfo);
algorithms.putAll(tsa[0].algorithms);
// Create a timestamp object
timestamp = new Timestamp(tsTokenInfo.getDate(), tsaChain);
return timestamp;
}
/*
* Check that the signature timestamp applies to this signature.
* Match the hash present in the signature timestamp token against the hash
* of this signature.
*/
private void verifyTimestamp(TimestampToken token)
throws NoSuchAlgorithmException, SignatureException {
AlgorithmId digestAlgId = token.getHashAlgorithm();
algorithms.put(digestAlgId,
new AlgorithmInfo("TimestampToken digestAlgorithm field", false));
MessageDigest md = MessageDigest.getInstance(digestAlgId.getName());
if (!MessageDigest.isEqual(token.getHashedMessage(),
md.digest(encryptedDigest))) {
throw new SignatureException("Signature timestamp (#" +
token.getSerialNumber() + ") generated on " + token.getDate() +
" is inapplicable");
}
if (debug != null) {
debug.println();
debug.println("Detected signature timestamp (#" +
token.getSerialNumber() + ") generated on " + token.getDate());
debug.println();
}
}
public String toString() {
HexDumpEncoder hexDump = new HexDumpEncoder();
String out = "";
out += "Signer Info for (issuer): " + issuerName + "\n";
out += "\tversion: " + Debug.toHexString(version) + "\n";
out += "\tcertificateSerialNumber: " +
Debug.toHexString(certificateSerialNumber) + "\n";
out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n";
if (authenticatedAttributes != null) {
out += "\tauthenticatedAttributes: " + authenticatedAttributes +
"\n";
}
out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId +
"\n";
out += "\tencryptedDigest: " + "\n" +
hexDump.encodeBuffer(encryptedDigest) + "\n";
if (unauthenticatedAttributes != null) {
out += "\tunauthenticatedAttributes: " +
unauthenticatedAttributes + "\n";
}
return out;
}
/**
* Verify all of the algorithms in the array of SignerInfos against the
* constraints in the jdk.jar.disabledAlgorithms security property.
*
* @param infos array of SignerInfos
* @param params constraint parameters
* @param name the name of the signer's PKCS7 file
* @return a set of algorithms that passed the checks and are not disabled
*/
public static Set<String> verifyAlgorithms(SignerInfo[] infos,
JarConstraintsParameters params, String name) throws SignatureException {
Map<AlgorithmId, AlgorithmInfo> algorithms = new HashMap<>();
for (SignerInfo info : infos) {
algorithms.putAll(info.algorithms);
}
Set<String> enabledAlgorithms = new HashSet<>();
try {
for (Map.Entry<AlgorithmId,AlgorithmInfo> algEntry : algorithms.entrySet()) {
AlgorithmInfo info = algEntry.getValue();
params.setExtendedExceptionMsg(name, info.field());
AlgorithmId algId = algEntry.getKey();
JAR_DISABLED_CHECK.permits(algId.getName(),
algId.getParameters(), params, info.checkKey());
enabledAlgorithms.add(algId.getName());
}
} catch (CertPathValidatorException e) {
throw new SignatureException(e);
}
return enabledAlgorithms;
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.pkcs;
import java.io.IOException;
import java.util.ArrayList;
import sun.misc.HexDumpEncoder;
import sun.security.util.DerInputStream;
import sun.security.util.DerValue;
import sun.security.x509.GeneralNames;
import sun.security.x509.SerialNumber;
/**
* This class represents a signing certificate attribute.
* Its attribute value is defined by the following ASN.1 definition.
* <pre>
*
* id-aa-signingCertificate OBJECT IDENTIFIER ::= { iso(1)
* member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
* smime(16) id-aa(2) 12 }
*
* SigningCertificate ::= SEQUENCE {
* certs SEQUENCE OF ESSCertID,
* policies SEQUENCE OF PolicyInformation OPTIONAL
* }
*
* ESSCertID ::= SEQUENCE {
* certHash Hash,
* issuerSerial IssuerSerial OPTIONAL
* }
*
* Hash ::= OCTET STRING -- SHA1 hash of entire certificate
*
* IssuerSerial ::= SEQUENCE {
* issuer GeneralNames,
* serialNumber CertificateSerialNumber
* }
*
* PolicyInformation ::= SEQUENCE {
* policyIdentifier CertPolicyId,
* policyQualifiers SEQUENCE SIZE (1..MAX) OF
* PolicyQualifierInfo OPTIONAL }
*
* CertPolicyId ::= OBJECT IDENTIFIER
*
* PolicyQualifierInfo ::= SEQUENCE {
* policyQualifierId PolicyQualifierId,
* qualifier ANY DEFINED BY policyQualifierId }
*
* -- Implementations that recognize additional policy qualifiers MUST
* -- augment the following definition for PolicyQualifierId
*
* PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
*
* </pre>
*
* @since 1.5
* @author Vincent Ryan
*/
public class SigningCertificateInfo {
private byte[] ber = null;
private ESSCertId[] certId = null;
public SigningCertificateInfo(byte[] ber) throws IOException {
parse(ber);
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[\n");
for (int i = 0; i < certId.length; i++) {
buffer.append(certId[i].toString());
}
// format policies as a string
buffer.append("\n]");
return buffer.toString();
}
public void parse(byte[] bytes) throws IOException {
// Parse signingCertificate
DerValue derValue = new DerValue(bytes);
if (derValue.tag != DerValue.tag_Sequence) {
throw new IOException("Bad encoding for signingCertificate");
}
// Parse certs
DerValue[] certs = derValue.data.getSequence(1);
certId = new ESSCertId[certs.length];
for (int i = 0; i < certs.length; i++) {
certId[i] = new ESSCertId(certs[i]);
}
// Parse policies, if present
if (derValue.data.available() > 0) {
DerValue[] policies = derValue.data.getSequence(1);
for (int i = 0; i < policies.length; i++) {
// parse PolicyInformation
}
}
}
}
class ESSCertId {
private static volatile HexDumpEncoder hexDumper;
private byte[] certHash;
private GeneralNames issuer;
private SerialNumber serialNumber;
ESSCertId(DerValue certId) throws IOException {
// Parse certHash
certHash = certId.data.getDerValue().toByteArray();
// Parse issuerSerial, if present
if (certId.data.available() > 0) {
DerValue issuerSerial = certId.data.getDerValue();
// Parse issuer
issuer = new GeneralNames(issuerSerial.data.getDerValue());
// Parse serialNumber
serialNumber = new SerialNumber(issuerSerial.data.getDerValue());
}
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("[\n\tCertificate hash (SHA-1):\n");
if (hexDumper == null) {
hexDumper = new HexDumpEncoder();
}
buffer.append(hexDumper.encode(certHash));
if (issuer != null && serialNumber != null) {
buffer.append("\n\tIssuer: " + issuer + "\n");
buffer.append("\t" + serialNumber);
}
buffer.append("\n]");
return buffer.toString();
}
}