1package org.bouncycastle.jcajce.provider.asymmetric.dh;
2
3import java.io.IOException;
4import java.io.ObjectInputStream;
5import java.io.ObjectOutputStream;
6import java.math.BigInteger;
7
8import javax.crypto.interfaces.DHPublicKey;
9import javax.crypto.spec.DHParameterSpec;
10import javax.crypto.spec.DHPublicKeySpec;
11
12import org.bouncycastle.asn1.ASN1Integer;
13import org.bouncycastle.asn1.ASN1ObjectIdentifier;
14import org.bouncycastle.asn1.ASN1Sequence;
15import org.bouncycastle.asn1.pkcs.DHParameter;
16import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
17import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
18import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
19import org.bouncycastle.asn1.x9.DomainParameters;
20import org.bouncycastle.asn1.x9.ValidationParams;
21import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
22import org.bouncycastle.crypto.params.DHParameters;
23import org.bouncycastle.crypto.params.DHPublicKeyParameters;
24import org.bouncycastle.crypto.params.DHValidationParameters;
25import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
26
27public class BCDHPublicKey
28    implements DHPublicKey
29{
30    static final long serialVersionUID = -216691575254424324L;
31
32    private BigInteger              y;
33
34    private transient DHPublicKeyParameters   dhPublicKey;
35    private transient DHParameterSpec         dhSpec;
36    private transient SubjectPublicKeyInfo    info;
37
38    BCDHPublicKey(
39        DHPublicKeySpec spec)
40    {
41        this.y = spec.getY();
42        this.dhSpec = new DHParameterSpec(spec.getP(), spec.getG());
43        this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(spec.getP(), spec.getG()));
44    }
45
46    BCDHPublicKey(
47        DHPublicKey key)
48    {
49        this.y = key.getY();
50        this.dhSpec = key.getParams();
51        this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(dhSpec.getP(), dhSpec.getG()));
52    }
53
54    BCDHPublicKey(
55        DHPublicKeyParameters params)
56    {
57        this.y = params.getY();
58        this.dhSpec = new DHParameterSpec(params.getParameters().getP(), params.getParameters().getG(), params.getParameters().getL());
59        this.dhPublicKey = params;
60    }
61
62    BCDHPublicKey(
63        BigInteger y,
64        DHParameterSpec dhSpec)
65    {
66        this.y = y;
67        this.dhSpec = dhSpec;
68        this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(dhSpec.getP(), dhSpec.getG()));
69    }
70
71    public BCDHPublicKey(
72        SubjectPublicKeyInfo info)
73    {
74        this.info = info;
75
76        ASN1Integer              derY;
77        try
78        {
79            derY = (ASN1Integer)info.parsePublicKey();
80        }
81        catch (IOException e)
82        {
83            throw new IllegalArgumentException("invalid info structure in DH public key");
84        }
85
86        this.y = derY.getValue();
87
88        ASN1Sequence seq = ASN1Sequence.getInstance(info.getAlgorithm().getParameters());
89        ASN1ObjectIdentifier id = info.getAlgorithm().getAlgorithm();
90
91        // we need the PKCS check to handle older keys marked with the X9 oid.
92        if (id.equals(PKCSObjectIdentifiers.dhKeyAgreement) || isPKCSParam(seq))
93        {
94            DHParameter             params = DHParameter.getInstance(seq);
95
96            if (params.getL() != null)
97            {
98                this.dhSpec = new DHParameterSpec(params.getP(), params.getG(), params.getL().intValue());
99            }
100            else
101            {
102                this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
103            }
104            this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(dhSpec.getP(), dhSpec.getG()));
105        }
106        else if (id.equals(X9ObjectIdentifiers.dhpublicnumber))
107        {
108            DomainParameters params = DomainParameters.getInstance(seq);
109
110            this.dhSpec = new DHParameterSpec(params.getP(), params.getG());
111            ValidationParams validationParams = params.getValidationParams();
112            if (validationParams != null)
113            {
114                this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(params.getP(), params.getG(), params.getQ(), params.getJ(),
115                                            new DHValidationParameters(validationParams.getSeed(), validationParams.getPgenCounter().intValue())));
116            }
117            else
118            {
119                this.dhPublicKey = new DHPublicKeyParameters(y, new DHParameters(params.getP(), params.getG(), params.getQ(), params.getJ(), null));
120            }
121        }
122        else
123        {
124            throw new IllegalArgumentException("unknown algorithm type: " + id);
125        }
126    }
127
128    public String getAlgorithm()
129    {
130        return "DH";
131    }
132
133    public String getFormat()
134    {
135        return "X.509";
136    }
137
138    public byte[] getEncoded()
139    {
140        if (info != null)
141        {
142            return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
143        }
144
145        return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.dhKeyAgreement, new DHParameter(dhSpec.getP(), dhSpec.getG(), dhSpec.getL()).toASN1Primitive()), new ASN1Integer(y));
146    }
147
148    public DHParameterSpec getParams()
149    {
150        return dhSpec;
151    }
152
153    public BigInteger getY()
154    {
155        return y;
156    }
157
158    public DHPublicKeyParameters engineGetKeyParameters()
159    {
160        return dhPublicKey;
161    }
162
163    private boolean isPKCSParam(ASN1Sequence seq)
164    {
165        if (seq.size() == 2)
166        {
167            return true;
168        }
169
170        if (seq.size() > 3)
171        {
172            return false;
173        }
174
175        ASN1Integer l = ASN1Integer.getInstance(seq.getObjectAt(2));
176        ASN1Integer p = ASN1Integer.getInstance(seq.getObjectAt(0));
177
178        if (l.getValue().compareTo(BigInteger.valueOf(p.getValue().bitLength())) > 0)
179        {
180            return false;
181        }
182
183        return true;
184    }
185
186    public int hashCode()
187    {
188        return this.getY().hashCode() ^ this.getParams().getG().hashCode()
189                ^ this.getParams().getP().hashCode() ^ this.getParams().getL();
190    }
191
192    public boolean equals(
193        Object o)
194    {
195        if (!(o instanceof DHPublicKey))
196        {
197            return false;
198        }
199
200        DHPublicKey other = (DHPublicKey)o;
201
202        return this.getY().equals(other.getY())
203            && this.getParams().getG().equals(other.getParams().getG())
204            && this.getParams().getP().equals(other.getParams().getP())
205            && this.getParams().getL() == other.getParams().getL();
206    }
207
208    private void readObject(
209        ObjectInputStream   in)
210        throws IOException, ClassNotFoundException
211    {
212        in.defaultReadObject();
213
214        this.dhSpec = new DHParameterSpec((BigInteger)in.readObject(), (BigInteger)in.readObject(), in.readInt());
215        this.info = null;
216    }
217
218    private void writeObject(
219        ObjectOutputStream  out)
220        throws IOException
221    {
222        out.defaultWriteObject();
223
224        out.writeObject(dhSpec.getP());
225        out.writeObject(dhSpec.getG());
226        out.writeInt(dhSpec.getL());
227    }
228}
229