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.MessageDigest;
033:
034: import javax.crypto.Cipher;
035: import javax.crypto.SecretKey;
036: import javax.crypto.spec.SecretKeySpec;
037:
038: import de.intarsys.pdf.cos.COSName;
039:
040: /**
041: * The {@link ISecurityHandler} implementing /R 4 of the PDF spec.
042: *
043: */
044: public class StandardSecurityHandlerR4 extends StandardSecurityHandler {
045: public static final String KEY_ALGORITHM = "RC4"; //$NON-NLS-1$
046:
047: public static final String CIPHER_ALGORITHM = "RC4"; //$NON-NLS-1$
048:
049: public static final String DIGEST_ALGORITHM = "MD5"; //$NON-NLS-1$
050:
051: public static final COSName DK_AuthEvent = COSName
052: .constant("AuthEvent"); //$NON-NLS-1$
053:
054: public static final COSName DK_Length = COSName.constant("Length"); //$NON-NLS-1$
055:
056: /** A byte sequence to be include in the hash under certain circumstances. */
057: private static byte[] HIGH_BYTES = new byte[] { (byte) 0xFF,
058: (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
059:
060: protected byte[] createUserPassword(byte[] user)
061: throws COSSecurityException {
062: try {
063: byte[] encryptionKey = createCryptKey(user);
064: setCryptKey(encryptionKey);
065: MessageDigest md = MessageDigest
066: .getInstance(DIGEST_ALGORITHM);
067: md.update(PADDING);
068: byte[] fd = getFileIdentifier();
069: if (fd != null) {
070: md.update(fd);
071: }
072: byte[] hash = md.digest();
073: SecretKey skeySpec = new SecretKeySpec(encryptionKey,
074: KEY_ALGORITHM);
075: Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
076: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
077: byte[] encrypted = cipher.doFinal(hash);
078: byte[] tempEncKey = new byte[encryptionKey.length];
079: for (int i = 1; i <= 19; i++) {
080: for (int index = 0; index < encryptionKey.length; index++) {
081: tempEncKey[index] = (byte) (encryptionKey[index] ^ i);
082: }
083: skeySpec = new SecretKeySpec(tempEncKey, KEY_ALGORITHM);
084: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
085: encrypted = cipher.doFinal(encrypted);
086: }
087: byte[] result = new byte[32];
088: System.arraycopy(encrypted, 0, result, 0, 16);
089: System.arraycopy(USER_R3_PADDING, 0, result, 16, 16);
090: return result;
091: } catch (Exception e) {
092: throw new COSSecurityException(e);
093: }
094: }
095:
096: protected byte[] createCryptKey(byte[] password)
097: throws COSSecurityException {
098: try {
099: MessageDigest md = MessageDigest
100: .getInstance(DIGEST_ALGORITHM);
101: byte[] prepared = prepareBytes(password);
102: md.update(prepared);
103: md.update(getO());
104: md.update(getPBytes());
105: byte[] fd = getFileIdentifier();
106: if (fd != null) {
107: md.update(fd);
108: }
109: if (!isEncryptMetadata()) {
110: md.update(HIGH_BYTES);
111: }
112: byte[] key = md.digest();
113: for (int i = 0; i < 50; i++) {
114: md.update(key);
115: key = md.digest();
116: }
117: int length = getEncryption().getLength() / 8;
118: byte[] result = new byte[length];
119: System.arraycopy(key, 0, result, 0, length);
120: return result;
121: } catch (Exception e) {
122: throw new COSSecurityException(e);
123: }
124: }
125:
126: protected byte[] createOwnerPassword(byte[] owner, byte[] user)
127: throws COSSecurityException {
128: try {
129: byte[] preparedOwner;
130: if (owner == null) {
131: preparedOwner = prepareBytes(user);
132: } else {
133: preparedOwner = prepareBytes(owner);
134: }
135: MessageDigest md = MessageDigest
136: .getInstance(DIGEST_ALGORITHM);
137: md.update(preparedOwner);
138: byte[] key = md.digest();
139: for (int i = 0; i < 50; i++) {
140: md.update(key);
141: key = md.digest();
142: }
143: int length = getEncryption().getLength();
144: byte[] encryptionKey = new byte[length];
145: System.arraycopy(key, 0, encryptionKey, 0, length);
146: //
147: SecretKey skeySpec = new SecretKeySpec(encryptionKey,
148: KEY_ALGORITHM);
149: byte[] preparedUser = prepareBytes(user);
150: Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
151: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
152: byte[] encrypted = cipher.doFinal(preparedUser);
153: for (int i = 1; i <= 19; i++) {
154: for (int index = 0; index <= encryptionKey.length; index++) {
155: encryptionKey[index] = (byte) (encryptionKey[index] ^ i);
156: }
157: skeySpec = new SecretKeySpec(encrypted, KEY_ALGORITHM);
158: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
159: encrypted = cipher.doFinal(encrypted);
160: }
161: return encrypted;
162: } catch (Exception e) {
163: throw new COSSecurityException(e);
164: }
165: }
166:
167: protected boolean authenticateUser(byte[] user)
168: throws COSSecurityException {
169: byte[] entryU = getU();
170: byte[] tempU = createUserPassword(user);
171: for (int i = 0; i < 16; i++) {
172: if (entryU[i] != tempU[i]) {
173: return false;
174: }
175: }
176: return true;
177: }
178:
179: protected boolean authenticateOwner(byte[] owner)
180: throws COSSecurityException {
181: try {
182: byte[] preparedOwner = prepareBytes(owner);
183: MessageDigest md = MessageDigest
184: .getInstance(DIGEST_ALGORITHM);
185: md.update(preparedOwner);
186: byte[] key = md.digest();
187: for (int i = 0; i < 50; i++) {
188: md.update(key);
189: key = md.digest();
190: }
191: int length = getEncryption().getLength() / 8;
192: byte[] encryptionKey = new byte[length];
193: System.arraycopy(key, 0, encryptionKey, 0, length);
194: //
195: Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
196: if (cipher == null) {
197: throw new COSSecurityException("RC4 cipher not found"); //$NON-NLS-1$
198: }
199: SecretKey skeySpec;
200: byte[] encrypted = getO();
201: byte[] tempEncKey = new byte[encryptionKey.length];
202: for (int i = 19; i >= 0; i--) {
203: for (int index = 0; index < encryptionKey.length; index++) {
204: tempEncKey[index] = (byte) (encryptionKey[index] ^ i);
205: }
206: skeySpec = new SecretKeySpec(tempEncKey, KEY_ALGORITHM);
207: cipher.init(Cipher.DECRYPT_MODE, skeySpec);
208: encrypted = cipher.doFinal(encrypted);
209: }
210: return authenticateUser(encrypted);
211: } catch (Exception e) {
212: throw new COSSecurityException(e);
213: }
214: }
215:
216: protected IAccessPermissions createAccessPermissions() {
217: return new AccessPermissionsR3(this);
218: }
219: }
|