0N/A/*
5212N/A * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.pkcs;
0N/A
0N/Aimport java.io.OutputStream;
0N/Aimport java.io.IOException;
0N/Aimport java.math.BigInteger;
0N/Aimport java.security.cert.X509Certificate;
0N/Aimport java.security.*;
0N/Aimport java.util.ArrayList;
0N/A
0N/Aimport sun.security.util.*;
0N/Aimport sun.security.x509.AlgorithmId;
0N/Aimport sun.security.x509.X500Name;
0N/Aimport sun.security.x509.KeyUsageExtension;
0N/Aimport sun.security.x509.PKIXExtensions;
0N/Aimport sun.misc.HexDumpEncoder;
0N/A
0N/A/**
0N/A * A SignerInfo, as defined in PKCS#7's signedData type.
0N/A *
0N/A * @author Benjamin Renaud
0N/A */
0N/Apublic class SignerInfo implements DerEncoder {
0N/A
0N/A BigInteger version;
0N/A X500Name issuerName;
0N/A BigInteger certificateSerialNumber;
0N/A AlgorithmId digestAlgorithmId;
0N/A AlgorithmId digestEncryptionAlgorithmId;
0N/A byte[] encryptedDigest;
0N/A
0N/A PKCS9Attributes authenticatedAttributes;
0N/A PKCS9Attributes unauthenticatedAttributes;
0N/A
0N/A public SignerInfo(X500Name issuerName,
0N/A BigInteger serial,
0N/A AlgorithmId digestAlgorithmId,
0N/A AlgorithmId digestEncryptionAlgorithmId,
0N/A byte[] encryptedDigest) {
0N/A this.version = BigInteger.ONE;
0N/A this.issuerName = issuerName;
0N/A this.certificateSerialNumber = serial;
0N/A this.digestAlgorithmId = digestAlgorithmId;
0N/A this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
0N/A this.encryptedDigest = encryptedDigest;
0N/A }
0N/A
0N/A public SignerInfo(X500Name issuerName,
0N/A BigInteger serial,
0N/A AlgorithmId digestAlgorithmId,
0N/A PKCS9Attributes authenticatedAttributes,
0N/A AlgorithmId digestEncryptionAlgorithmId,
0N/A byte[] encryptedDigest,
0N/A PKCS9Attributes unauthenticatedAttributes) {
0N/A this.version = BigInteger.ONE;
0N/A this.issuerName = issuerName;
0N/A this.certificateSerialNumber = serial;
0N/A this.digestAlgorithmId = digestAlgorithmId;
0N/A this.authenticatedAttributes = authenticatedAttributes;
0N/A this.digestEncryptionAlgorithmId = digestEncryptionAlgorithmId;
0N/A this.encryptedDigest = encryptedDigest;
0N/A this.unauthenticatedAttributes = unauthenticatedAttributes;
0N/A }
0N/A
0N/A /**
0N/A * Parses a PKCS#7 signer info.
0N/A */
0N/A public SignerInfo(DerInputStream derin)
0N/A throws IOException, ParsingException
0N/A {
0N/A this(derin, false);
0N/A }
0N/A
0N/A /**
0N/A * Parses a PKCS#7 signer info.
0N/A *
0N/A * <p>This constructor is used only for backwards compatibility with
0N/A * PKCS#7 blocks that were generated using JDK1.1.x.
0N/A *
0N/A * @param derin the ASN.1 encoding of the signer info.
0N/A * @param oldStyle flag indicating whether or not the given signer info
0N/A * is encoded according to JDK1.1.x.
0N/A */
0N/A public SignerInfo(DerInputStream derin, boolean oldStyle)
0N/A throws IOException, ParsingException
0N/A {
0N/A // version
0N/A version = derin.getBigInteger();
0N/A
0N/A // issuerAndSerialNumber
0N/A DerValue[] issuerAndSerialNumber = derin.getSequence(2);
0N/A byte[] issuerBytes = issuerAndSerialNumber[0].toByteArray();
0N/A issuerName = new X500Name(new DerValue(DerValue.tag_Sequence,
0N/A issuerBytes));
0N/A certificateSerialNumber = issuerAndSerialNumber[1].getBigInteger();
0N/A
0N/A // digestAlgorithmId
0N/A DerValue tmp = derin.getDerValue();
0N/A
0N/A digestAlgorithmId = AlgorithmId.parse(tmp);
0N/A
0N/A // authenticatedAttributes
0N/A if (oldStyle) {
0N/A // In JDK1.1.x, the authenticatedAttributes are always present,
0N/A // encoded as an empty Set (Set of length zero)
0N/A derin.getSet(0);
0N/A } else {
0N/A // check if set of auth attributes (implicit tag) is provided
0N/A // (auth attributes are OPTIONAL)
0N/A if ((byte)(derin.peekByte()) == (byte)0xA0) {
0N/A authenticatedAttributes = new PKCS9Attributes(derin);
0N/A }
0N/A }
0N/A
0N/A // digestEncryptionAlgorithmId - little RSA naming scheme -
0N/A // signature == encryption...
0N/A tmp = derin.getDerValue();
0N/A
0N/A digestEncryptionAlgorithmId = AlgorithmId.parse(tmp);
0N/A
0N/A // encryptedDigest
0N/A encryptedDigest = derin.getOctetString();
0N/A
0N/A // unauthenticatedAttributes
0N/A if (oldStyle) {
0N/A // In JDK1.1.x, the unauthenticatedAttributes are always present,
0N/A // encoded as an empty Set (Set of length zero)
0N/A derin.getSet(0);
0N/A } else {
0N/A // check if set of unauth attributes (implicit tag) is provided
0N/A // (unauth attributes are OPTIONAL)
0N/A if (derin.available() != 0
0N/A && (byte)(derin.peekByte()) == (byte)0xA1) {
0N/A unauthenticatedAttributes =
0N/A new PKCS9Attributes(derin, true);// ignore unsupported attrs
0N/A }
0N/A }
0N/A
0N/A // all done
0N/A if (derin.available() != 0) {
0N/A throw new ParsingException("extra data at the end");
0N/A }
0N/A }
0N/A
0N/A public void encode(DerOutputStream out) throws IOException {
0N/A
0N/A derEncode(out);
0N/A }
0N/A
0N/A /**
0N/A * DER encode this object onto an output stream.
0N/A * Implements the <code>DerEncoder</code> interface.
0N/A *
0N/A * @param out
0N/A * the output stream on which to write the DER encoding.
0N/A *
0N/A * @exception IOException on encoding error.
0N/A */
0N/A public void derEncode(OutputStream out) throws IOException {
0N/A DerOutputStream seq = new DerOutputStream();
0N/A seq.putInteger(version);
0N/A DerOutputStream issuerAndSerialNumber = new DerOutputStream();
0N/A issuerName.encode(issuerAndSerialNumber);
0N/A issuerAndSerialNumber.putInteger(certificateSerialNumber);
0N/A seq.write(DerValue.tag_Sequence, issuerAndSerialNumber);
0N/A
0N/A digestAlgorithmId.encode(seq);
0N/A
0N/A // encode authenticated attributes if there are any
0N/A if (authenticatedAttributes != null)
0N/A authenticatedAttributes.encode((byte)0xA0, seq);
0N/A
0N/A digestEncryptionAlgorithmId.encode(seq);
0N/A
0N/A seq.putOctetString(encryptedDigest);
0N/A
0N/A // encode unauthenticated attributes if there are any
0N/A if (unauthenticatedAttributes != null)
0N/A unauthenticatedAttributes.encode((byte)0xA1, seq);
0N/A
0N/A DerOutputStream tmp = new DerOutputStream();
0N/A tmp.write(DerValue.tag_Sequence, seq);
0N/A
0N/A out.write(tmp.toByteArray());
0N/A }
0N/A
0N/A
0N/A
0N/A /*
0N/A * Returns the (user) certificate pertaining to this SignerInfo.
0N/A */
0N/A public X509Certificate getCertificate(PKCS7 block)
0N/A throws IOException
0N/A {
0N/A return block.getCertificate(certificateSerialNumber, issuerName);
0N/A }
0N/A
0N/A /*
0N/A * Returns the certificate chain pertaining to this SignerInfo.
0N/A */
0N/A public ArrayList<X509Certificate> getCertificateChain(PKCS7 block)
0N/A throws IOException
0N/A {
0N/A X509Certificate userCert;
0N/A userCert = block.getCertificate(certificateSerialNumber, issuerName);
0N/A if (userCert == null)
0N/A return null;
0N/A
4046N/A ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
0N/A certList.add(userCert);
0N/A
0N/A X509Certificate[] pkcsCerts = block.getCertificates();
0N/A if (pkcsCerts == null
0N/A || userCert.getSubjectDN().equals(userCert.getIssuerDN())) {
0N/A return certList;
0N/A }
0N/A
0N/A Principal issuer = userCert.getIssuerDN();
0N/A int start = 0;
0N/A while (true) {
0N/A boolean match = false;
0N/A int i = start;
0N/A while (i < pkcsCerts.length) {
0N/A if (issuer.equals(pkcsCerts[i].getSubjectDN())) {
0N/A // next cert in chain found
0N/A certList.add(pkcsCerts[i]);
0N/A // if selected cert is self-signed, we're done
0N/A // constructing the chain
0N/A if (pkcsCerts[i].getSubjectDN().equals(
0N/A pkcsCerts[i].getIssuerDN())) {
0N/A start = pkcsCerts.length;
0N/A } else {
0N/A issuer = pkcsCerts[i].getIssuerDN();
0N/A X509Certificate tmpCert = pkcsCerts[start];
0N/A pkcsCerts[start] = pkcsCerts[i];
0N/A pkcsCerts[i] = tmpCert;
0N/A start++;
0N/A }
0N/A match = true;
0N/A break;
0N/A } else {
0N/A i++;
0N/A }
0N/A }
0N/A if (!match)
0N/A break;
0N/A }
0N/A
0N/A return certList;
0N/A }
0N/A
5212N/A // Copied from com.sun.crypto.provider.OAEPParameters.
5212N/A private static String convertToStandardName(String internalName) {
5212N/A if (internalName.equals("SHA")) {
5212N/A return "SHA-1";
5212N/A } else if (internalName.equals("SHA224")) {
5212N/A return "SHA-224";
5212N/A } else if (internalName.equals("SHA256")) {
5212N/A return "SHA-256";
5212N/A } else if (internalName.equals("SHA384")) {
5212N/A return "SHA-384";
5212N/A } else if (internalName.equals("SHA512")) {
5212N/A return "SHA-512";
5212N/A } else {
5212N/A return internalName;
5212N/A }
5212N/A }
5212N/A
5212N/A
0N/A /* Returns null if verify fails, this signerInfo if
0N/A verify succeeds. */
0N/A SignerInfo verify(PKCS7 block, byte[] data)
4046N/A throws NoSuchAlgorithmException, SignatureException {
4046N/A
4046N/A try {
4046N/A
4046N/A ContentInfo content = block.getContentInfo();
4046N/A if (data == null) {
4046N/A data = content.getContentBytes();
4046N/A }
4046N/A
4046N/A String digestAlgname = getDigestAlgorithmId().getName();
4046N/A
4046N/A byte[] dataSigned;
4046N/A
4046N/A // if there are authenticate attributes, get the message
4046N/A // digest and compare it with the digest of data
4046N/A if (authenticatedAttributes == null) {
4046N/A dataSigned = data;
4046N/A } else {
4046N/A
4046N/A // first, check content type
4046N/A ObjectIdentifier contentType = (ObjectIdentifier)
4046N/A authenticatedAttributes.getAttributeValue(
4046N/A PKCS9Attribute.CONTENT_TYPE_OID);
4046N/A if (contentType == null ||
4046N/A !contentType.equals(content.contentType))
4046N/A return null; // contentType does not match, bad SignerInfo
4046N/A
4046N/A // now, check message digest
4046N/A byte[] messageDigest = (byte[])
4046N/A authenticatedAttributes.getAttributeValue(
4046N/A PKCS9Attribute.MESSAGE_DIGEST_OID);
4046N/A
4046N/A if (messageDigest == null) // fail if there is no message digest
4046N/A return null;
4046N/A
5212N/A MessageDigest md = MessageDigest.getInstance(
5212N/A convertToStandardName(digestAlgname));
4046N/A byte[] computedMessageDigest = md.digest(data);
4046N/A
4046N/A if (messageDigest.length != computedMessageDigest.length)
4046N/A return null;
4046N/A for (int i = 0; i < messageDigest.length; i++) {
4046N/A if (messageDigest[i] != computedMessageDigest[i])
4046N/A return null;
4046N/A }
4046N/A
4046N/A // message digest attribute matched
4046N/A // digest of original data
4046N/A
4046N/A // the data actually signed is the DER encoding of
4046N/A // the authenticated attributes (tagged with
4046N/A // the "SET OF" tag, not 0xA0).
4046N/A dataSigned = authenticatedAttributes.getDerEncoding();
4046N/A }
4046N/A
4046N/A // put together digest algorithm and encryption algorithm
4046N/A // to form signing algorithm
4046N/A String encryptionAlgname =
4046N/A getDigestEncryptionAlgorithmId().getName();
0N/A
4046N/A // Workaround: sometimes the encryptionAlgname is actually
4046N/A // a signature name
4046N/A String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname);
4046N/A if (tmp != null) encryptionAlgname = tmp;
4046N/A String algname = AlgorithmId.makeSigAlg(
4046N/A digestAlgname, encryptionAlgname);
4046N/A
4046N/A Signature sig = Signature.getInstance(algname);
4046N/A X509Certificate cert = getCertificate(block);
4046N/A
4046N/A if (cert == null) {
4046N/A return null;
4046N/A }
4046N/A if (cert.hasUnsupportedCriticalExtension()) {
4046N/A throw new SignatureException("Certificate has unsupported "
4046N/A + "critical extension(s)");
0N/A }
4046N/A
4046N/A // Make sure that if the usage of the key in the certificate is
4046N/A // restricted, it can be used for digital signatures.
4046N/A // XXX We may want to check for additional extensions in the
4046N/A // future.
4046N/A boolean[] keyUsageBits = cert.getKeyUsage();
4046N/A if (keyUsageBits != null) {
4046N/A KeyUsageExtension keyUsage;
4046N/A try {
4046N/A // We don't care whether or not this extension was marked
4046N/A // critical in the certificate.
4046N/A // We're interested only in its value (i.e., the bits set)
4046N/A // and treat the extension as critical.
4046N/A keyUsage = new KeyUsageExtension(keyUsageBits);
4046N/A } catch (IOException ioe) {
4046N/A throw new SignatureException("Failed to parse keyUsage "
4046N/A + "extension");
4046N/A }
4046N/A
4046N/A boolean digSigAllowed = ((Boolean)keyUsage.get(
4046N/A KeyUsageExtension.DIGITAL_SIGNATURE)).booleanValue();
4046N/A
4046N/A boolean nonRepuAllowed = ((Boolean)keyUsage.get(
4046N/A KeyUsageExtension.NON_REPUDIATION)).booleanValue();
4046N/A
4046N/A if (!digSigAllowed && !nonRepuAllowed) {
4046N/A throw new SignatureException("Key usage restricted: "
4046N/A + "cannot be used for "
4046N/A + "digital signatures");
4046N/A }
4046N/A }
4046N/A
4046N/A PublicKey key = cert.getPublicKey();
4046N/A sig.initVerify(key);
4046N/A
4046N/A sig.update(dataSigned);
4046N/A
4046N/A if (sig.verify(encryptedDigest)) {
4046N/A return this;
4046N/A }
4046N/A
4046N/A } catch (IOException e) {
4046N/A throw new SignatureException("IO error verifying signature:\n" +
4046N/A e.getMessage());
4046N/A
4046N/A } catch (InvalidKeyException e) {
4046N/A throw new SignatureException("InvalidKey: " + e.getMessage());
4046N/A
0N/A }
4046N/A return null;
0N/A }
0N/A
0N/A /* Verify the content of the pkcs7 block. */
0N/A SignerInfo verify(PKCS7 block)
0N/A throws NoSuchAlgorithmException, SignatureException {
0N/A return verify(block, null);
0N/A }
0N/A
0N/A
0N/A public BigInteger getVersion() {
0N/A return version;
0N/A }
0N/A
0N/A public X500Name getIssuerName() {
0N/A return issuerName;
0N/A }
0N/A
0N/A public BigInteger getCertificateSerialNumber() {
0N/A return certificateSerialNumber;
0N/A }
0N/A
0N/A public AlgorithmId getDigestAlgorithmId() {
0N/A return digestAlgorithmId;
0N/A }
0N/A
0N/A public PKCS9Attributes getAuthenticatedAttributes() {
0N/A return authenticatedAttributes;
0N/A }
0N/A
0N/A public AlgorithmId getDigestEncryptionAlgorithmId() {
0N/A return digestEncryptionAlgorithmId;
0N/A }
0N/A
0N/A public byte[] getEncryptedDigest() {
0N/A return encryptedDigest;
0N/A }
0N/A
0N/A public PKCS9Attributes getUnauthenticatedAttributes() {
0N/A return unauthenticatedAttributes;
0N/A }
0N/A
0N/A public String toString() {
0N/A HexDumpEncoder hexDump = new HexDumpEncoder();
0N/A
0N/A String out = "";
0N/A
0N/A out += "Signer Info for (issuer): " + issuerName + "\n";
0N/A out += "\tversion: " + Debug.toHexString(version) + "\n";
0N/A out += "\tcertificateSerialNumber: " +
0N/A Debug.toHexString(certificateSerialNumber) + "\n";
0N/A out += "\tdigestAlgorithmId: " + digestAlgorithmId + "\n";
0N/A if (authenticatedAttributes != null) {
0N/A out += "\tauthenticatedAttributes: " + authenticatedAttributes +
0N/A "\n";
0N/A }
0N/A out += "\tdigestEncryptionAlgorithmId: " + digestEncryptionAlgorithmId +
0N/A "\n";
0N/A
0N/A out += "\tencryptedDigest: " + "\n" +
0N/A hexDump.encodeBuffer(encryptedDigest) + "\n";
0N/A if (unauthenticatedAttributes != null) {
0N/A out += "\tunauthenticatedAttributes: " +
0N/A unauthenticatedAttributes + "\n";
0N/A }
0N/A return out;
0N/A }
0N/A
0N/A}