feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
226
jdkSrc/jdk8/sun/security/pkcs/ContentInfo.java
Normal file
226
jdkSrc/jdk8/sun/security/pkcs/ContentInfo.java
Normal 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;
|
||||
}
|
||||
}
|
||||
172
jdkSrc/jdk8/sun/security/pkcs/EncryptedPrivateKeyInfo.java
Normal file
172
jdkSrc/jdk8/sun/security/pkcs/EncryptedPrivateKeyInfo.java
Normal 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;
|
||||
}
|
||||
}
|
||||
961
jdkSrc/jdk8/sun/security/pkcs/PKCS7.java
Normal file
961
jdkSrc/jdk8/sun/security/pkcs/PKCS7.java
Normal 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();
|
||||
}
|
||||
}
|
||||
442
jdkSrc/jdk8/sun/security/pkcs/PKCS8Key.java
Normal file
442
jdkSrc/jdk8/sun/security/pkcs/PKCS8Key.java
Normal 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);
|
||||
}
|
||||
}
|
||||
781
jdkSrc/jdk8/sun/security/pkcs/PKCS9Attribute.java
Normal file
781
jdkSrc/jdk8/sun/security/pkcs/PKCS9Attribute.java
Normal 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());
|
||||
}
|
||||
}
|
||||
369
jdkSrc/jdk8/sun/security/pkcs/PKCS9Attributes.java
Normal file
369
jdkSrc/jdk8/sun/security/pkcs/PKCS9Attributes.java
Normal 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;
|
||||
}
|
||||
}
|
||||
47
jdkSrc/jdk8/sun/security/pkcs/ParsingException.java
Normal file
47
jdkSrc/jdk8/sun/security/pkcs/ParsingException.java
Normal 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);
|
||||
}
|
||||
}
|
||||
663
jdkSrc/jdk8/sun/security/pkcs/SignerInfo.java
Normal file
663
jdkSrc/jdk8/sun/security/pkcs/SignerInfo.java
Normal 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;
|
||||
}
|
||||
}
|
||||
167
jdkSrc/jdk8/sun/security/pkcs/SigningCertificateInfo.java
Normal file
167
jdkSrc/jdk8/sun/security/pkcs/SigningCertificateInfo.java
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user