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