File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\jenkins\security\DefaultConfidentialStore.java.html

1 package jenkins.security;
2
3 import hudson.FilePath;
4 import hudson.Util;
5 import hudson.util.IOException2;
6 import hudson.util.IOUtils;
7 import hudson.util.Secret;
8 import hudson.util.TextFile;
9 import jenkins.model.Jenkins;
10
11 import javax.crypto.Cipher;
12 import javax.crypto.CipherInputStream;
13 import javax.crypto.CipherOutputStream;
14 import javax.crypto.SecretKey;
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.security.GeneralSecurityException;
20 import java.security.SecureRandom;
21
22 /**
23 * Default portable implementation of {@link ConfidentialStore} that uses
24 * a directory inside $JENKINS_HOME.
25 *
26 * The master key is also stored in this same directory.
27 *
28 * @author Kohsuke Kawaguchi
29 */
30 // @MetaInfServices --- not annotated because this is the fallback implementation
31 public class DefaultConfidentialStore extends ConfidentialStore {
32 private final SecureRandom sr = new SecureRandom();
33
34 /**
35 * Directory that stores individual keys.
36 */
37 private final File rootDir;
38
39 /**
40 * The master key.
41 *
42 * The sole purpose of the master key is to encrypt individual keys on the disk.
43 * Because leaking this master key compromises all the individual keys, we must not let
44 * this master key used for any other purpose, hence the protected access.
45 */
46 private final SecretKey masterKey;
47
48 public DefaultConfidentialStore() throws IOException, InterruptedException {
49 this(new File(Jenkins.getInstance().getRootDir(),"secrets"));
50 }
51
52 public DefaultConfidentialStore(File rootDir) throws IOException, InterruptedException {
53 this.rootDir = rootDir;
54 if (rootDir.mkdirs()) {
55 // protect this directory. but don't change the permission of the existing directory
56 // in case the administrator changed this.
57 new FilePath(rootDir).chmod(0700);
58 }
59
60 TextFile masterSecret = new TextFile(new File(rootDir,"master.key"));
61 if (!masterSecret.exists()) {
62 // we are only going to use small number of bits (since export control limits AES key length)
63 // but let's generate a long enough key anyway
64 masterSecret.write(Util.toHexString(randomBytes(128)));
65 }
66 this.masterKey = Util.toAes128Key(masterSecret.readTrim());
67 }
68
69 /**
70 * Persists the payload of {@link ConfidentialKey} to the disk.
71 */
72 @Override
73 protected void store(ConfidentialKey key, byte[] payload) throws IOException {
74 CipherOutputStream cos=null;
75 FileOutputStream fos=null;
76 try {
77 Cipher sym = Secret.getCipher("AES");
78 sym.init(Cipher.ENCRYPT_MODE, masterKey);
79 cos = new CipherOutputStream(fos=new FileOutputStream(getFileFor(key)), sym);
80 cos.write(payload);
81 cos.write(MAGIC);
82 } catch (GeneralSecurityException e) {
83 throw new IOException2("Failed to persist the key: "+key.getId(),e);
84 } finally {
85 IOUtils.closeQuietly(cos);
86 IOUtils.closeQuietly(fos);
87 }
88 }
89
90 /**
91 * Reverse operation of {@link #store(ConfidentialKey, byte[])}
92 *
93 * @return
94 * null the data has not been previously persisted.
95 */
96 @Override
97 protected byte[] load(ConfidentialKey key) throws IOException {
98 CipherInputStream cis=null;
99 FileInputStream fis=null;
100 try {
101 File f = getFileFor(key);
102 if (!f.exists()) return null;
103
104 Cipher sym = Secret.getCipher("AES");
105 sym.init(Cipher.DECRYPT_MODE, masterKey);
106 cis = new CipherInputStream(fis=new FileInputStream(f), sym);
107 byte[] bytes = IOUtils.toByteArray(cis);
108 return verifyMagic(bytes);
109 } catch (GeneralSecurityException e) {
110 throw new IOException2("Failed to persist the key: "+key.getId(),e);
111 } finally {
112 IOUtils.closeQuietly(cis);
113 IOUtils.closeQuietly(fis);
114 }
115 }
116
117 /**
118 * Verifies that the given byte[] has the MAGIC trailer, to verify the integrity of the decryption process.
119 */
120 private byte[] verifyMagic(byte[] payload) {
121 int payloadLen = payload.length-MAGIC.length;
122 if (payloadLen<0) return null; // obviously broken
123
124 for (int i=0; i<MAGIC.length; i++) {
125 if (payload[payloadLen+i]!=MAGIC[i])
126 return null; // broken
127 }
128 byte[] truncated = new byte[payloadLen];
129 System.arraycopy(payload,0,truncated,0,truncated.length);
130 return truncated;
131 }
132
133 private File getFileFor(ConfidentialKey key) {
134 return new File(rootDir, key.getId());
135 }
136
137 public byte[] randomBytes(int size) {
138 byte[] random = new byte[size];
139 sr.nextBytes(random);
140 return random;
141 }
142
143 private static final byte[] MAGIC = "::::MAGIC::::".getBytes();
144 }
145