001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.crypt;
031:
032: import java.security.Provider;
033: import java.security.Security;
034: import java.util.logging.Level;
035:
036: import de.intarsys.pdf.cos.COSArray;
037: import de.intarsys.pdf.cos.COSName;
038: import de.intarsys.pdf.cos.COSObjectKey;
039: import de.intarsys.pdf.cos.COSString;
040: import de.intarsys.pdf.cos.COSTrailer;
041: import de.intarsys.pdf.st.STDocument;
042: import de.intarsys.tools.authenticate.IPasswordProvider;
043: import de.intarsys.tools.logging.LogTools;
044:
045: /**
046: * The standard security handler as specified in the PDF reference.
047: *
048: * <p>
049: * It supports the implementation up to and including revision 4.
050: * </p>
051: */
052: abstract public class StandardSecurityHandler extends
053: AbstractSecurityHandler {
054: public static final COSName DK_O = COSName.constant("O"); //$NON-NLS-1$
055:
056: public static final COSName DK_P = COSName.constant("P"); //$NON-NLS-1$
057:
058: public static final COSName DK_R = COSName.constant("R"); //$NON-NLS-1$
059:
060: public static final COSName DK_U = COSName.constant("U"); //$NON-NLS-1$
061:
062: public static final COSName DK_EncryptMetadata = COSName
063: .constant("EncryptMetadata"); //$NON-NLS-1$
064:
065: /**
066: * The default value for the access permission flags.
067: *
068: * <p>
069: * Everything is allowed, only the reserved flags are zero.
070: * </p>
071: */
072: public static final int DEFAULT_ACCESS_PERMISSIONS = 0xFFFFFFFC;
073:
074: /** The padding sequence as defined in the spec. */
075: protected static byte[] PADDING = new byte[] { (byte) 0x28,
076: (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, (byte) 0x4E,
077: (byte) 0x75, (byte) 0x8A, (byte) 0x41, (byte) 0x64,
078: (byte) 0x00, (byte) 0x4E, (byte) 0x56, (byte) 0xFF,
079: (byte) 0xFA, (byte) 0x01, (byte) 0x08, (byte) 0x2E,
080: (byte) 0x2E, (byte) 0x00, (byte) 0xB6, (byte) 0xD0,
081: (byte) 0x68, (byte) 0x3E, (byte) 0x80, (byte) 0x2F,
082: (byte) 0x0C, (byte) 0xA9, (byte) 0xFE, (byte) 0x64,
083: (byte) 0x53, (byte) 0x69, (byte) 0x7A };
084:
085: /** A dummy padding sequence for the revision 3 variant. */
086: protected static byte[] USER_R3_PADDING = new byte[] { (byte) 0x28,
087: (byte) 0xBF, (byte) 0x4E, (byte) 0x5E, (byte) 0x4E,
088: (byte) 0x75, (byte) 0x8A, (byte) 0x41, (byte) 0x64,
089: (byte) 0x00, (byte) 0x28, (byte) 0xBF, (byte) 0x4E,
090: (byte) 0x5E, (byte) 0x4E, (byte) 0x4E };
091:
092: static {
093: try {
094: Class clazz = Class
095: .forName("org.bouncycastle.jce.provider.BouncyCastleProvider"); //$NON-NLS-1$
096: Security.addProvider((Provider) clazz.newInstance());
097: } catch (Throwable e) {
098: LogTools.getLogger(StandardSecurityHandler.class).log(
099: Level.SEVERE,
100: "error loading BouncyCastleProvider", e); //$NON-NLS-1$
101: }
102: }
103:
104: /** The access permissions that are encoded in the encryption instance. */
105: private IAccessPermissions accessPermissions;
106:
107: private byte[] fileIdentifier;
108:
109: /** The key that was computed for the encryption instance. */
110: private byte[] cryptKey;
111:
112: public StandardSecurityHandler() {
113: super ();
114: }
115:
116: /*
117: * (non-Javadoc)
118: *
119: * @see de.intarsys.pdf.encryption.ISecurityHandler#authenticate()
120: */
121: public void authenticate(IPasswordProvider passwordProvider)
122: throws COSSecurityException {
123: if (!authenticateUser(null) && (passwordProvider != null)) {
124: while (true) {
125: // query password
126: char[] password = passwordProvider.getPassword();
127: if (password == null) {
128: throw new COSSecurityException("password missing"); //$NON-NLS-1$
129: }
130: if (authenticateUser(new String(password).getBytes())) {
131: break;
132: }
133: if (authenticateOwner(new String(password).getBytes())) {
134: setAccessPermissions(AccessPermissionsFull
135: .getActive());
136: break;
137: }
138: }
139: }
140: }
141:
142: abstract protected boolean authenticateOwner(byte[] owner)
143: throws COSSecurityException;
144:
145: abstract protected boolean authenticateUser(byte[] user)
146: throws COSSecurityException;
147:
148: abstract protected byte[] createCryptKey(byte[] password)
149: throws COSSecurityException;
150:
151: abstract protected byte[] createOwnerPassword(byte[] owner,
152: byte[] user) throws COSSecurityException;
153:
154: abstract protected byte[] createUserPassword(byte[] user)
155: throws COSSecurityException;
156:
157: /*
158: * (non-Javadoc)
159: *
160: * @see de.intarsys.pdf.encryption.ISecurityHandler#getAccessPermissions()
161: */
162: public IAccessPermissions getAccessPermissions() {
163: if (accessPermissions == null) {
164: accessPermissions = createAccessPermissions();
165: }
166: return accessPermissions;
167: }
168:
169: abstract protected IAccessPermissions createAccessPermissions();
170:
171: protected int getAccessPermissionValue() {
172: return getEncryption().getFieldInt(
173: StandardSecurityHandler.DK_P,
174: DEFAULT_ACCESS_PERMISSIONS);
175: }
176:
177: protected byte[] getFileIdentifier() {
178: return fileIdentifier;
179: }
180:
181: /*
182: * (non-Javadoc)
183: *
184: * @see de.intarsys.pdf.encryption.ISecurityHandler#getCryptKey()
185: */
186: public byte[] getCryptKey() {
187: return cryptKey;
188: }
189:
190: protected byte[] getO() {
191: COSString o = getEncryption().cosGetField(DK_O).asString();
192: if (o != null) {
193: return o.byteValue();
194: }
195: return null;
196: }
197:
198: protected byte[] getPBytes() {
199: int pint = getAccessPermissionValue();
200: byte[] result = new byte[4];
201: result[0] = (byte) (pint & 0xff);
202: pint = pint >> 8;
203: result[1] = (byte) (pint & 0xff);
204: pint = pint >> 8;
205: result[2] = (byte) (pint & 0xff);
206: pint = pint >> 8;
207: result[3] = (byte) (pint & 0xff);
208: return result;
209: }
210:
211: protected int getRevision() {
212: return getEncryption().getFieldInt(DK_R, -1);
213: }
214:
215: protected byte[] getU() {
216: COSString u = getEncryption().cosGetField(DK_U).asString();
217: if (u == null) {
218: return null;
219: }
220: return u.byteValue();
221: }
222:
223: /*
224: * (non-Javadoc)
225: *
226: * @see de.intarsys.pdf.encryption.ISecurityHandler#init(de.intarsys.pdf.cos.COSEncryption)
227: */
228: public void init(STDocument document, COSEncryption encryption)
229: throws COSSecurityException {
230: super .init(document, encryption);
231: COSArray id = document.cosGetTrailer().get(COSTrailer.DK_ID)
232: .asArray();
233: if ((id != null) && (id.size() > 0)) {
234: setFileIdentifier(((COSString) id.get(0)).byteValue());
235: }
236: }
237:
238: protected boolean isEncryptMetadata() {
239: return getEncryption()
240: .getFieldBoolean(DK_EncryptMetadata, true);
241: }
242:
243: protected byte[] prepareBytes(byte[] bytes) {
244: byte[] padded = new byte[32];
245: if (bytes == null) {
246: System.arraycopy(PADDING, 0, padded, 0, 32);
247: } else {
248: if (bytes.length > 32) {
249: System.arraycopy(bytes, 0, padded, 0, 32);
250: } else {
251: System.arraycopy(bytes, 0, padded, 0, bytes.length);
252: System.arraycopy(PADDING, 0, padded, bytes.length,
253: 32 - bytes.length);
254: }
255: }
256: return padded;
257: }
258:
259: protected void setAccessPermissions(
260: IAccessPermissions accessPermissions) {
261: this .accessPermissions = accessPermissions;
262: }
263:
264: protected void setAccessPermissionValue(int newValue) {
265: if (newValue != DEFAULT_ACCESS_PERMISSIONS) {
266: getEncryption().setFieldInt(StandardSecurityHandler.DK_P,
267: newValue);
268: } else {
269: getEncryption()
270: .cosRemoveField(StandardSecurityHandler.DK_P);
271: }
272: }
273:
274: protected void setFileIdentifier(byte[] fileIdentifier) {
275: this .fileIdentifier = fileIdentifier;
276: }
277:
278: protected void setCryptKey(byte[] key) {
279: this .cryptKey = key;
280: }
281:
282: public byte[] decrypt(COSObjectKey key, byte[] bytes)
283: throws COSSecurityException {
284: throw new COSSecurityException(
285: "pluggable encryption not supported"); //$NON-NLS-1$
286: }
287:
288: public byte[] encrypt(COSObjectKey key, byte[] bytes)
289: throws COSSecurityException {
290: throw new COSSecurityException(
291: "pluggable encryption not supported"); //$NON-NLS-1$
292: }
293:
294: }
|