1 /* Copyright 2018 Google LLC 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * https://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 package com.google.security.cryptauth.lib.securegcm; 16 17 import com.google.protobuf.InvalidProtocolBufferException; 18 import com.google.security.cryptauth.lib.securemessage.PublicKeyProtoUtil; 19 import com.google.security.cryptauth.lib.securemessage.SecureMessageProto.GenericPublicKey; 20 import java.security.KeyFactory; 21 import java.security.NoSuchAlgorithmException; 22 import java.security.PrivateKey; 23 import java.security.PublicKey; 24 import java.security.interfaces.ECPrivateKey; 25 import java.security.interfaces.ECPublicKey; 26 import java.security.spec.InvalidKeySpecException; 27 import java.security.spec.PKCS8EncodedKeySpec; 28 import javax.crypto.SecretKey; 29 import javax.crypto.interfaces.DHPrivateKey; 30 import javax.crypto.spec.SecretKeySpec; 31 32 /** 33 * Utility class for encoding and parsing keys used by SecureGcm. 34 */ 35 public class KeyEncoding { KeyEncoding()36 private KeyEncoding() {} // Do not instantiate 37 38 private static boolean simulateLegacyCryptoRequired = false; 39 40 /** 41 * The JCA algorithm name to use when encoding/decoding symmetric keys. 42 */ 43 static final String SYMMETRIC_KEY_ENCODING_ALG = "AES"; 44 encodeMasterKey(SecretKey masterKey)45 public static byte[] encodeMasterKey(SecretKey masterKey) { 46 return masterKey.getEncoded(); 47 } 48 parseMasterKey(byte[] encodedMasterKey)49 public static SecretKey parseMasterKey(byte[] encodedMasterKey) { 50 return new SecretKeySpec(encodedMasterKey, SYMMETRIC_KEY_ENCODING_ALG); 51 } 52 encodeUserPublicKey(PublicKey pk)53 public static byte[] encodeUserPublicKey(PublicKey pk) { 54 return encodePublicKey(pk); 55 } 56 encodeUserPrivateKey(PrivateKey sk)57 public static byte[] encodeUserPrivateKey(PrivateKey sk) { 58 return sk.getEncoded(); 59 } 60 parseUserPrivateKey(byte[] encodedPrivateKey, boolean isLegacy)61 public static PrivateKey parseUserPrivateKey(byte[] encodedPrivateKey, boolean isLegacy) 62 throws InvalidKeySpecException { 63 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey); 64 if (isLegacy) { 65 return getRsaKeyFactory().generatePrivate(keySpec); 66 } 67 return getEcKeyFactory().generatePrivate(keySpec); 68 } 69 parseUserPublicKey(byte[] keyBytes)70 public static PublicKey parseUserPublicKey(byte[] keyBytes) throws InvalidKeySpecException { 71 return parsePublicKey(keyBytes); 72 } 73 encodeKeyAgreementPublicKey(PublicKey pk)74 public static byte[] encodeKeyAgreementPublicKey(PublicKey pk) { 75 return encodePublicKey(pk); 76 } 77 parseKeyAgreementPublicKey(byte[] keyBytes)78 public static PublicKey parseKeyAgreementPublicKey(byte[] keyBytes) 79 throws InvalidKeySpecException { 80 return parsePublicKey(keyBytes); 81 } 82 encodeKeyAgreementPrivateKey(PrivateKey sk)83 public static byte[] encodeKeyAgreementPrivateKey(PrivateKey sk) { 84 if (isLegacyPrivateKey(sk)) { 85 return PublicKeyProtoUtil.encodeDh2048PrivateKey((DHPrivateKey) sk); 86 } 87 return sk.getEncoded(); 88 } 89 parseKeyAgreementPrivateKey(byte[] keyBytes, boolean isLegacy)90 public static PrivateKey parseKeyAgreementPrivateKey(byte[] keyBytes, boolean isLegacy) 91 throws InvalidKeySpecException { 92 if (isLegacy) { 93 return PublicKeyProtoUtil.parseDh2048PrivateKey(keyBytes); 94 } 95 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 96 return getEcKeyFactory().generatePrivate(keySpec); 97 } 98 encodeSigningPublicKey(PublicKey pk)99 public static byte[] encodeSigningPublicKey(PublicKey pk) { 100 return encodePublicKey(pk); 101 } 102 parseSigningPublicKey(byte[] keyBytes)103 public static PublicKey parseSigningPublicKey(byte[] keyBytes) throws InvalidKeySpecException { 104 return parsePublicKey(keyBytes); 105 } 106 encodeSigningPrivateKey(PrivateKey sk)107 public static byte[] encodeSigningPrivateKey(PrivateKey sk) { 108 return sk.getEncoded(); 109 } 110 parseSigningPrivateKey(byte[] keyBytes)111 public static PrivateKey parseSigningPrivateKey(byte[] keyBytes) throws InvalidKeySpecException { 112 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 113 return getEcKeyFactory().generatePrivate(keySpec); 114 } 115 isLegacyPublicKey(PublicKey pk)116 public static boolean isLegacyPublicKey(PublicKey pk) { 117 if (pk instanceof ECPublicKey) { 118 return false; 119 } 120 return true; 121 } 122 isLegacyPrivateKey(PrivateKey sk)123 public static boolean isLegacyPrivateKey(PrivateKey sk) { 124 if (sk instanceof ECPrivateKey) { 125 return false; 126 } 127 return true; 128 } 129 isLegacyCryptoRequired()130 public static boolean isLegacyCryptoRequired() { 131 return PublicKeyProtoUtil.isLegacyCryptoRequired() || simulateLegacyCryptoRequired; 132 } 133 134 /** 135 * When testing, use this to force {@link #isLegacyCryptoRequired()} to return {@code true} 136 */ 137 // @VisibleForTesting setSimulateLegacyCrypto(boolean forceLegacy)138 public static void setSimulateLegacyCrypto(boolean forceLegacy) { 139 simulateLegacyCryptoRequired = forceLegacy; 140 } 141 encodePublicKey(PublicKey pk)142 private static byte[] encodePublicKey(PublicKey pk) { 143 return PublicKeyProtoUtil.encodePublicKey(pk).toByteArray(); 144 } 145 parsePublicKey(byte[] keyBytes)146 private static PublicKey parsePublicKey(byte[] keyBytes) throws InvalidKeySpecException { 147 try { 148 return PublicKeyProtoUtil.parsePublicKey(GenericPublicKey.parseFrom(keyBytes)); 149 } catch (InvalidProtocolBufferException e) { 150 throw new InvalidKeySpecException("Unable to parse GenericPublicKey", e); 151 } catch (IllegalArgumentException e) { 152 throw new InvalidKeySpecException("Unable to parse GenericPublicKey", e); 153 } 154 } 155 getEcKeyFactory()156 static KeyFactory getEcKeyFactory() { 157 try { 158 return KeyFactory.getInstance("EC"); 159 } catch (NoSuchAlgorithmException e) { 160 throw new RuntimeException(e); // No ECDH provider available 161 } 162 } 163 getRsaKeyFactory()164 static KeyFactory getRsaKeyFactory() { 165 try { 166 return KeyFactory.getInstance("RSA"); 167 } catch (NoSuchAlgorithmException e) { 168 throw new RuntimeException(e); // No RSA provider available 169 } 170 } 171 } 172