2 * Copyright (c) 2016 Brocade Communication Systems and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.callhome.protocol;
10 import java.io.ByteArrayOutputStream;
11 import java.io.DataOutputStream;
12 import java.io.IOException;
13 import java.math.BigInteger;
14 import java.security.KeyFactory;
15 import java.security.NoSuchAlgorithmException;
16 import java.security.NoSuchProviderException;
17 import java.security.PublicKey;
18 import java.security.interfaces.DSAParams;
19 import java.security.interfaces.DSAPublicKey;
20 import java.security.interfaces.RSAPublicKey;
21 import java.security.spec.DSAPublicKeySpec;
22 import java.security.spec.ECPoint;
23 import java.security.spec.ECPublicKeySpec;
24 import java.security.spec.InvalidKeySpecException;
25 import java.security.spec.RSAPublicKeySpec;
26 import java.util.Arrays;
27 import java.util.HashMap;
29 import org.apache.sshd.common.util.Base64;
30 import org.apache.sshd.common.util.SecurityUtils;
31 import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
32 import org.bouncycastle.jce.ECNamedCurveTable;
33 import org.bouncycastle.jce.ECPointUtil;
34 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
35 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
38 * FIXME: This should be probably located at AAA library.
40 public class AuthorizedKeysDecoder {
42 private static final String KEY_FACTORY_TYPE_RSA = "RSA";
43 private static final String KEY_FACTORY_TYPE_DSA = "DSA";
44 private static final String KEY_FACTORY_TYPE_ECDSA = "EC";
46 private static final Map<String, String> ECDSA_CURVES = new HashMap<>();
49 ECDSA_CURVES.put("nistp256", "secp256r1");
50 ECDSA_CURVES.put("nistp384", "secp384r1");
51 ECDSA_CURVES.put("nistp512", "secp512r1");
54 private static final String ECDSA_SUPPORTED_CURVE_NAME = "nistp256";
55 private static final String ECDSA_SUPPORTED_CURVE_NAME_SPEC = ECDSA_CURVES.get(ECDSA_SUPPORTED_CURVE_NAME);
57 private static final String KEY_TYPE_RSA = "ssh-rsa";
58 private static final String KEY_TYPE_DSA = "ssh-dss";
59 private static final String KEY_TYPE_ECDSA = "ecdsa-sha2-" + ECDSA_SUPPORTED_CURVE_NAME;
61 private byte[] bytes = new byte[0];
64 public PublicKey decodePublicKey(String keyLine)
65 throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException {
67 // look for the Base64 encoded part of the line to decode
68 // both ssh-rsa and ssh-dss begin with "AAAA" due to the length bytes
69 bytes = Base64.decodeBase64(keyLine.getBytes());
70 if (bytes.length == 0) {
71 throw new IllegalArgumentException("No Base64 part to decode in " + keyLine);
76 String type = decodeType();
77 if (type.equals(KEY_TYPE_RSA)) {
81 if (type.equals(KEY_TYPE_DSA)) {
85 if (type.equals(KEY_TYPE_ECDSA)) {
86 return decodeAsEcDSA();
89 throw new IllegalArgumentException("Unknown decode key type " + type + " in " + keyLine);
92 private PublicKey decodeAsEcDSA()
93 throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
94 KeyFactory ecdsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_ECDSA);
96 ECNamedCurveParameterSpec spec256r1 = ECNamedCurveTable.getParameterSpec(ECDSA_SUPPORTED_CURVE_NAME_SPEC);
97 ECNamedCurveSpec params256r1 = new ECNamedCurveSpec(
98 ECDSA_SUPPORTED_CURVE_NAME_SPEC, spec256r1.getCurve(), spec256r1.getG(), spec256r1.getN());
99 // copy last 65 bytes from ssh key.
100 ECPoint point = ECPointUtil.decodePoint(params256r1.getCurve(), Arrays.copyOfRange(bytes, 39, bytes.length));
101 ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params256r1);
103 return ecdsaFactory.generatePublic(pubKeySpec);
106 private PublicKey decodeAsDSA() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
107 KeyFactory dsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_DSA);
108 BigInteger prime = decodeBigInt();
109 BigInteger subPrime = decodeBigInt();
110 BigInteger base = decodeBigInt();
111 BigInteger publicKey = decodeBigInt();
112 DSAPublicKeySpec spec = new DSAPublicKeySpec(publicKey, prime, subPrime, base);
114 return dsaFactory.generatePublic(spec);
117 private PublicKey decodeAsRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
118 KeyFactory rsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_RSA);
119 BigInteger exponent = decodeBigInt();
120 BigInteger modulus = decodeBigInt();
121 RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
123 return rsaFactory.generatePublic(spec);
126 private String decodeType() {
127 int len = decodeInt();
128 String type = new String(bytes, pos, len);
133 private int decodeInt() {
134 return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
135 | ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
138 private BigInteger decodeBigInt() {
139 int len = decodeInt();
140 byte[] bigIntBytes = new byte[len];
141 System.arraycopy(bytes, pos, bigIntBytes, 0, len);
143 return new BigInteger(bigIntBytes);
146 public static String encodePublicKey(PublicKey publicKey) throws IOException {
147 String publicKeyEncoded;
148 ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
149 if (publicKey.getAlgorithm().equals(KEY_FACTORY_TYPE_RSA)) {
150 RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
151 DataOutputStream dout = new DataOutputStream(byteOs);
152 dout.writeInt(KEY_TYPE_RSA.getBytes().length);
153 dout.write(KEY_TYPE_RSA.getBytes());
154 dout.writeInt(rsaPublicKey.getPublicExponent().toByteArray().length);
155 dout.write(rsaPublicKey.getPublicExponent().toByteArray());
156 dout.writeInt(rsaPublicKey.getModulus().toByteArray().length);
157 dout.write(rsaPublicKey.getModulus().toByteArray());
158 } else if (publicKey.getAlgorithm().equals(KEY_FACTORY_TYPE_DSA)) {
159 DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
160 DSAParams dsaParams = dsaPublicKey.getParams();
161 DataOutputStream dout = new DataOutputStream(byteOs);
162 dout.writeInt(KEY_TYPE_DSA.getBytes().length);
163 dout.write(KEY_TYPE_DSA.getBytes());
164 dout.writeInt(dsaParams.getP().toByteArray().length);
165 dout.write(dsaParams.getP().toByteArray());
166 dout.writeInt(dsaParams.getQ().toByteArray().length);
167 dout.write(dsaParams.getQ().toByteArray());
168 dout.writeInt(dsaParams.getG().toByteArray().length);
169 dout.write(dsaParams.getG().toByteArray());
170 dout.writeInt(dsaPublicKey.getY().toByteArray().length);
171 dout.write(dsaPublicKey.getY().toByteArray());
172 } else if (publicKey.getAlgorithm().equals(KEY_FACTORY_TYPE_ECDSA)) {
173 BCECPublicKey ecPublicKey = (BCECPublicKey) publicKey;
174 DataOutputStream dout = new DataOutputStream(byteOs);
175 dout.writeInt(KEY_TYPE_ECDSA.getBytes().length);
176 dout.write(KEY_TYPE_ECDSA.getBytes());
177 dout.writeInt(ECDSA_SUPPORTED_CURVE_NAME.getBytes().length);
178 dout.write(ECDSA_SUPPORTED_CURVE_NAME.getBytes());
180 byte[] coordX = ecPublicKey.getQ().getAffineXCoord().getEncoded();
181 byte[] coordY = ecPublicKey.getQ().getAffineYCoord().getEncoded();
182 dout.writeInt(coordX.length + coordY.length + 1);
183 dout.writeByte(0x04);
187 throw new IllegalArgumentException("Unknown public key encoding: " + publicKey.getAlgorithm());
189 publicKeyEncoded = new String(Base64.encodeBase64(byteOs.toByteArray()));
190 return publicKeyEncoded;