1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.security;
19
20import java.io.ByteArrayInputStream;
21import java.io.ByteArrayOutputStream;
22import java.io.IOException;
23import java.io.ObjectInputStream;
24import java.io.ObjectOutputStream;
25import java.io.Serializable;
26
27/**
28 * A {@code SignedObject} instance acts as a container for another object. The
29 * {@code SignedObject} contains the target in serialized form along with a
30 * digital signature of the serialized data.
31 */
32public final class SignedObject implements Serializable {
33
34    private static final long serialVersionUID = 720502720485447167L;
35
36    private byte[] content;
37
38    private byte[] signature;
39
40    private String thealgorithm;
41
42    private void readObject(ObjectInputStream s) throws IOException,
43            ClassNotFoundException {
44
45        s.defaultReadObject();
46        byte[] tmp = new byte[content.length];
47        System.arraycopy(content, 0, tmp, 0, content.length);
48        content = tmp;
49        tmp = new byte[signature.length];
50        System.arraycopy(signature, 0, tmp, 0, signature.length);
51        signature = tmp;
52    }
53
54    /**
55     * Constructs a new instance of {@code SignedObject} with the target object,
56     * the private key and the engine to compute the signature. The given
57     * {@code object} is signed with the specified key and engine.
58     *
59     * @param object
60     *            the object to bes signed.
61     * @param signingKey
62     *            the private key, used to sign the {@code object}.
63     * @param signingEngine
64     *            the engine that performs the signature generation.
65     * @throws IOException
66     *             if a serialization error occurs.
67     * @throws InvalidKeyException
68     *             if the private key is not valid.
69     * @throws SignatureException
70     *             if signature generation failed.
71     */
72    public SignedObject(Serializable object, PrivateKey signingKey,
73            Signature signingEngine) throws IOException, InvalidKeyException,
74            SignatureException {
75
76        ByteArrayOutputStream baos = new ByteArrayOutputStream();
77        ObjectOutputStream oos = new ObjectOutputStream(baos);
78        try {
79            // Serialize
80            oos.writeObject(object);
81            oos.flush();
82        } finally {
83            oos.close();
84        }
85        content = baos.toByteArray();
86        signingEngine.initSign(signingKey);
87        thealgorithm = signingEngine.getAlgorithm();
88        signingEngine.update(content);
89        signature = signingEngine.sign();
90    }
91
92    /**
93     * Returns the encapsulated object. Each time this method is invoked, the
94     * encapsulated object is deserialized before it is returned.
95     *
96     * @return the encapsulated object.
97     * @throws IOException
98     *             if deserialization failed.
99     * @throws ClassNotFoundException
100     *             if the class of the encapsulated object can not be found.
101     */
102    public Object getObject() throws IOException, ClassNotFoundException {
103        // deserialize our object
104        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
105                content));
106        try {
107            return ois.readObject();
108        } finally {
109            ois.close();
110        }
111    }
112
113    /**
114     * Returns the signature data of the encapsulated serialized object.
115     *
116     * @return the signature data of the encapsulated serialized object.
117     */
118    public byte[] getSignature() {
119        byte[] sig = new byte[signature.length];
120        System.arraycopy(signature, 0, sig, 0, signature.length);
121        return sig;
122    }
123
124    /**
125     * Returns the name of the algorithm of this {@code SignedObject}.
126     *
127     * @return the name of the algorithm of this {@code SignedObject}.
128     */
129    public String getAlgorithm() {
130        return thealgorithm;
131    }
132
133    /**
134     * Indicates whether the contained signature for the encapsulated object is
135     * valid.
136     *
137     * @param verificationKey
138     *            the public key to verify the signature.
139     * @param verificationEngine
140     *            the signature engine.
141     * @return {@code true} if the contained signature for the encapsulated
142     *         object is valid, {@code false} otherwise.
143     * @throws InvalidKeyException
144     *             if the public key is invalid.
145     * @throws SignatureException
146     *             if signature verification failed.
147     */
148    public boolean verify(PublicKey verificationKey,
149            Signature verificationEngine) throws InvalidKeyException,
150            SignatureException {
151
152        verificationEngine.initVerify(verificationKey);
153        verificationEngine.update(content);
154        return verificationEngine.verify(signature);
155    }
156
157}
158