/* * Copyright 2012-2013 Mathias Herberts * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.geoxp.oss; import java.io.IOException; import java.security.SecureRandom; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.util.encoders.Hex; public class MasterSecretGenerator { /** * AES 256 key used to wrap the master secret. It is not intended to protect * the secrecy of the master secret but simply to allow for integrity check * via the use of AES Key Wrapping. */ private static final byte[] MASTER_SECRET_WRAPPING_KEY = Hex.decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); public static Map<PGPPublicKey, byte[]> generate(byte[] secret, List<PGPPublicKey> keys, int k) throws OSSException { // // Generate 256 bit secret // if (null == secret) { SecureRandom sr = CryptoHelper.getSecureRandom(); secret = new byte[32]; sr.nextBytes(secret); } else { // // Attempt to unwrap secret // secret = CryptoHelper.unwrapAES(MASTER_SECRET_WRAPPING_KEY, secret); if (null == secret) { throw new OSSException("Invalid secret to split. The input was not generated by OSSGenMasterSecret/OSSSplit with k=1."); } if (secret.length != 32) { throw new OSSException("Invalid secret length, MUST be 32 bytes (256 bits)."); } } // // Wrap secret with a static AES Wrapping Key so we can check // we've correctly recovered the secret on initialization // byte[] wrappedsecret = CryptoHelper.wrapAES(MASTER_SECRET_WRAPPING_KEY, secret); return generate(keys, k, wrappedsecret); } public static Map<PGPPublicKey, byte[]> generate(List<PGPPublicKey> keys, int k, byte[] wrappedsecret) throws OSSException { if (null == keys) { throw new OSSException("Missing public PGP Public Keys."); } // // Make sure each PGP public key can encrypt data // Set<String> nonEncryptionFingerprints = new HashSet<String>(); for (PGPPublicKey key: keys) { if (!key.isEncryptionKey()) { nonEncryptionFingerprints.add(new String(Hex.encode(key.getFingerprint()))); } } if (!nonEncryptionFingerprints.isEmpty()) { StringBuilder sb = new StringBuilder(); sb.append("PGP Public Keys need to be encryption keys, the following keys were not:"); for (String fpr: nonEncryptionFingerprints) { sb.append(" "); sb.append(fpr); } throw new OSSException(sb.toString()); } // // Check value of k // if (k < 1 || k > keys.size()) { throw new OSSException("Invalid number of needed shares, was " + k + ", should have been in [1," + keys.size() + "]"); } // // Split the secret using Shamir Secret Sharing Scheme if k is > 1 // Map<PGPPublicKey, byte[]> perkeysecret = new HashMap<PGPPublicKey, byte[]>(); if (k == 1) { // // Simply encrypt the secret with each public key // for (PGPPublicKey key: keys) { try { perkeysecret.put(key, CryptoHelper.encryptPGP(wrappedsecret, key, true, "", PGPCompressedData.ZIP, PGPEncryptedData.AES_256)); } catch (IOException ioe) { throw new OSSException(ioe); } } } else { List<byte[]> secrets = CryptoHelper.SSSSSplit(wrappedsecret, keys.size(), k); for (int i = 0; i < keys.size(); i++) { PGPPublicKey key = keys.get(i); byte[] share = secrets.get(i); try { perkeysecret.put(key, CryptoHelper.encryptPGP(share, key, true, "", PGPCompressedData.ZIP, PGPEncryptedData.AES_256)); } catch (IOException ioe) { throw new OSSException(ioe); } } } return perkeysecret; } public static byte[] getMasterSecretWrappingKey() { return MASTER_SECRET_WRAPPING_KEY; } }