The defending five-time world champion and current series leader came home second on the track behind Vettel of Ferrari, but was declared the winner because of a five-second penalty for a racing infringement by the German.
Vettel ran off and re-joined across a strip of grass on lap 50, forcing a charging Hamilton off-track towards a wall. The Briton braked to withdraw from his challenge to take the lead, prompting a race stewards' inquiry, which resulted in the time penalty.
"Where could I go?" protested Vettel. “They're stealing the race from us."
"No, no, no, not like that," he repeated on his slow-down lap as Hamilton celebrated. "If there wasn't a wall, I would have gone past him," said the Briton.
It was Hamilton's record seventh victory in Canada and the 78th of his career.
Vettel ended up classified second ahead of his Ferrari team-mate Charles Leclerc, fourth-placed Valtteri Bottas in the second Mercedes, Max Verstappen of Red Bull and Daniel Ricciardo of Renault.
Nico Hulkenberg finished seventh in the second Renault ahead of Pierre Gasly of Red Bull, home hero Lance Stroll of Racing Point and Daniil Kvyat of Toro Rosso.
Mercedes congratulated Hamilton on his win. "You forced the error," they told him.
"I just want to thank the team as we had an engine problem after my crash - and it was not the way I wanted to get past."
In a bizarre sequence of incidents after the race, Vettel at first refused to attend the post-race interviews and stormed away to the Ferrari motor home before, after persuasion by the Italian team, he returned for the podium ceremonies.
As he did so, however, he walked to the parked cars at parc ferme and moved the numbers used to mark the finishing order - shifting the number one to the blank space for his car, two for Hamilton's Mercedes and three for Leclerc.