BrightSide Workbench Full Report + Source Code
ServerKeys.java
Go to the documentation of this file.
1 /*
2  * TurrĂ³ i Cutiller Foundation. License notice.
3  * Copyright (C) 2022 Lluis TurrĂ³ Cutiller <http://www.turro.org/>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 package org.turro.push.security;
20 
21 import java.io.File;
22 import java.io.FileReader;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.math.BigInteger;
26 import java.security.InvalidAlgorithmParameterException;
27 import java.security.KeyFactory;
28 import java.security.KeyPair;
29 import java.security.KeyPairGenerator;
30 import java.security.NoSuchAlgorithmException;
31 import java.security.NoSuchProviderException;
32 import java.security.PrivateKey;
33 import java.security.PublicKey;
34 import java.security.Security;
35 import java.security.spec.InvalidKeySpecException;
36 import java.util.Properties;
37 import java.util.logging.Level;
38 import java.util.logging.Logger;
39 import org.apache.commons.codec.binary.Base64;
40 import org.bouncycastle.jce.ECNamedCurveTable;
41 import org.bouncycastle.jce.interfaces.ECPrivateKey;
42 import org.bouncycastle.jce.interfaces.ECPublicKey;
43 import org.bouncycastle.jce.provider.BouncyCastleProvider;
44 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
45 import org.bouncycastle.jce.spec.ECParameterSpec;
46 import org.bouncycastle.jce.spec.ECPrivateKeySpec;
47 import org.bouncycastle.jce.spec.ECPublicKeySpec;
48 import org.bouncycastle.math.ec.ECCurve;
49 import org.bouncycastle.math.ec.ECPoint;
50 import org.bouncycastle.util.BigIntegers;
51 import org.turro.elephant.context.ElephantContext;
52 import org.turro.lock.Initializer;
53 
58 public class ServerKeys {
59 
60  public static String publicKey() {
61  return instance().getProperty("pk");
62  }
63 
64  public static String privateKey() {
65  return instance().getProperty("sk");
66  }
67 
68  /* Factory */
69 
70  private static final Initializer<Properties> PUSH_KEYS = new Initializer<>();
71 
72  private static Properties instance() {
73  return PUSH_KEYS.instance(() -> {
74  Security.addProvider(new BouncyCastleProvider());
75  File keyPair = pushKeyPairFile();
76  if(!keyPair.exists()) generateKeyPair();
77  if(keyPair.exists()) {
78  try(FileReader reader = new FileReader(keyPair)) {
79  Properties properties = new Properties();
80  properties.load(reader);
81  return properties;
82  } catch (IOException ex) {
83  Logger.getLogger(ServerKeys.class.getName()).log(Level.SEVERE, null, ex);
84  }
85  }
86  return null;
87  });
88  }
89 
90  private static final String PUSH_KEYPAIR = "/WEB-INF/elephant/security/push.cipher";
91 
92  private static void generateKeyPair() {
93  try {
94  ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(CURVE);
95  KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
96  keyPairGenerator.initialize(parameterSpec);
97  saveKeyPair(keyPairGenerator.generateKeyPair());
98  } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException ex) {
99  Logger.getLogger(ServerKeys.class.getName()).log(Level.SEVERE, null, ex);
100  }
101  }
102 
103  private static void saveKeyPair(KeyPair serverKey) {
104  try{
105  ECPublicKey publicKey = (ECPublicKey) serverKey.getPublic();
106  ECPrivateKey privateKey = (ECPrivateKey) serverKey.getPrivate();
107  byte[] encodedPublicKey = encode(publicKey);
108  byte[] encodedPrivateKey = encode(privateKey);
109  String publicKeyBase64 = Base64.encodeBase64URLSafeString(encodedPublicKey); //Base64Encoder.encodeUrl(encodedPublicKey);
110  String privateKeyBase64 = Base64.encodeBase64URLSafeString(encodedPrivateKey); //Base64Encoder.encodeUrl(encodedPrivateKey);
111  if(verifyKeyPair(loadPrivateKey(privateKeyBase64), loadPublicKey(publicKeyBase64))) {
112  Properties keypair = new Properties();
113  keypair.put("sk", privateKeyBase64);
114  keypair.put("pk", publicKeyBase64);
115  try(FileWriter writer = new FileWriter(pushKeyPairFile())) {
116  keypair.store(writer, "Elephant Push");
117  } catch (IOException ex) {
118  Logger.getLogger(ServerKeys.class.getName()).log(Level.SEVERE, null, ex);
119  }
120  }
121  } catch (NoSuchProviderException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
122  Logger.getLogger(ServerKeys.class.getName()).log(Level.SEVERE, null, ex);
123  }
124  }
125 
126  private static File pushKeyPairFile() {
127  return new File(ElephantContext.getRealPath(PUSH_KEYPAIR));
128  }
129 
130  public static final String CURVE = "prime256v1";
131  public static final String ALGORITHM = "ECDH";
132 
133  public static byte[] encode(ECPublicKey publicKey) {
134  return publicKey.getQ().getEncoded(false);
135  }
136 
137  public static byte[] encode(ECPrivateKey privateKey) {
138  return privateKey.getD().toByteArray();
139  }
140 
141  public static boolean verifyKeyPair(PrivateKey privateKey, PublicKey publicKey) {
142  ECNamedCurveParameterSpec curveParameters = ECNamedCurveTable.getParameterSpec(CURVE);
143  ECPoint g = curveParameters.getG();
144  ECPoint sG = g.multiply(((java.security.interfaces.ECPrivateKey) privateKey).getS());
145 
146  return sG.equals(((ECPublicKey) publicKey).getQ());
147  }
148 
149  public static PrivateKey loadPrivateKey(String encodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
150  byte[] decodedPrivateKey = Base64.decodeBase64(encodedPrivateKey);
151  return loadPrivateKey(decodedPrivateKey);
152  }
153 
154  public static PrivateKey loadPrivateKey(byte[] decodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
155  BigInteger s = BigIntegers.fromUnsignedByteArray(decodedPrivateKey);
156  ECParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(CURVE);
157  ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(s, parameterSpec);
158  KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
159 
160  return keyFactory.generatePrivate(privateKeySpec);
161  }
162 
163  public static PublicKey loadPublicKey(String encodedPublicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
164  byte[] decodedPublicKey = Base64.decodeBase64(encodedPublicKey);
165  return loadPublicKey(decodedPublicKey);
166  }
167 
168  public static PublicKey loadPublicKey(byte[] decodedPublicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
169  KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
170  ECParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(CURVE);
171  ECCurve curve = parameterSpec.getCurve();
172  ECPoint point = curve.decodePoint(decodedPublicKey);
173  ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, parameterSpec);
174 
175  return keyFactory.generatePublic(pubSpec);
176  }
177 
178 }
static PrivateKey loadPrivateKey(String encodedPrivateKey)
static PublicKey loadPublicKey(String encodedPublicKey)
static byte[] encode(ECPrivateKey privateKey)
static byte[] encode(ECPublicKey publicKey)
static PrivateKey loadPrivateKey(byte[] decodedPrivateKey)
static boolean verifyKeyPair(PrivateKey privateKey, PublicKey publicKey)
static PublicKey loadPublicKey(byte[] decodedPublicKey)