/* * Copyright (c) 2015, 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. * * 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. */ /* * @test * @bug 8048357 * @summary test PKCS7 data signing, encoding and verification * @modules java.base/sun.security.pkcs * java.base/sun.security.util * java.base/sun.security.x509 * @run main SignerOrder */ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.X509Certificate; import java.util.Date; import sun.security.util.HexDumpEncoder; import sun.security.pkcs.ContentInfo; import sun.security.pkcs.PKCS7; import sun.security.pkcs.SignerInfo; import sun.security.util.DerOutputStream; import sun.security.x509.AlgorithmId; import sun.security.x509.CertificateAlgorithmId; import sun.security.x509.CertificateSerialNumber; import sun.security.x509.CertificateValidity; import sun.security.x509.CertificateVersion; import sun.security.x509.CertificateX509Key; import sun.security.x509.X500Name; import sun.security.x509.X509CertImpl; import sun.security.x509.X509CertInfo; import sun.security.x509.X509Key; public class SignerOrder { static final HexDumpEncoder hexDump = new HexDumpEncoder(); //signer infos static final byte[] data1 = "12345".getBytes(); static final byte[] data2 = "abcde".getBytes(); public static void main(String[] argv) throws Exception { SignerInfo[] signerInfos = new SignerInfo[9]; SimpleSigner signer1 = new SimpleSigner(null, null, null, null); signerInfos[8] = signer1.genSignerInfo(data1); signerInfos[7] = signer1.genSignerInfo(new byte[]{}); signerInfos[6] = signer1.genSignerInfo(data2); SimpleSigner signer2 = new SimpleSigner(null, null, null, null); signerInfos[5] = signer2.genSignerInfo(data1); signerInfos[4] = signer2.genSignerInfo(new byte[]{}); signerInfos[3] = signer2.genSignerInfo(data2); SimpleSigner signer3 = new SimpleSigner(null, null, null, null); signerInfos[2] = signer3.genSignerInfo(data1); signerInfos[1] = signer3.genSignerInfo(new byte[]{}); signerInfos[0] = signer3.genSignerInfo(data2); ContentInfo contentInfo = new ContentInfo(data1); AlgorithmId[] algIds = {new AlgorithmId(AlgorithmId.SHA256_oid)}; X509Certificate[] certs = {signer3.getCert(), signer2.getCert(), signer1.getCert()}; PKCS7 pkcs71 = new PKCS7(algIds, contentInfo, certs, signerInfos); System.out.println("SignerInfos in original."); printSignerInfos(pkcs71.getSignerInfos()); DerOutputStream out = new DerOutputStream(); pkcs71.encodeSignedData(out); PKCS7 pkcs72 = new PKCS7(out.toByteArray()); System.out.println("\nSignerInfos read back in:"); printSignerInfos(pkcs72.getSignerInfos()); System.out.println("Verified signers of original:"); SignerInfo[] verifs1 = pkcs71.verify(); System.out.println("Verified signers of after read-in:"); SignerInfo[] verifs2 = pkcs72.verify(); if (verifs1.length != verifs2.length) { throw new RuntimeException("Length or Original vs read-in " + "should be same"); } } static void printSignerInfos(SignerInfo signerInfo) throws IOException { ByteArrayOutputStream strm = new ByteArrayOutputStream(); signerInfo.derEncode(strm); System.out.println("SignerInfo, length: " + strm.toByteArray().length); System.out.println(hexDump.encode(strm.toByteArray())); System.out.println("\n"); strm.reset(); } static void printSignerInfos(SignerInfo[] signerInfos) throws IOException { ByteArrayOutputStream strm = new ByteArrayOutputStream(); for (int i = 0; i < signerInfos.length; i++) { signerInfos[i].derEncode(strm); System.out.println("SignerInfo[" + i + "], length: " + strm.toByteArray().length); System.out.println(hexDump.encode(strm.toByteArray())); System.out.println("\n"); strm.reset(); } } } /** * A simple extension of sun.security.x509.X500Signer that adds a no-fuss * signing algorithm. */ class SimpleSigner { private final Signature sig; private final X500Name agent; private final AlgorithmId digestAlgId; private final AlgorithmId encryptionAlgId; private final AlgorithmId algId; // signature algid; //combines digest + encryption private final X509Key publicKey; private final PrivateKey privateKey; private final X509Certificate cert; public SimpleSigner(String digestAlg, String encryptionAlg, KeyPair keyPair, X500Name agent) throws Exception { if (agent == null) { agent = new X500Name("cn=test"); } if (digestAlg == null) { digestAlg = "SHA"; } if (encryptionAlg == null) { encryptionAlg = "DSA"; } if (keyPair == null) { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(encryptionAlg); keyGen.initialize(1024); keyPair = keyGen.generateKeyPair(); } publicKey = (X509Key) keyPair.getPublic(); privateKey = keyPair.getPrivate(); if ("DSA".equals(encryptionAlg)) { this.sig = Signature.getInstance(encryptionAlg); } else { // RSA this.sig = Signature.getInstance(digestAlg + "/" + encryptionAlg); } this.sig.initSign(privateKey); this.agent = agent; this.digestAlgId = AlgorithmId.get(digestAlg); this.encryptionAlgId = AlgorithmId.get(encryptionAlg); this.algId = AlgorithmId.get(this.sig.getAlgorithm()); this.cert = getSelfCert(); } /** * Take the data and sign it. * * @param buf buffer holding the next chunk of the data to be signed * @param offset starting point of to-be-signed data * @param len how many bytes of data are to be signed * @return the signature for the input data. * @exception SignatureException on errors. */ public byte[] simpleSign(byte[] buf, int offset, int len) throws SignatureException { sig.update(buf, offset, len); return sig.sign(); } /** * Returns the digest algorithm used to sign. */ public AlgorithmId getDigestAlgId() { return digestAlgId; } /** * Returns the encryption algorithm used to sign. */ public AlgorithmId getEncryptionAlgId() { return encryptionAlgId; } /** * Returns the name of the signing agent. */ public X500Name getSigner() { return agent; } public X509Certificate getCert() { return cert; } private X509Certificate getSelfCert() throws Exception { long validity = 1000; X509CertImpl certLocal; Date firstDate, lastDate; firstDate = new Date(); lastDate = new Date(); lastDate.setTime(lastDate.getTime() + validity + 1000); CertificateValidity interval = new CertificateValidity(firstDate, lastDate); X509CertInfo info = new X509CertInfo(); // Add all mandatory attributes info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V1)); info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber( (int) (firstDate.getTime() / 1000))); info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algId)); info.set(X509CertInfo.SUBJECT, agent); info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey)); info.set(X509CertInfo.VALIDITY, interval); info.set(X509CertInfo.ISSUER, agent); certLocal = new X509CertImpl(info); certLocal.sign(privateKey, algId.getName()); return certLocal; } public SignerInfo genSignerInfo(byte[] data) throws SignatureException { return new SignerInfo((X500Name) cert.getIssuerDN(), new BigInteger("" + cert.getSerialNumber()), getDigestAlgId(), algId, simpleSign(data, 0, data.length)); } }