1 // Copyright 2020 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 encodeDeviceSyncGroupPublicKey(PublicKey pk)61 public static byte[] encodeDeviceSyncGroupPublicKey(PublicKey pk) { 62 return PublicKeyProtoUtil.encodePaddedEcPublicKey(pk).toByteArray(); 63 } 64 parseUserPrivateKey(byte[] encodedPrivateKey, boolean isLegacy)65 public static PrivateKey parseUserPrivateKey(byte[] encodedPrivateKey, boolean isLegacy) 66 throws InvalidKeySpecException { 67 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey); 68 if (isLegacy) { 69 return getRsaKeyFactory().generatePrivate(keySpec); 70 } 71 return getEcKeyFactory().generatePrivate(keySpec); 72 } 73 parseUserPublicKey(byte[] keyBytes)74 public static PublicKey parseUserPublicKey(byte[] keyBytes) throws InvalidKeySpecException { 75 return parsePublicKey(keyBytes); 76 } 77 parseDeviceSyncGroupPublicKey(byte[] keyBytes)78 public static PublicKey parseDeviceSyncGroupPublicKey(byte[] keyBytes) 79 throws InvalidKeySpecException { 80 return parsePublicKey(keyBytes); 81 } 82 encodeKeyAgreementPublicKey(PublicKey pk)83 public static byte[] encodeKeyAgreementPublicKey(PublicKey pk) { 84 return encodePublicKey(pk); 85 } 86 parseKeyAgreementPublicKey(byte[] keyBytes)87 public static PublicKey parseKeyAgreementPublicKey(byte[] keyBytes) 88 throws InvalidKeySpecException { 89 return parsePublicKey(keyBytes); 90 } 91 encodeKeyAgreementPrivateKey(PrivateKey sk)92 public static byte[] encodeKeyAgreementPrivateKey(PrivateKey sk) { 93 if (isLegacyPrivateKey(sk)) { 94 return PublicKeyProtoUtil.encodeDh2048PrivateKey((DHPrivateKey) sk); 95 } 96 return sk.getEncoded(); 97 } 98 parseKeyAgreementPrivateKey(byte[] keyBytes, boolean isLegacy)99 public static PrivateKey parseKeyAgreementPrivateKey(byte[] keyBytes, boolean isLegacy) 100 throws InvalidKeySpecException { 101 if (isLegacy) { 102 return PublicKeyProtoUtil.parseDh2048PrivateKey(keyBytes); 103 } 104 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 105 return getEcKeyFactory().generatePrivate(keySpec); 106 } 107 encodeSigningPublicKey(PublicKey pk)108 public static byte[] encodeSigningPublicKey(PublicKey pk) { 109 return encodePublicKey(pk); 110 } 111 parseSigningPublicKey(byte[] keyBytes)112 public static PublicKey parseSigningPublicKey(byte[] keyBytes) throws InvalidKeySpecException { 113 return parsePublicKey(keyBytes); 114 } 115 encodeSigningPrivateKey(PrivateKey sk)116 public static byte[] encodeSigningPrivateKey(PrivateKey sk) { 117 return sk.getEncoded(); 118 } 119 parseSigningPrivateKey(byte[] keyBytes)120 public static PrivateKey parseSigningPrivateKey(byte[] keyBytes) throws InvalidKeySpecException { 121 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 122 return getEcKeyFactory().generatePrivate(keySpec); 123 } 124 isLegacyPublicKey(PublicKey pk)125 public static boolean isLegacyPublicKey(PublicKey pk) { 126 if (pk instanceof ECPublicKey) { 127 return false; 128 } 129 return true; 130 } 131 isLegacyPrivateKey(PrivateKey sk)132 public static boolean isLegacyPrivateKey(PrivateKey sk) { 133 if (sk instanceof ECPrivateKey) { 134 return false; 135 } 136 return true; 137 } 138 isLegacyCryptoRequired()139 public static boolean isLegacyCryptoRequired() { 140 return PublicKeyProtoUtil.isLegacyCryptoRequired() || simulateLegacyCryptoRequired; 141 } 142 143 /** 144 * When testing, use this to force {@link #isLegacyCryptoRequired()} to return {@code true} 145 */ 146 // @VisibleForTesting setSimulateLegacyCrypto(boolean forceLegacy)147 public static void setSimulateLegacyCrypto(boolean forceLegacy) { 148 simulateLegacyCryptoRequired = forceLegacy; 149 } 150 encodePublicKey(PublicKey pk)151 private static byte[] encodePublicKey(PublicKey pk) { 152 return PublicKeyProtoUtil.encodePublicKey(pk).toByteArray(); 153 } 154 parsePublicKey(byte[] keyBytes)155 private static PublicKey parsePublicKey(byte[] keyBytes) throws InvalidKeySpecException { 156 try { 157 return PublicKeyProtoUtil.parsePublicKey(GenericPublicKey.parseFrom(keyBytes)); 158 } catch (InvalidProtocolBufferException e) { 159 throw new InvalidKeySpecException("Unable to parse GenericPublicKey", e); 160 } catch (IllegalArgumentException e) { 161 throw new InvalidKeySpecException("Unable to parse GenericPublicKey", e); 162 } 163 } 164 getEcKeyFactory()165 static KeyFactory getEcKeyFactory() { 166 try { 167 return KeyFactory.getInstance("EC"); 168 } catch (NoSuchAlgorithmException e) { 169 throw new RuntimeException(e); // No ECDH provider available 170 } 171 } 172 getRsaKeyFactory()173 static KeyFactory getRsaKeyFactory() { 174 try { 175 return KeyFactory.getInstance("RSA"); 176 } catch (NoSuchAlgorithmException e) { 177 throw new RuntimeException(e); // No RSA provider available 178 } 179 } 180 } 181