1 /*
   2  * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8048357
  27  * @summary test PKCS7 data signing, encoding and verification
  28  * @run main SignerOrder
  29  */
  30 import java.io.ByteArrayOutputStream;
  31 import java.io.IOException;
  32 import java.math.BigInteger;
  33 import java.security.KeyPair;
  34 import java.security.KeyPairGenerator;
  35 import java.security.PrivateKey;
  36 import java.security.Signature;
  37 import java.security.SignatureException;
  38 import java.security.cert.X509Certificate;
  39 import java.util.Date;
  40 import sun.misc.HexDumpEncoder;
  41 import sun.security.pkcs.ContentInfo;
  42 import sun.security.pkcs.PKCS7;
  43 import sun.security.pkcs.SignerInfo;
  44 import sun.security.util.DerOutputStream;
  45 import sun.security.x509.AlgorithmId;
  46 import sun.security.x509.CertificateAlgorithmId;
  47 import sun.security.x509.CertificateSerialNumber;
  48 import sun.security.x509.CertificateValidity;
  49 import sun.security.x509.CertificateVersion;
  50 import sun.security.x509.CertificateX509Key;
  51 import sun.security.x509.X500Name;
  52 import sun.security.x509.X509CertImpl;
  53 import sun.security.x509.X509CertInfo;
  54 import sun.security.x509.X509Key;
  55 
  56 public class SignerOrder {
  57 
  58     static final HexDumpEncoder hexDump = new HexDumpEncoder();
  59 
  60     //signer infos
  61     static final byte[] data1 = "12345".getBytes();
  62     static final byte[] data2 = "abcde".getBytes();
  63 
  64     public static void main(String[] argv) throws Exception {
  65 
  66         SignerInfo[] signerInfos = new SignerInfo[9];
  67         SimpleSigner signer1 = new SimpleSigner(null, null, null, null);
  68         signerInfos[8] = signer1.genSignerInfo(data1);
  69         signerInfos[7] = signer1.genSignerInfo(new byte[]{});
  70         signerInfos[6] = signer1.genSignerInfo(data2);
  71 
  72         SimpleSigner signer2 = new SimpleSigner(null, null, null, null);
  73         signerInfos[5] = signer2.genSignerInfo(data1);
  74         signerInfos[4] = signer2.genSignerInfo(new byte[]{});
  75         signerInfos[3] = signer2.genSignerInfo(data2);
  76 
  77         SimpleSigner signer3 = new SimpleSigner(null, null, null, null);
  78         signerInfos[2] = signer3.genSignerInfo(data1);
  79         signerInfos[1] = signer3.genSignerInfo(new byte[]{});
  80         signerInfos[0] = signer3.genSignerInfo(data2);
  81 
  82         ContentInfo contentInfo = new ContentInfo(data1);
  83 
  84         AlgorithmId[] algIds = {new AlgorithmId(AlgorithmId.SHA256_oid)};
  85 
  86         X509Certificate[] certs = {signer3.getCert(), signer2.getCert(),
  87             signer1.getCert()};
  88 
  89         PKCS7 pkcs71 = new PKCS7(algIds, contentInfo,
  90                 certs,
  91                 signerInfos);
  92 
  93         System.out.println("SignerInfos in original.");
  94         printSignerInfos(pkcs71.getSignerInfos());
  95 
  96         DerOutputStream out = new DerOutputStream();
  97         pkcs71.encodeSignedData(out);
  98 
  99         PKCS7 pkcs72 = new PKCS7(out.toByteArray());
 100         System.out.println("\nSignerInfos read back in:");
 101         printSignerInfos(pkcs72.getSignerInfos());
 102 
 103         System.out.println("Verified signers of original:");
 104         SignerInfo[] verifs1 = pkcs71.verify();
 105 
 106         System.out.println("Verified signers of after read-in:");
 107         SignerInfo[] verifs2 = pkcs72.verify();
 108 
 109         if (verifs1.length != verifs2.length) {
 110             throw new RuntimeException("Length or Original vs read-in "
 111                     + "should be same");
 112         }
 113     }
 114 
 115     static void printSignerInfos(SignerInfo signerInfo) throws IOException {
 116         ByteArrayOutputStream strm = new ByteArrayOutputStream();
 117         signerInfo.derEncode(strm);
 118         System.out.println("SignerInfo, length: "
 119                 + strm.toByteArray().length);
 120         System.out.println(hexDump.encode(strm.toByteArray()));
 121         System.out.println("\n");
 122         strm.reset();
 123     }
 124 
 125     static void printSignerInfos(SignerInfo[] signerInfos) throws IOException {
 126         ByteArrayOutputStream strm = new ByteArrayOutputStream();
 127         for (int i = 0; i < signerInfos.length; i++) {
 128             signerInfos[i].derEncode(strm);
 129             System.out.println("SignerInfo[" + i + "], length: "
 130                     + strm.toByteArray().length);
 131             System.out.println(hexDump.encode(strm.toByteArray()));
 132             System.out.println("\n");
 133             strm.reset();
 134         }
 135     }
 136 
 137 }
 138 
 139 /**
 140  * A simple extension of sun.security.x509.X500Signer that adds a no-fuss
 141  * signing algorithm.
 142  */
 143 class SimpleSigner {
 144 
 145     private final Signature sig;
 146     private final X500Name agent;
 147     private final AlgorithmId digestAlgId;
 148     private final AlgorithmId encryptionAlgId;
 149     private final AlgorithmId algId; // signature algid;
 150                                      //combines digest + encryption
 151     private final X509Key publicKey;
 152     private final PrivateKey privateKey;
 153     private final X509Certificate cert;
 154 
 155     public SimpleSigner(String digestAlg,
 156             String encryptionAlg,
 157             KeyPair keyPair,
 158             X500Name agent) throws Exception {
 159 
 160         if (agent == null) {
 161             agent = new X500Name("cn=test");
 162         }
 163         if (digestAlg == null) {
 164             digestAlg = "SHA";
 165         }
 166         if (encryptionAlg == null) {
 167             encryptionAlg = "DSA";
 168         }
 169         if (keyPair == null) {
 170             KeyPairGenerator keyGen =
 171                     KeyPairGenerator.getInstance(encryptionAlg);
 172             keyGen.initialize(1024);
 173             keyPair = keyGen.generateKeyPair();
 174         }
 175         publicKey = (X509Key) keyPair.getPublic();
 176         privateKey = keyPair.getPrivate();
 177 
 178         if ("DSA".equals(encryptionAlg)) {
 179             this.sig = Signature.getInstance(encryptionAlg);
 180         } else { // RSA
 181             this.sig = Signature.getInstance(digestAlg + "/" + encryptionAlg);
 182         }
 183         this.sig.initSign(privateKey);
 184 
 185         this.agent = agent;
 186         this.digestAlgId = AlgorithmId.get(digestAlg);
 187         this.encryptionAlgId = AlgorithmId.get(encryptionAlg);
 188         this.algId = AlgorithmId.get(this.sig.getAlgorithm());
 189 
 190         this.cert = getSelfCert();
 191     }
 192 
 193     /**
 194      * Take the data and sign it.
 195      *
 196      * @param buf buffer holding the next chunk of the data to be signed
 197      * @param offset starting point of to-be-signed data
 198      * @param len how many bytes of data are to be signed
 199      * @return the signature for the input data.
 200      * @exception SignatureException on errors.
 201      */
 202     public byte[] simpleSign(byte[] buf, int offset, int len)
 203             throws SignatureException {
 204         sig.update(buf, offset, len);
 205         return sig.sign();
 206     }
 207 
 208     /**
 209      * Returns the digest algorithm used to sign.
 210      */
 211     public AlgorithmId getDigestAlgId() {
 212         return digestAlgId;
 213     }
 214 
 215     /**
 216      * Returns the encryption algorithm used to sign.
 217      */
 218     public AlgorithmId getEncryptionAlgId() {
 219         return encryptionAlgId;
 220     }
 221 
 222     /**
 223      * Returns the name of the signing agent.
 224      */
 225     public X500Name getSigner() {
 226         return agent;
 227     }
 228 
 229     public X509Certificate getCert() {
 230         return cert;
 231     }
 232 
 233     private X509Certificate getSelfCert() throws Exception {
 234         long validity = 1000;
 235         X509CertImpl certLocal;
 236         Date firstDate, lastDate;
 237 
 238         firstDate = new Date();
 239         lastDate = new Date();
 240         lastDate.setTime(lastDate.getTime() + validity + 1000);
 241 
 242         CertificateValidity interval = new CertificateValidity(firstDate,
 243                 lastDate);
 244 
 245         X509CertInfo info = new X509CertInfo();
 246         // Add all mandatory attributes
 247         info.set(X509CertInfo.VERSION,
 248                 new CertificateVersion(CertificateVersion.V1));
 249         info.set(X509CertInfo.SERIAL_NUMBER,
 250                 new CertificateSerialNumber(
 251                         (int) (firstDate.getTime() / 1000)));
 252         info.set(X509CertInfo.ALGORITHM_ID,
 253                 new CertificateAlgorithmId(algId));
 254         info.set(X509CertInfo.SUBJECT, agent);
 255         info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
 256         info.set(X509CertInfo.VALIDITY, interval);
 257         info.set(X509CertInfo.ISSUER, agent);
 258 
 259         certLocal = new X509CertImpl(info);
 260         certLocal.sign(privateKey, algId.getName());
 261 
 262         return certLocal;
 263     }
 264 
 265     public SignerInfo genSignerInfo(byte[] data) throws SignatureException {
 266         return new SignerInfo((X500Name) cert.getIssuerDN(),
 267                 new BigInteger("" + cert.getSerialNumber()),
 268                 getDigestAlgId(), algId,
 269                 simpleSign(data, 0, data.length));
 270     }
 271 }